commit da923801b32ef35f933cef7f00b647042d06dca9 Author: lzwdgc Date: Thu Mar 26 14:03:00 2015 +0300 Add db_extractor, unpaker. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..acd2451 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +win* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bc5e2d0 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 2.8) + +set(output_dir ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${output_dir}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${output_dir}) + +project(aim_tools) + +if (MSVC) + set(disable_msvc_warnings "/W1 /wd4996") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP ${disable_msvc_warnings}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP ${disable_msvc_warnings}") + + #set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") + #set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") +endif(MSVC) + +add_subdirectory(src) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..807d1b5 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +This repository contains different tools for A.I.M. games. \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..490eac9 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB unpaker_src "unpaker/*") +add_executable(unpaker ${unpaker_src}) + +file(GLOB db_extractor_src "db_extractor/*") +add_executable(db_extractor ${db_extractor_src}) \ No newline at end of file diff --git a/src/db_extractor/db.cpp b/src/db_extractor/db.cpp new file mode 100644 index 0000000..820520f --- /dev/null +++ b/src/db_extractor/db.cpp @@ -0,0 +1,149 @@ +/* + * AIM db_extractor + * Copyright (C) 2015 lzwdgc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db.h" + +#define FREAD(var) fread(&var, 1, sizeof(var), f) +#define SREAD(var) s.read(&var, sizeof(var)) +#define SREAD_N(var, sz) s.read(&var, sz) + +string getSqlType(uint32_t ft) +{ + switch (ft) + { + case T_STRING: + return "TEXT"; + case T_INTEGER: + return "INTEGER"; + case T_FLOAT: + return "REAL"; + default: + assert(false); + } + return ""; +} + +void table::load(FILE *f) +{ + FREAD(id); + FREAD(name); + FREAD(unk1); + FREAD(unk2); + FREAD(unk3); + FREAD(unk4); +} + +void field::load(FILE *f) +{ + FREAD(table_id); + FREAD(id); + FREAD(name); + FREAD(unk1); + FREAD(unk2); + FREAD(unk3); + FREAD(type); +} + +void tab::load(FILE *f) +{ + FREAD(number_of_tables); + FREAD(number_of_fields); + + auto n = number_of_tables; + while (n--) + { + table t; + t.load(f); + tables[t.id] = t; + } + + n = number_of_fields; + while (n--) + { + field t; + t.load(f); + fields[t.id] = t; + } +} + +void value::load_index(FILE *f) +{ + FREAD(table_id); + FREAD(name); + FREAD(unk1); + FREAD(unk2); + FREAD(unk3); + FREAD(offset); + FREAD(data_size); + buf.resize(data_size); +} + +void value::load_data(FILE *f) +{ + fseek(f, offset, SEEK_SET); + fread(buf.data(), buf.size(), 1, f); +} + +void value::extract_fields(const tab &tab) +{ + s_file s(buf); + + while (1) + { + field_value fv; + if (SREAD(fv.field_id) == 0) + break; + SREAD(fv.size); + auto i = tab.fields.find(fv.field_id); + if (i == tab.fields.end()) + continue; + switch (i->second.type) + { + case T_STRING: + fv.s.resize(fv.size); + SREAD_N(fv.s[0], fv.s.size()); + break; + case T_INTEGER: + SREAD(fv.i); + if (fv.size > sizeof(fv.i)) + s.skip(fv.size - sizeof(fv.i)); + break; + case T_FLOAT: + SREAD(fv.f); + if (fv.size > sizeof(fv.i)) + s.skip(fv.size - sizeof(fv.i)); + break; + default: + assert(false); + } + fields.push_back(fv); + } +} + +void db::load(FILE *f) +{ + FREAD(number_of_values); + + auto n = number_of_values; + while (n--) + { + value t; + t.load_index(f); + values.push_back(t); + } +} \ No newline at end of file diff --git a/src/db_extractor/db.h b/src/db_extractor/db.h new file mode 100644 index 0000000..7e4e70d --- /dev/null +++ b/src/db_extractor/db.h @@ -0,0 +1,137 @@ +/* + * AIM db_extractor + * Copyright (C) 2015 lzwdgc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +enum FieldType +{ + T_STRING, + T_INTEGER, + T_FLOAT, +}; +string getSqlType(uint32_t ft); + +struct table +{ + uint32_t id; + char name[0x14]; + uint32_t unk1; + uint32_t unk2; + uint32_t unk3; + uint32_t unk4; + + void load(FILE *f); +}; + +struct field +{ + uint32_t table_id; + uint32_t id; + char name[0x14]; + uint32_t unk1; + uint32_t unk2; + uint32_t unk3; + uint32_t type; + + void load(FILE *f); +}; + +struct tab +{ + uint32_t number_of_tables; + uint32_t number_of_fields; + + map tables; + map fields; + + void load(FILE *f); +}; + +struct field_value +{ + uint32_t field_id; + uint32_t size; + + int i = 0; + float f = 0.f; + string s; +}; + +struct value +{ + struct s_file + { + uint32_t index = 0; + const vector buf; + + s_file(const vector &buf) + : buf(buf) + {} + uint32_t read(void *dst, uint32_t size) + { + if (index >= buf.size()) + return 0; + if (index + size > buf.size()) + size = buf.size() - index; + memcpy(dst, buf.data() + index, size); + index += size; + return size; + } + void skip(int n) + { + index += n; + } + }; + + uint32_t table_id; + char name[0x14]; + uint32_t unk1; + uint32_t unk2; + uint32_t unk3; + uint32_t offset; + uint32_t data_size; + + // + vector buf; + // + uint32_t number_of_fields; + vector fields; + + void extract_fields(const tab &tab); + + void load_index(FILE *f); + void load_data(FILE *f); +}; + +struct db +{ + uint32_t number_of_values; + + tab t; + vector values; + + void load(FILE *f); +}; \ No newline at end of file diff --git a/src/db_extractor/db_extractor.cpp b/src/db_extractor/db_extractor.cpp new file mode 100644 index 0000000..1259bac --- /dev/null +++ b/src/db_extractor/db_extractor.cpp @@ -0,0 +1,170 @@ +/* + * AIM db_extractor + * Copyright (C) 2015 lzwdgc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db.h" + +#include + +#include + +void open_tab(string path, tab &tab) +{ + path += ".tab"; + FILE *f = fopen(path.c_str(), "rb"); + if (!f) + return; + tab.load(f); + fclose(f); +} + +void open_index(string path, db &index) +{ + path += ".ind"; + FILE *f = fopen(path.c_str(), "rb"); + if (!f) + return; + index.load(f); + fclose(f); +} + +void open_data(string path, db &index) +{ + path += ".dat"; + FILE *f = fopen(path.c_str(), "rb"); + if (!f) + return; + for (auto &v : index.values) + v.load_data(f); + fclose(f); +} + +void open_db(string path, db &db) +{ + open_tab(path, db.t); + open_index(path, db); + open_data(path, db); + for (auto &v : db.values) + v.extract_fields(db.t); +} + +string str2utf8(string codepage_str) +{ + int size = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, codepage_str.c_str(), + codepage_str.length(), nullptr, 0); + std::wstring utf16_str(size, '\0'); + MultiByteToWideChar(CP_ACP, MB_COMPOSITE, codepage_str.c_str(), + codepage_str.length(), &utf16_str[0], size); + + int utf8_size = WideCharToMultiByte(CP_UTF8, 0, utf16_str.c_str(), + utf16_str.length(), nullptr, 0, + nullptr, nullptr); + std::string utf8_str(utf8_size, '\0'); + WideCharToMultiByte(CP_UTF8, 0, utf16_str.c_str(), + utf16_str.length(), &utf8_str[0], utf8_size, + nullptr, nullptr); + return utf8_str; +} + +void create_sql(string path, const db &db) +{ + ofstream ofile(path + ".sql"); + if (!ofile) + return; + + const string id = "ID"; + const string row_type = "TEXT_ID"; + for (auto &table : db.t.tables) + { + auto &t = table.second; + string name = str2utf8(t.name); + ofile << "drop table if exists " << name << ";\n"; + ofile << "create table \"" << name << "\"\n"; + ofile << "(\n"; + string s; + s += " \"" + str2utf8(id) + "\" INTEGER,\n"; + s += " \"" + str2utf8(row_type) + "\" TEXT,\n"; + for (auto &f : db.t.fields) + { + if (f.second.table_id != t.id) + continue; + s += " \"" + str2utf8(f.second.name) + "\" " + getSqlType(f.second.type) + ",\n"; + } + s.resize(s.size() - 2); + ofile << s << "\n"; + ofile << ");\n"; + ofile << "\n"; + } + + map idx; + for (auto &v : db.values) + { + auto tbl = db.t.tables.find(v.table_id); + if (tbl == db.t.tables.end()) + continue; + ofile << "insert into \"" << str2utf8(tbl->second.name) << "\" ("; + string s; + s += "'" + str2utf8(id) + "', "; + s += "'" + str2utf8(row_type) + "', "; + for (auto &f : v.fields) + { + auto fld = db.t.fields.find(f.field_id); + s += "'" + str2utf8(fld->second.name) + "', "; + } + s.resize(s.size() - 2); + ofile << s << ") values ("; + s.clear(); + s += "'" + to_string(idx[v.table_id]++) + "', "; + s += "'" + str2utf8(v.name) + "', "; + for (auto &f : v.fields) + { + s += "'"; + auto fld = db.t.fields.find(f.field_id); + switch (fld->second.type) + { + case T_STRING: + s += str2utf8(f.s.c_str()); + break; + case T_INTEGER: + s += to_string(f.i); + break; + case T_FLOAT: + s += to_string(f.f); + break; + default: + assert(false); + } + s += "', "; + } + s.resize(s.size() - 2); + ofile << s << ");\n"; + } +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) + { + cout << "Usage:\n" << argv[0] << " path/to/aim_game/data/db" << "\n" << argv[0] << " path/to/aim_game/data/quest" << "\n"; + return 1; + } + string path = argv[1]; + db db; + open_db(path, db); + create_sql(path, db); + return 0; +} \ No newline at end of file diff --git a/src/unpaker/decode.h b/src/unpaker/decode.h new file mode 100644 index 0000000..c4e3d6f --- /dev/null +++ b/src/unpaker/decode.h @@ -0,0 +1,313 @@ +#pragma once + +#define _BYTE uint8_t +#define _WORD uint16_t +#define _DWORD uint32_t +#define _QWORD uint64_t + +#define LOBYTE(x) (*((_BYTE*)&(x))) +#define LOWORD(x) (*((_WORD*)&(x))) +#define HIBYTE(x) (*((_BYTE*)&(x)+1)) + +#define BYTEn(x, n) (*((_BYTE*)&(x)+n)) +#define BYTE1(x) BYTEn(x, 1) + +template int8_t __SETS__(T x) +{ + if ( sizeof(T) == 1 ) + return int8_t(x) < 0; + if ( sizeof(T) == 2 ) + return int16_t(x) < 0; + if ( sizeof(T) == 4 ) + return int32_t(x) < 0; + return int64_t(x) < 0; +} + +template int8_t __OFSUB__(T x, U y) +{ + if ( sizeof(T) < sizeof(U) ) + { + U x2 = x; + int8_t sx = __SETS__(x2); + return (sx ^ __SETS__(y)) & (sx ^ __SETS__(x2-y)); + } + else + { + T y2 = y; + int8_t sx = __SETS__(x); + return (sx ^ __SETS__(y2)) & (sx ^ __SETS__(x-y2)); + } +} + +inline void memset32(void *ptr, uint32_t value, int count) +{ + uint32_t *p = (uint32_t *)ptr; + for (int i = 0; i < count; i++) + *p++ = value; +} + +char *decode_f1(char *input, int size, char *output) +{ + char *result; // eax@1 + char *v4; // ebx@1 + int v5; // ebp@1 + char *v6; // edi@2 + char *v7; // edx@4 + int v8; // ecx@6 + int v9; // esi@6 + int v10; // esi@6 + int v11; // ecx@6 + char *v12; // edx@7 + int v13; // esi@7 + + result = input; + v4 = &input[size]; + v5 = 8; + if ( input < &input[size] ) + { + v6 = output; + do + { + if ( v5 == 8 ) + { + v7 = (char *)(uint8_t)*result++; + v5 = 0; + input = v7; + if ( result == v4 ) + break; + } + if ( (uint8_t)input & 1 ) + { + v8 = (uint8_t)*result; + v9 = (uint8_t)result[1]; + result += 2; + v10 = ((v8 & 0xF) << 8) + v9; + v11 = (v8 >> 4) + 4; + if ( v11 ) + { + v12 = &v6[-v10]; + v13 = v11; + do + { + *v6++ = *v12++; + --v13; + } + while ( v13 ); + } + } + else + { + *v6++ = *result++; + } + ++v5; + input = (char *)((signed int)input >> 1); + } + while ( result < v4 ); + } + return result; +} + +char decode_f2(char *input, int size, char *output) +{ + int c; // eax@1 + char v4; // bl@1 + char *v5; // edx@1 + char *v6; // esi@2 + char *v7; // edx@4 + int c_1; // edi@4 + int c_2; // edi@7 + char *v10; // ecx@8 + int c_3; // edi@8 + + LOBYTE(c) = size; + v4 = *input; + v5 = input + 1; + if ( input + 1 < &input[size] ) + { + v6 = output; + do + { + LOBYTE(c) = *v5; + if ( *v5 == v4 ) + { + c = (uint8_t)v5[1]; + v7 = v5 + 1; + c_1 = (uint8_t)v7[1]; + v5 = v7 + 2; + if ( c != 255 || c_1 != 255 ) + { + c_2 = ((c & 0xF) << 8) + c_1; + c = (c >> 4) + 4; + if ( c ) + { + v10 = &v6[-c_2]; + c_3 = c; + do + { + LOBYTE(c) = *v10; + *v6++ = *v10++; + --c_3; + } + while ( c_3 ); + } + } + else + { + *v6++ = v4; + } + } + else + { + *v6++ = c; + ++v5; + } + } + while ( v5 < &input[size] ); + } + return c; +} + +int decode_f3(char *input, int size, char *output) +{ + uint16_t s; // cx@1 + char *v4; // edi@1 + int result; // eax@1 + int idx; // edx@1 + bool v7; // zf@1 + bool v8; // sf@1 + uint8_t v9; // of@1 + int v10; // ebp@1 + char *v11; // ebx@2 + uint16_t v12; // ax@3 + uint8_t v13; // cl@4 + __int16 v14; // ax@6 + unsigned int v15; // esi@6 + void *v16; // edi@7 + int v17; // ebp@7 + int v18; // eax@7 + int v19; // edi@7 + int i; // ecx@7 + int v21; // [sp+8h] [bp-4h]@1 + int v22; // [sp+14h] [bp+8h]@1 + + LOBYTE(s) = 0; + v4 = input; + HIBYTE(s) = *input; + result = size >> 1; + idx = 1; + v9 = __OFSUB__(size >> 1, 1); + v7 = size >> 1 == 1; + v8 = (size >> 1) - 1 < 0; + v22 = size >> 1; + v10 = s; + v21 = s; + if ( !((uint8_t)(v8 ^ v9) | v7) ) + { + v11 = output; + do + { + v12 = *(_WORD *)&v4[2 * idx]; + if ( (*(_WORD *)&v4[2 * idx] & 0xFF00) == (_WORD)v10 ) + { + v13 = *(_WORD *)&v4[2 * idx++]; + if ( v12 != (uint16_t)v10 + 255 ) + { + v14 = *(_WORD *)&v4[2 * idx]; + v15 = v13 + 3; + if ( (signed int)v15 > 0 ) + { + LOWORD(v10) = *(_WORD *)&v4[2 * idx]; + v16 = v11; + v11 += 2 * v15; + v17 = v10 << 16; + LOWORD(v17) = v14; + v18 = v17; + v10 = v21; + memset32(v16, v18, v15 >> 1); + v19 = (int)((char *)v16 + 4 * (v15 >> 1)); + for ( i = (v13 + 3) & 1; i; --i ) + { + *(_WORD *)v19 = v18; + v19 += 2; + } + v4 = input; + } + goto LABEL_13; + } + *(_WORD *)v11 = *(_WORD *)&v4[2 * idx]; + } + else + { + *(_WORD *)v11 = v12; + } + v11 += 2; +LABEL_13: + result = v22; + ++idx; + } + while ( idx < v22 ); + } + return result; +} + +int decode_f4(char *input, int size, char *output, int segment_offset) +{ + char *in3; // edx@1 + int result; // eax@1 + char in1; // bl@1 + int ptr; // esi@1 + char *out1; // edi@2 + char c; // al@3 + char c_next; // cl@4 + char v11; // al@6 + unsigned int c_prev; // ebp@6 + int v13; // eax@7 + char in2; // [sp+1h] [bp-1h]@1 + + in3 = input; + result = size; + in1 = *input; + ptr = 1; + in2 = *input; + if ( size > 1 ) + { + out1 = output; + while ( 1 ) + { + c = in3[ptr]; + if ( c != in1 ) + break; + c_next = in3[ptr++ + 1]; + if ( c_next == -1 ) + { + *out1 = in1; +LABEL_9: + ++out1; + goto LABEL_10; + } + v11 = in3[ptr++ + 1]; + c_prev = (uint8_t)c_next + 3; + if ( (signed int)c_prev > 0 ) + { + LOBYTE(segment_offset) = v11; + BYTE1(segment_offset) = v11; + v13 = segment_offset << 16; + LOWORD(v13) = segment_offset; + in1 = in2; + memset32(out1, v13, c_prev >> 2); + in3 = input; + memset(&out1[4 * (c_prev >> 2)], v13, c_prev & 3); + out1 = &output[c_prev]; +LABEL_10: + output = out1; + } + result = size; + ++ptr; + if ( ptr >= size ) + return result; + } + *out1 = c; + goto LABEL_9; + } + return result; +} \ No newline at end of file diff --git a/src/unpaker/pak.cpp b/src/unpaker/pak.cpp new file mode 100644 index 0000000..51738d1 --- /dev/null +++ b/src/unpaker/pak.cpp @@ -0,0 +1,162 @@ +/* + * AIM 1 unpaker + * Copyright (C) 2015 lzwdgc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "pak.h" + +#include +#include +#include + +#include "decode.h" + +#define FREAD(var) fread(&var, 1, sizeof(var), f) + +const int header_size = 0xC; + +void header::load(FILE *f) +{ + FREAD(unk1); + FREAD(unk2); + FREAD(number_of_files); + FREAD(unk3); + FREAD(number_of_chunks); + FREAD(chunk_size); + FREAD(unk5); +} + +void record::load(FILE *f) +{ + FREAD(name); + FREAD(pos); + FREAD(len); +} + +void record::write(string name, const vector &data) const +{ + name += "\\" + string(this->name); + string dir = name.substr(0, name.rfind('\\')); + system(string("mkdir " + dir + " 2> nul").c_str()); + FILE *f = fopen(name.c_str(), "wb"); + if (!f) + return; + fwrite(data.data(), 1, data.size(), f); + fclose(f); +} + +int record::read(pak *pak, void *output, int size) +{ + int file_start_pos = offset + pos; + int segment = file_start_pos / pak->h.chunk_size; + offset += size; + + pak->header[segment].decompress(segment); + + auto file_start_pos2 = file_start_pos - segment * pak->h.chunk_size; + auto size3 = size; + if (pak->h.chunk_size - file_start_pos2 < size) + size3 = pak->h.chunk_size - file_start_pos2; + memcpy(output, pak->header[segment].decoded + file_start_pos2, size3); + + auto size_diff = size - size3; + uint32_t diff = 0; + for (char *out = (char *)output + size3; size_diff > 0; out += diff) + { + segment++; + pak->header[segment].decompress(segment); + + diff = pak->h.chunk_size; + if (diff >= size_diff) + diff = size_diff; + memcpy(out, pak->header[segment].decoded, 4 * (diff >> 2)); + size_diff -= diff; + memcpy( + out + 4 * (diff >> 2), + pak->header[segment].decoded + 4 * (diff >> 2), + diff & 3); + } + return size; +} + +void segment::load_header(FILE *f) +{ + FREAD(unk1); + FREAD(flags); + FREAD(offset); +} + +void segment::load_segment() +{ + auto f = file; + + fseek(f, offset, SEEK_SET); + assert(flags != 0); + + FREAD(size1); + size2 = size1; + if ((flags & 0x3) && (flags & 0xC)) + { + FREAD(size2); + fread(&decoded[0], 1, size2, f); + } + else + { + fread(&encoded[0], 1, size1, f); + } +} + +void segment::decompress(int segment_id) +{ + load_segment(); + + if (flags & 0xC) + if (flags & 0x4) + decode_f1((char*)decoded, size2, (char*)encoded); + else + decode_f2((char*)decoded, size2, (char*)encoded); + if (flags & 0x3) + if (flags & 0x1) + decode_f3((char*)encoded, size1, (char*)decoded); + else + decode_f4((char*)encoded, size1, (char*)decoded, segment_id * header_size); +} + +void pak::load(FILE *f) +{ + h.load(f); + encoded.resize(h.chunk_size * 256 + 128); + decoded.resize(h.chunk_size * 256 + 128); + + int n = h.number_of_files; + while (n--) + { + record rec; + rec.load(f); + files[rec.name] = rec; + } + + n = h.number_of_chunks; + while (n--) + { + segment t; + t.load_header(f); + t.file = f; + t.encoded = encoded.data(); + t.decoded = decoded.data(); + header.push_back(t); + } +} diff --git a/src/unpaker/pak.h b/src/unpaker/pak.h new file mode 100644 index 0000000..c9dbfa1 --- /dev/null +++ b/src/unpaker/pak.h @@ -0,0 +1,86 @@ +/* + * AIM 1 unpaker + * Copyright (C) 2015 lzwdgc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +using namespace std; + +struct header +{ + uint32_t unk1; + uint16_t unk2; + uint16_t number_of_files; + uint16_t unk3; + uint32_t number_of_chunks; + int32_t chunk_size; + uint32_t unk5; + + void load(FILE *f); +}; + +struct record +{ + char name[0x50]; + uint32_t pos; + uint32_t len; + + // + vector data; + int offset = 0; + + void load(FILE *f); + void write(string name, const vector &data) const; + int read(struct pak *pak, void *output, int size); +}; + +struct segment +{ + uint32_t unk1; + uint32_t flags; + uint32_t offset; + + uint32_t size1; + uint32_t size2; + + // + FILE *file = 0; + uint8_t* encoded; + uint8_t* decoded; + + void load_header(FILE *f); + void load_segment(); + void decompress(int segment_id); +}; + +struct pak +{ + header h; + vector header; + map files; + + // + vector encoded; + vector decoded; + + void load(FILE *f); +}; diff --git a/src/unpaker/unpaker.cpp b/src/unpaker/unpaker.cpp new file mode 100644 index 0000000..c020acf --- /dev/null +++ b/src/unpaker/unpaker.cpp @@ -0,0 +1,51 @@ +/* + * AIM 1 unpaker + * Copyright (C) 2015 lzwdgc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "pak.h" + +void unpak(string fn) +{ + FILE *f = fopen(fn.c_str(), "rb"); + if (!f) + return; + pak p; + p.load(f); + auto read_write_any_file = [&](record &file) + { + cout << "Unpacking " << file.name << "\n"; + vector buf(file.len); + file.read(&p, &buf[0], file.len); + file.write(fn + ".dir", buf); + }; + for (auto &f : p.files) + read_write_any_file(f.second); + fclose(f); +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) + { + cout << "Usage: " << argv[0] << " archive.pak" << "\n"; + return 1; + } + unpak(argv[1]); + return 0; +} \ No newline at end of file