mirror of
https://github.com/aimrebirth/tools.git
synced 2026-04-14 17:33:25 +00:00
[mods] Add ability to patch database.
This commit is contained in:
parent
3541f7a2ad
commit
9d77576607
6 changed files with 366 additions and 112 deletions
|
|
@ -29,17 +29,9 @@ deps: pub.lzwdgc.Polygon4.Tools.aim1_mod_maker-master
|
|||
// patch note:
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
mod_maker mod;
|
||||
mod.files_to_distribute.insert(INJECTIONS_FILE_NAME);
|
||||
mod.files_to_distribute.insert(AIM_TYPES_FILE_NAME);
|
||||
|
||||
// patch note: enable double weap gliders (still have many bugs related)
|
||||
mod.make_injection(0x004072FA); // can trade for buy purposes
|
||||
mod.make_injection(0x004D62E4); // setup proper weapon slots for a glider
|
||||
mod.make_injection(0x00417A6D); // put weapon into the right slot after purchase
|
||||
mod.make_injection(0x004176BC); // sell correct weapon
|
||||
mod.make_injection(0x004067C4); // empty light weap
|
||||
mod.make_injection(0x0040688B); // empty heavy weap
|
||||
mod_maker mod("community_fix-0.0.2"s);
|
||||
mod.add_code_file_for_archive(INJECTIONS_FILE_NAME);
|
||||
mod.add_code_file_for_archive(AIM_TYPES_FILE_NAME);
|
||||
|
||||
// patch note: CHANGES
|
||||
// patch note:
|
||||
|
|
@ -90,13 +82,30 @@ int main(int argc, char *argv[]) {
|
|||
"IF(_PLAYERHAS(GL_S2_PA_SINYGR)|_PLAYERHAS(GL_S4_S_SINYGR))",
|
||||
"IF(_ISGLIDER(GL_S2_PA_SINYGR)|_ISGLIDER(GL_S4_S_SINYGR))");
|
||||
|
||||
// patch note: _ISGLIDER() function can check exact glider name now, for example _ISGLIDER(GL_M3_A_FIRST1) (lz)
|
||||
// patch note: * _ISGLIDER() function can check exact glider name now, for example _ISGLIDER(GL_M3_A_FIRST1) (lz)
|
||||
mod.make_injection(0x0043A1F6, 10);
|
||||
//
|
||||
|
||||
// end of scripts section
|
||||
// patch note:
|
||||
|
||||
// patch note: Database Changes
|
||||
// patch note: add name for SINIGR armor, it was unnamed before (lz)
|
||||
mod.db.quest().add_value("INFORMATION"sv, "EQP_ZERO_ARMOR_S_SIN"sv, "NAME", (const char *)u8"Îñîáàÿ íóëü-áðîíÿ");
|
||||
// patch note:
|
||||
|
||||
// patch note: Game Changes
|
||||
// patch note: enable double weapon gliders (lz)
|
||||
// patch note: double light weapons: GL_M2_PA_NARGOON and GL_S3_PS_FINDER1
|
||||
// patch note: double heavy weapons: GL_M3_PA_EYEDSTONE and GL_S3_PS_FINDER2
|
||||
// patch note: (still have many bugs related)
|
||||
mod.make_injection(0x004072FA); // can trade for buy purposes
|
||||
mod.make_injection(0x004D62E4); // setup proper weapon slots for a glider
|
||||
mod.make_injection(0x00417A6D); // put weapon into the right slot after purchase
|
||||
mod.make_injection(0x004176BC); // sell correct weapon
|
||||
mod.make_injection(0x004067C4); // empty light weap
|
||||
mod.make_injection(0x0040688B); // empty heavy weap
|
||||
// patch note:
|
||||
|
||||
// test scripts
|
||||
mod.replace("Script/bin/B_L1_BASE1.scr", "_ADDBALANCE(300)", R"(
|
||||
_ADDBALANCE(300 )
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <db2.h>
|
||||
#include <mmap.h>
|
||||
|
||||
#include <primitives/command.h>
|
||||
#include <primitives/filesystem.h>
|
||||
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <source_location>
|
||||
|
|
@ -14,9 +16,6 @@ constexpr auto aim_exe = "aim.exe"sv;
|
|||
|
||||
using byte_array = std::vector<uint8_t>;
|
||||
|
||||
struct patcher {
|
||||
};
|
||||
|
||||
auto operator""_bin(const char *ptr, uint64_t len) {
|
||||
byte_array ret;
|
||||
auto lines = split_lines(ptr);
|
||||
|
|
@ -51,6 +50,13 @@ auto operator""_bin(const char *ptr, uint64_t len) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
void log(auto &&format, auto &&arg, auto &&...args) {
|
||||
std::println("{}", std::vformat(format, std::make_format_args(arg, args...)));
|
||||
}
|
||||
void log(auto &&str) {
|
||||
std::println("{}", str);
|
||||
}
|
||||
|
||||
struct aim_exe_v1_06_constants {
|
||||
enum : uint32_t {
|
||||
trampoline_base_real = 0x00025100,
|
||||
|
|
@ -75,20 +81,43 @@ struct mod_maker {
|
|||
script,
|
||||
sound,
|
||||
};
|
||||
enum pak_files {
|
||||
};
|
||||
struct {
|
||||
mod_maker &m;
|
||||
db2 db_;
|
||||
db2 quest_;
|
||||
|
||||
db2 &db() {
|
||||
add_files("db");
|
||||
return db_;
|
||||
}
|
||||
db2 &quest() {
|
||||
add_files("quest");
|
||||
return quest_;
|
||||
}
|
||||
private:
|
||||
void add_files(const path &fn) {
|
||||
auto base = path{"data"} / fn;
|
||||
m.files_to_distribute.insert(path{base} += ".ind");
|
||||
m.files_to_distribute.insert(path{base} += ".dat");
|
||||
m.files_to_distribute.insert(path{base} += ".tab");
|
||||
}
|
||||
} db{*this};
|
||||
|
||||
std::string name;
|
||||
std::string version;
|
||||
path game_dir;
|
||||
std::set<path> files_to_pak;
|
||||
std::set<path> files_to_distribute;
|
||||
std::set<path> code_files_to_distribute;
|
||||
std::source_location loc;
|
||||
|
||||
mod_maker(std::source_location loc = std::source_location::current()) : loc{loc} {
|
||||
init(fs::current_path());
|
||||
}
|
||||
mod_maker(const path &dir, std::source_location loc = std::source_location::current()) : loc{loc} {
|
||||
mod_maker(const std::string &name, std::source_location loc = std::source_location::current()) : name{name}, loc{loc} {
|
||||
init(fs::current_path());
|
||||
}
|
||||
mod_maker(const std::string &name, const path &dir, std::source_location loc = std::source_location::current()) : name{name}, loc{loc} {
|
||||
init(dir);
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +143,9 @@ struct mod_maker {
|
|||
}
|
||||
}
|
||||
void apply() {
|
||||
db.db_.close();
|
||||
db.quest_.close();
|
||||
|
||||
std::vector<std::string> files;
|
||||
for (auto &&p : files_to_pak) {
|
||||
if (p.filename() == aim_exe) {
|
||||
|
|
@ -140,7 +172,10 @@ struct mod_maker {
|
|||
auto pos = line.find(anchor);
|
||||
if (pos != -1) {
|
||||
auto s = line.substr(pos + anchor.size());
|
||||
boost::trim(s);
|
||||
if (!s.empty() && s[0] == ' ') {
|
||||
s = s.substr(1);
|
||||
}
|
||||
boost::trim_right(s);
|
||||
if (!s.empty() && (s[0] >= 'a' && s[0] <= 'z' || s[0] >= '0' && s[0] <= '9')) {
|
||||
s = "* " + s;
|
||||
}
|
||||
|
|
@ -151,17 +186,41 @@ struct mod_maker {
|
|||
|
||||
// we do not check for presence of 7z command here
|
||||
if (has_in_path("7z")) {
|
||||
auto ar = get_full_mod_name() + ".zip";
|
||||
|
||||
primitives::Command c;
|
||||
c.working_directory = game_dir;
|
||||
c.push_back("7z");
|
||||
c.push_back("a");
|
||||
c.push_back(get_full_mod_name() + ".zip"); // we use zip as more common
|
||||
c.push_back(ar); // we use zip as more common
|
||||
for (auto &&f : files_to_distribute) {
|
||||
c.push_back(f);
|
||||
}
|
||||
for (auto &&f : code_files_to_distribute) {
|
||||
c.push_back(f);
|
||||
}
|
||||
run_command(c);
|
||||
|
||||
auto rename = [&](auto &&from, auto &&to) {
|
||||
primitives::Command c;
|
||||
c.working_directory = game_dir;
|
||||
c.push_back("7z");
|
||||
c.push_back("rn");
|
||||
c.push_back(ar);
|
||||
c.push_back(from);
|
||||
c.push_back(to);
|
||||
run_command(c);
|
||||
};
|
||||
for (auto &&f : code_files_to_distribute) {
|
||||
if (f.filename() == path{loc.file_name()}.filename()) {
|
||||
rename(f.filename(), path{"data"} / "mods" / get_full_mod_name() / get_full_mod_name() += ".cpp");
|
||||
} else {
|
||||
rename(f.filename(), path{"data"} / "mods" / get_full_mod_name() / f.filename());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void patch(path fn, uint32_t offset, T val) {
|
||||
fn = find_real_filename(fn);
|
||||
|
|
@ -190,7 +249,7 @@ struct mod_maker {
|
|||
memcpy(ptr, make_insn_with_address("e8"_bin, aim_exe_v1_06_constants::free_data_base_virtual -
|
||||
(virtual_address + call_command_length)));
|
||||
memcpy(ptr, make_nops(len - call_command_length));
|
||||
std::println("making injection on the virtual address 0x{:0X} (real address 0x{:0X}), size {}", virtual_address, ptr - f.p,
|
||||
log("making injection on the virtual address 0x{:0X} (real address 0x{:0X}), size {}", virtual_address, ptr - f.p,
|
||||
size);
|
||||
}
|
||||
|
||||
|
|
@ -201,17 +260,25 @@ struct mod_maker {
|
|||
ENABLE_DISABLE_FUNC(win_key, 0x00, 0x10)
|
||||
#undef ENABLE_DISABLE_FUNC
|
||||
|
||||
void add_code_file_for_archive(const path &fn) {
|
||||
code_files_to_distribute.insert(path{loc.file_name()}.parent_path() / fn);
|
||||
}
|
||||
|
||||
private:
|
||||
void init(const path &dir) {
|
||||
read_name();
|
||||
detect_game_dir(dir);
|
||||
fs::create_directories(get_mod_dir());
|
||||
files_to_distribute.insert(loc.file_name());
|
||||
code_files_to_distribute.insert(loc.file_name());
|
||||
db.db_.open(get_data_dir() / "db", primitives::templates2::mmap_file<uint8_t>::rw{});
|
||||
db.quest_.open(get_data_dir() / "quest", primitives::templates2::mmap_file<uint8_t>::rw{});
|
||||
detect_tools();
|
||||
prepare_injections();
|
||||
}
|
||||
void read_name() {
|
||||
if (name.empty()) {
|
||||
name = path{loc.file_name()}.stem().string();
|
||||
}
|
||||
// use regex?
|
||||
auto p = name.find('-');
|
||||
if (p != -1) {
|
||||
|
|
@ -261,6 +328,8 @@ private:
|
|||
return arr;
|
||||
}
|
||||
void make_injected_dll() {
|
||||
log("making injected dll");
|
||||
|
||||
path fn = loc.file_name();
|
||||
//fs::copy_file(fn, get_mod_dir() / fn.filename(), fs::copy_options::overwrite_existing);
|
||||
std::string contents;
|
||||
|
|
@ -306,7 +375,9 @@ private:
|
|||
enable_free_camera();
|
||||
#endif
|
||||
create_backup_exe_file();
|
||||
#ifdef NDEBUG
|
||||
make_injected_dll();
|
||||
#endif
|
||||
files_to_distribute.insert(aim_exe);
|
||||
primitives::templates2::mmap_file<uint8_t> f{find_real_filename(aim_exe), primitives::templates2::mmap_file<uint8_t>::rw{}};
|
||||
uint32_t our_data = aim_exe_v1_06_constants::our_code_start_virtual;
|
||||
|
|
@ -439,34 +510,34 @@ FF D7 ; call edi
|
|||
void patch_raw(path fn, uint32_t offset, T val) const {
|
||||
primitives::templates2::mmap_file<uint8_t> f{fn, primitives::templates2::mmap_file<uint8_t>::rw{}};
|
||||
auto &old = *(T *)(f.p + offset);
|
||||
std::println("patching {} offset 0x{:08X} to {} (old value: {})", fn.string(), offset, val, old);
|
||||
log("patching {} offset 0x{:08X} to {} (old value: {})", fn.string(), offset, val, old);
|
||||
old = val;
|
||||
}
|
||||
template <typename T>
|
||||
bool patch_raw(path fn, uint32_t offset, T expected, T val) const {
|
||||
primitives::templates2::mmap_file<uint8_t> f{fn, primitives::templates2::mmap_file<uint8_t>::rw{}};
|
||||
auto &old = *(T *)(f.p + offset);
|
||||
std::println("patching {} offset 0x{:08X} from {} to {}", fn.string(), offset, expected, val);
|
||||
log("patching {} offset 0x{:08X} from {} to {}", fn.string(), offset, expected, val);
|
||||
if (old == expected) {
|
||||
std::println("success");
|
||||
log("success");
|
||||
old = val;
|
||||
return true;
|
||||
} else if (old == val) {
|
||||
std::println("success, already patched");
|
||||
log("success, already patched");
|
||||
return true;
|
||||
} else {
|
||||
std::println("old value {} != expected {}", old, expected);
|
||||
log("old value {} != expected {}", old, expected);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
path get_mod_dir() const {
|
||||
return get_data_dir() / "mods" / name;
|
||||
return get_data_dir() / "mods" / get_full_mod_name();
|
||||
}
|
||||
path get_data_dir() const {
|
||||
return game_dir / "data";
|
||||
}
|
||||
static void replace_in_file_raw(const path &fn, const std::string &from, const std::string &to) {
|
||||
std::println("replacing in file {} from '{}' to '{}'", fn.string(), from, to);
|
||||
log("replacing in file {} from '{}' to '{}'", fn.string(), from, to);
|
||||
auto f = read_file(fn);
|
||||
boost::replace_all(f, from, to);
|
||||
boost::replace_all(f, "\r", "");
|
||||
|
|
@ -501,7 +572,7 @@ FF D7 ; call edi
|
|||
static void run_command(auto &c) {
|
||||
c.out.inherit = true;
|
||||
c.err.inherit = true;
|
||||
std::cout << c.print() << "\n";
|
||||
log(c.print());
|
||||
c.execute();
|
||||
}
|
||||
void detect_game_dir(const path &dir) {
|
||||
|
|
|
|||
|
|
@ -58,3 +58,15 @@ std::wstring str2utf16(const std::string &codepage_str, int cp)
|
|||
SW_UNIMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string str2str(const std::string &codepage_str, int cp_from, int cp_to) {
|
||||
#ifdef _WIN32
|
||||
auto utf16_str = str2utf16(codepage_str, cp_from);
|
||||
int dest_size = WideCharToMultiByte(cp_to, 0, utf16_str.c_str(), utf16_str.length(), nullptr, 0, nullptr, nullptr);
|
||||
std::string dest_str(dest_size, '\0');
|
||||
WideCharToMultiByte(cp_to, 0, utf16_str.c_str(), utf16_str.length(), &dest_str[0], dest_size, nullptr, nullptr);
|
||||
return dest_str;
|
||||
#else
|
||||
SW_UNIMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
std::string str2utf8(const std::string &codepage_str, int cp = 0);
|
||||
std::wstring str2utf16(const std::string &codepage_str, int cp = 0);
|
||||
|
||||
std::string str2str(const std::string &codepage_str, int cp_from, int cp_to);
|
||||
|
||||
struct progress_bar {
|
||||
const size_t max_elements;
|
||||
const int displaylen;
|
||||
|
|
|
|||
234
src/common/db2.h
Normal file
234
src/common/db2.h
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* AIM db_extractor
|
||||
* Copyright (C) 2024 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 "common.h"
|
||||
#include "mmap.h"
|
||||
|
||||
struct db2 {
|
||||
using char20 = char[0x20];
|
||||
|
||||
enum class field_type : uint32_t {
|
||||
string,
|
||||
integer,
|
||||
float_,
|
||||
};
|
||||
|
||||
// table structure
|
||||
struct tab {
|
||||
struct table {
|
||||
uint32_t id;
|
||||
char20 name;
|
||||
uint32_t unk;
|
||||
};
|
||||
struct field {
|
||||
uint32_t table_id;
|
||||
uint32_t id;
|
||||
char20 name;
|
||||
field_type type;
|
||||
};
|
||||
|
||||
uint32_t n_tables;
|
||||
uint32_t n_fields;
|
||||
|
||||
auto tables() {
|
||||
auto base = (table *)(&n_fields + 1);
|
||||
return std::span{base, base + n_tables};
|
||||
}
|
||||
auto fields() {
|
||||
auto table_base = (table *)(&n_fields + 1);
|
||||
auto base = (field *)(table_base + n_tables);
|
||||
return std::span{base, base + n_fields};
|
||||
}
|
||||
};
|
||||
// table values (index)
|
||||
struct ind {
|
||||
struct value {
|
||||
uint32_t table_id;
|
||||
char20 name;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
uint32_t n_values;
|
||||
|
||||
auto values() {
|
||||
auto base = (value *)(&n_values + 1);
|
||||
return std::span{base, base + n_values};
|
||||
}
|
||||
};
|
||||
// field values
|
||||
struct dat {
|
||||
// NOTE: for some reason int fields can be != 4
|
||||
// so follow this size field
|
||||
struct field_value_base {
|
||||
uint32_t field_id;
|
||||
uint32_t size;
|
||||
};
|
||||
};
|
||||
|
||||
path fn;
|
||||
primitives::templates2::mmap_file<uint8_t> fdat, find, ftab;
|
||||
tab *tab_{};
|
||||
ind *ind_{};
|
||||
dat *dat_{};
|
||||
|
||||
db2() = default;
|
||||
db2(const path &fn) : fn{fn} {
|
||||
open(fn, primitives::templates2::mmap_file<uint8_t>::ro{});
|
||||
}
|
||||
|
||||
void open(const path &fn, auto mode) {
|
||||
close();
|
||||
this->fn = fn;
|
||||
fdat.open(path{fn} += ".dat", mode);
|
||||
find.open(path{fn} += ".ind", mode);
|
||||
ftab.open(path{fn} += ".tab", mode);
|
||||
|
||||
dat_ = (dat *)find.p;
|
||||
ind_ = (ind *)find.p;
|
||||
tab_ = (tab *)ftab.p;
|
||||
}
|
||||
void open(auto mode) {
|
||||
open(fn, mode);
|
||||
}
|
||||
void close() {
|
||||
fdat.close();
|
||||
find.close();
|
||||
ftab.close();
|
||||
}
|
||||
|
||||
void add_value(std::string_view table, std::string_view value, auto && ... fields1) {
|
||||
auto tbl = tab_->tables();
|
||||
auto fields = tab_->fields();
|
||||
auto values = ind_->values();
|
||||
|
||||
auto calc_fields_size = [](this auto &&f, std::string_view field_name, auto &&n, auto &&v, auto &&...fields) {
|
||||
if (field_name == n) {
|
||||
if constexpr (std::same_as<std::decay_t<decltype(v)>, int>) {
|
||||
return sizeof(db2::dat::field_value_base) + sizeof(int);
|
||||
} else if constexpr (std::same_as<std::decay_t<decltype(v)>, float>) {
|
||||
return sizeof(db2::dat::field_value_base) + sizeof(float);
|
||||
} else {
|
||||
auto s = str2str(v, CP_UTF8, 1251);
|
||||
return sizeof(db2::dat::field_value_base) + s.size() + 1;
|
||||
}
|
||||
}
|
||||
if constexpr (sizeof...(fields)) {
|
||||
return f(field_name, fields...);
|
||||
}
|
||||
if constexpr (std::same_as<std::decay_t<decltype(v)>, int>) {
|
||||
return sizeof(db2::dat::field_value_base) + sizeof(int);
|
||||
} else if constexpr (std::same_as<std::decay_t<decltype(v)>, float>) {
|
||||
return sizeof(db2::dat::field_value_base) + sizeof(float);
|
||||
} else {
|
||||
return sizeof(db2::dat::field_value_base) + 1;
|
||||
}
|
||||
};
|
||||
auto write_fields = [](this auto &&f, auto &&p, auto &&field, std::string_view field_name, auto &&n, auto &&v, auto &&...fields) {
|
||||
if (field_name == n) {
|
||||
if constexpr (std::same_as<std::decay_t<decltype(v)>, int>) {
|
||||
if (field.type != db2::field_type::integer) {
|
||||
throw std::runtime_error{"field type mismatch"};
|
||||
}
|
||||
(*(db2::dat::field_value_base*)p).field_id = field.id;
|
||||
(*(db2::dat::field_value_base*)p).size = sizeof(int);
|
||||
p += sizeof(db2::dat::field_value_base);
|
||||
*(int*)p = v;
|
||||
p += sizeof(int);
|
||||
return;
|
||||
} else if constexpr (std::same_as<std::decay_t<decltype(v)>, float>) {
|
||||
if (field.type != db2::field_type::float_) {
|
||||
throw std::runtime_error{"field type mismatch"};
|
||||
}
|
||||
(*(db2::dat::field_value_base *)p).field_id = field.id;
|
||||
(*(db2::dat::field_value_base *)p).size = sizeof(float);
|
||||
p += sizeof(db2::dat::field_value_base);
|
||||
*(float *)p = v;
|
||||
p += sizeof(float);
|
||||
return;
|
||||
} else {
|
||||
if (field.type != db2::field_type::string) {
|
||||
throw std::runtime_error{"field type mismatch"};
|
||||
}
|
||||
auto s = str2str(v, CP_UTF8, 1251);
|
||||
(*(db2::dat::field_value_base *)p).field_id = field.id;
|
||||
(*(db2::dat::field_value_base *)p).size = s.size() + 1;
|
||||
p += sizeof(db2::dat::field_value_base);
|
||||
memcpy(p, s.data(), s.size());
|
||||
p[s.size()] = 0;
|
||||
p += s.size() + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if constexpr (sizeof...(fields)) {
|
||||
return f(p, field, field_name, fields...);
|
||||
}
|
||||
if constexpr (std::same_as<std::decay_t<decltype(v)>, int>) {
|
||||
(*(db2::dat::field_value_base *)p).field_id = field.id;
|
||||
(*(db2::dat::field_value_base *)p).size = 0;
|
||||
p += sizeof(db2::dat::field_value_base);
|
||||
return;
|
||||
} else if constexpr (std::same_as<std::decay_t<decltype(v)>, float>) {
|
||||
(*(db2::dat::field_value_base *)p).field_id = field.id;
|
||||
(*(db2::dat::field_value_base *)p).size = 0;
|
||||
p += sizeof(db2::dat::field_value_base);
|
||||
return;
|
||||
} else {
|
||||
(*(db2::dat::field_value_base *)p).field_id = field.id;
|
||||
(*(db2::dat::field_value_base *)p).size = 1;
|
||||
p += sizeof(db2::dat::field_value_base);
|
||||
p[1] = 0;
|
||||
p += 1;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
auto it = std::ranges::find_if(tbl, [&](auto &v){return v.name == table;});
|
||||
if (it == tbl.end()) {
|
||||
throw std::runtime_error{"no such table"};
|
||||
}
|
||||
auto &t = *it;
|
||||
auto itv = std::ranges::find_if(values, [&](auto &v){return v.table_id == t.id && value == v.name;});
|
||||
if (itv == values.end()) {
|
||||
db2::ind::value i{};
|
||||
i.table_id = t.id;
|
||||
memcpy(i.name, value.data(), value.size());
|
||||
i.offset = fdat.sz;
|
||||
for (auto &&f : fields) {
|
||||
if (f.table_id != t.id) {
|
||||
continue;
|
||||
}
|
||||
std::string_view fn = f.name;
|
||||
i.size += calc_fields_size(fn, fields1...);
|
||||
}
|
||||
|
||||
++ind_->n_values;
|
||||
auto p = find.alloc_raw(find.sz + sizeof(i));
|
||||
memcpy(p, &i, sizeof(i));
|
||||
|
||||
p = fdat.alloc_raw(fdat.sz + i.size);
|
||||
for (auto &&f : fields) {
|
||||
if (f.table_id != t.id) {
|
||||
continue;
|
||||
}
|
||||
std::string_view fn = f.name;
|
||||
write_fields(p, f, fn, fields1...);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* AIM db_extractor
|
||||
* Copyright (C) 2015 lzwdgc
|
||||
* Copyright (C) 2024 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
|
||||
|
|
@ -16,12 +16,10 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "db.h"
|
||||
#include "db2.h"
|
||||
|
||||
#include <buffer.h>
|
||||
#include <common.h>
|
||||
#include <mmap.h>
|
||||
#include <mmap.h>
|
||||
|
||||
#include <primitives/sw/main.h>
|
||||
#include <primitives/sw/settings.h>
|
||||
|
|
@ -31,78 +29,6 @@
|
|||
#include <span>
|
||||
#include <print>
|
||||
|
||||
struct db2 {
|
||||
using char20 = char[0x20];
|
||||
|
||||
// table structure
|
||||
struct tab {
|
||||
struct table {
|
||||
uint32_t id;
|
||||
char20 name;
|
||||
uint32_t unk;
|
||||
};
|
||||
struct field {
|
||||
uint32_t table_id;
|
||||
uint32_t id;
|
||||
char20 name;
|
||||
FieldType type;
|
||||
};
|
||||
|
||||
uint32_t n_tables;
|
||||
uint32_t n_fields;
|
||||
|
||||
auto tables() {
|
||||
auto base = (table *)(&n_fields+1);
|
||||
return std::span{base, base+n_tables};
|
||||
}
|
||||
auto fields() {
|
||||
auto table_base = (table *)(&n_fields + 1);
|
||||
auto base = (field *)(table_base + n_tables);
|
||||
return std::span{base, base + n_fields};
|
||||
}
|
||||
};
|
||||
// table values (index)
|
||||
struct ind {
|
||||
struct value {
|
||||
uint32_t table_id;
|
||||
char20 name;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
uint32_t n_values;
|
||||
|
||||
auto values() {
|
||||
auto base = (value *)(&n_values + 1);
|
||||
return std::span{base, base + n_values};
|
||||
}
|
||||
};
|
||||
// field values
|
||||
struct dat {
|
||||
// NOTE: for some reason int fields can be != 4
|
||||
// so follow this size field
|
||||
struct field_value_base {
|
||||
uint32_t field_id;
|
||||
uint32_t size;
|
||||
};
|
||||
};
|
||||
|
||||
primitives::templates2::mmap_file<uint8_t> fdat,find,ftab;
|
||||
tab *tab_;
|
||||
ind *ind_;
|
||||
dat *dat_;
|
||||
|
||||
db2(const path &fn) {
|
||||
fdat.open(path{fn} += ".dat");
|
||||
find.open(path{fn} += ".ind");
|
||||
ftab.open(path{fn} += ".tab");
|
||||
|
||||
dat_ = (dat *)find.p;
|
||||
ind_ = (ind *)find.p;
|
||||
tab_ = (tab *)ftab.p;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
cl::opt<path> db_fn(cl::Positional, cl::desc("<db file>"), cl::Required);
|
||||
|
|
@ -130,19 +56,19 @@ int main(int argc, char *argv[])
|
|||
continue;
|
||||
}
|
||||
switch (f->type) {
|
||||
case FieldType::Integer: {
|
||||
case db2::field_type::integer: {
|
||||
auto fv = (int*)p;
|
||||
p += vb->size;
|
||||
std::println("{}{}: {}", spacefield, f->name, *fv);
|
||||
break;
|
||||
}
|
||||
case FieldType::Float: {
|
||||
case db2::field_type::float_: {
|
||||
auto fv = (float*)p;
|
||||
p += vb->size;
|
||||
std::println("{}{}: {}", spacefield, f->name, *fv);
|
||||
break;
|
||||
}
|
||||
case FieldType::String: {
|
||||
case db2::field_type::string: {
|
||||
auto fv = (const char*)p;
|
||||
p += vb->size;
|
||||
std::println("{}{}: {}", spacefield, f->name, fv);
|
||||
|
|
|
|||
Loading…
Reference in a new issue