mirror of
https://github.com/aimrebirth/tools.git
synced 2026-04-14 17:33:25 +00:00
Add db_extractor, unpaker.
This commit is contained in:
commit
da923801b3
11 changed files with 1093 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
win*
|
||||
18
CMakeLists.txt
Normal file
18
CMakeLists.txt
Normal file
|
|
@ -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)
|
||||
1
README.md
Normal file
1
README.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
This repository contains different tools for A.I.M. games.
|
||||
5
src/CMakeLists.txt
Normal file
5
src/CMakeLists.txt
Normal file
|
|
@ -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})
|
||||
149
src/db_extractor/db.cpp
Normal file
149
src/db_extractor/db.cpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
137
src/db_extractor/db.h
Normal file
137
src/db_extractor/db.h
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<uint32_t, table> tables;
|
||||
map<uint32_t, field> 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<char> buf;
|
||||
|
||||
s_file(const vector<char> &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<char> buf;
|
||||
//
|
||||
uint32_t number_of_fields;
|
||||
vector<field_value> 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<value> values;
|
||||
|
||||
void load(FILE *f);
|
||||
};
|
||||
170
src/db_extractor/db_extractor.cpp
Normal file
170
src/db_extractor/db_extractor.cpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "db.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
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<int,int> 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;
|
||||
}
|
||||
313
src/unpaker/decode.h
Normal file
313
src/unpaker/decode.h
Normal file
|
|
@ -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<class T> 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<class T, class U> 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;
|
||||
}
|
||||
162
src/unpaker/pak.cpp
Normal file
162
src/unpaker/pak.cpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pak.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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<char> &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);
|
||||
}
|
||||
}
|
||||
86
src/unpaker/pak.h
Normal file
86
src/unpaker/pak.h
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<char> data;
|
||||
int offset = 0;
|
||||
|
||||
void load(FILE *f);
|
||||
void write(string name, const vector<char> &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<segment> header;
|
||||
map<string, record> files;
|
||||
|
||||
//
|
||||
vector<uint8_t> encoded;
|
||||
vector<uint8_t> decoded;
|
||||
|
||||
void load(FILE *f);
|
||||
};
|
||||
51
src/unpaker/unpaker.cpp
Normal file
51
src/unpaker/unpaker.cpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#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<char> 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;
|
||||
}
|
||||
Loading…
Reference in a new issue