diff --git a/src/common/db2.h b/src/common/db2.h index 4f0dfa8..4c86f98 100644 --- a/src/common/db2.h +++ b/src/common/db2.h @@ -86,6 +86,7 @@ struct db2 { tab *tab_{}; ind *ind_{}; dat *dat_{}; + int codepage{1251}; db2() = default; db2(const path &fn) : fn{fn} { @@ -117,14 +118,14 @@ struct db2 { 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) { + 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, int>) { return sizeof(db2::dat::field_value_base) + sizeof(int); } else if constexpr (std::same_as, float>) { return sizeof(db2::dat::field_value_base) + sizeof(float); } else { - auto s = str2str(v, CP_UTF8, 1251); + auto s = str2str(v, CP_UTF8, codepage); return sizeof(db2::dat::field_value_base) + s.size() + 1; } } @@ -139,7 +140,7 @@ struct db2 { 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) { + 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, int>) { if (field.type != db2::field_type::integer) { @@ -165,7 +166,7 @@ struct db2 { if (field.type != db2::field_type::string) { throw std::runtime_error{"field type mismatch"}; } - auto s = str2str(v, CP_UTF8, 1251); + auto s = str2str(v, CP_UTF8, codepage); (*(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); diff --git a/src/common/mmap.h b/src/common/mmap.h index 5a8a9ce..0306557 100644 --- a/src/common/mmap.h +++ b/src/common/mmap.h @@ -24,6 +24,11 @@ struct stream { memcpy(p, (uint8_t*)&v, sizeof(v)); p += sizeof(v); } + template + void read(std::vector &v) { + memcpy(v.data(), p, sizeof(T) * v.size()); + p += sizeof(T) * v.size(); + } void skip(size_t len) { p += len; } diff --git a/src/common/mmo2.h b/src/common/mmo2.h new file mode 100644 index 0000000..ea484ee --- /dev/null +++ b/src/common/mmo2.h @@ -0,0 +1,124 @@ +#pragma once + +#include "mmap.h" + +struct mmo_storage2 { + struct object { + uint32_t type; + uint32_t size; + uint32_t n_objects; + }; + struct mech_group { + char name[0x20]; + char org[0x20]; + // probably state + // alive + // dead + // in the base + // in the other mech cargo + uint32_t type; + uint32_t n_mechs; + char org_ru[0x70]; + }; + struct map_good { + char name[0x20]; + char cond[0x40]; + float buy_price; + float sell_price; + float unk[8]; + uint32_t unk1; + }; + + // our own data + struct section { + uint32_t offset; + }; + struct { + section objects; + section mech_groups; + section map_goods; + section music_and_sounds; + } sections; + struct map_building { + uint32_t offset; + std::map building_goods; + }; + std::map map_building_goods; + + void load(auto &&fn) { + primitives::templates2::mmap_file f{fn}; + stream s{f}; + // objects + { + sections.objects.offset = s.p - f.p; + uint32_t n_segments = s; + while (n_segments--) { + object o = s; + s.skip(o.size); + } + } + // mech_groups + { + sections.mech_groups.offset = s.p - f.p; + uint32_t n_mechs = s; + s.skip(0x30); + while (n_mechs--) { + mech_group o = s; + + switch (o.type) { + case 0: + case 1: { + uint32_t unk0 = s; // road id? + float unk1 = s; // height? + } break; + case 2: { + std::vector t; // current path? + uint32_t len = s; + t.resize(len); + s.read(t); + } break; + case 3: // 3 = free mechanoids only? + case 4: { + uint32_t t = s; // other mech id? + } break; + default: + assert(false); + } + + while (o.n_mechs--) { + struct mech { + char name[0x20]; + }; + mech m = s; + } + bool hidden = s; + } + } + // map goods + { + sections.map_goods.offset = s.p - f.p; + uint32_t size = s; + uint32_t unk2 = s; + float unk3 = s; + uint32_t n_buildings = s; + while (n_buildings--) { + struct bld { + char name[0x20]; + }; + bld b = s; + map_building_goods[b.name].offset = s.p - f.p; + uint32_t n_goods = s; + while (n_goods--) { + map_good g = s; + map_building_goods[b.name].building_goods[g.name] = s.p - f.p; + } + } + } + // music & sounds section + { + sections.music_and_sounds.offset = s.p - f.p; + uint32_t size = s; + s.skip(size); + } + } +}; diff --git a/src/mmo_extractor2/mmo_extractor2.cpp b/src/mmo_extractor2/mmo_extractor2.cpp new file mode 100644 index 0000000..33e8377 --- /dev/null +++ b/src/mmo_extractor2/mmo_extractor2.cpp @@ -0,0 +1,52 @@ +/* + * AIM mmo_extractor2 (only for aim1) + * 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 . + */ + +#include "mmo2.h" +#include "objects.h" + +#include +#include + +void read_mmo(const path &fn) +{ + mmo_storage2 s; + s.load(fn); +} + +int main(int argc, char *argv[]) +{ + cl::opt p(cl::Positional, cl::desc("<.mmo file or directory with .mmo files>"), cl::value_desc("file or directory"), cl::Required); + + cl::ParseCommandLineOptions(argc, argv); + + if (fs::is_regular_file(p)) + read_mmo(p); + else if (fs::is_directory(p)) + { + auto files = enumerate_files_like(p, ".*\\.[Mm][Mm][Oo]", false); + for (auto &file : files) + { + std::cerr << "processing: " << file << "\n"; + read_mmo(file); + } + } + else + throw std::runtime_error("Bad fs object"); + + return 0; +}