├── .gitignore ├── src ├── aim1_mod_maker │ ├── aim_fixes.natvis │ └── aim.exe.injections.h ├── common │ ├── bmp.h │ ├── mmap.h │ ├── tga.h │ ├── common.h │ ├── color.h │ ├── common.natvis │ ├── common.cpp │ ├── db.h │ ├── objects.cpp │ ├── types.cpp │ ├── objects.h │ ├── buffer.h │ ├── buffer.cpp │ ├── mat.h │ ├── db.cpp │ ├── dxt5.h │ ├── mmo2.h │ └── types.h ├── mpj_loader │ ├── mpj_loader.cpp │ ├── mpj.h │ └── mpj.cpp ├── aim1_mod_activator │ └── mod_activator.cpp ├── mmo_extractor2 │ └── mmo_extractor2.cpp ├── save_loader │ └── save_loader.cpp ├── mod_reader │ └── mod_reader.cpp ├── unpaker │ ├── decode.h │ └── unpaker.cpp ├── mmm_extractor │ └── mmm_extractor.cpp ├── db_extractor2 │ └── db_extractor2.cpp ├── mmp_extractor │ ├── mmp_extractor.cpp │ ├── mmp.h │ └── mmp_tex_aim1.txt ├── txt2script │ └── txt2script.cpp ├── name_generator │ └── name_generator.cpp ├── tm_converter │ └── tm_converter.cpp ├── rgb_converter │ └── rgb_converter.cpp ├── script2txt │ └── script2txt.cpp ├── db_extractor │ └── db_extractor.cpp ├── mod_converter2 │ └── mod_converter2.cpp ├── mod_converter │ └── mod_converter.cpp ├── paker │ └── paker.cpp ├── mmo_extractor │ └── other.h ├── bms_converter │ └── bms_converter.cpp ├── tm_converter2 │ └── tm_converter2.cpp ├── aim1_language_switcher │ └── language_switcher.cpp ├── db_add_language │ └── db_add_language.cpp └── model │ └── model.h ├── .github └── workflows │ └── sw.yml ├── formats └── tab.ksy ├── README.md └── sw.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | win* 2 | build 3 | bin 4 | .cppan 5 | doc 6 | 7 | *.dll 8 | *.lnk 9 | *.exe 10 | *.bat 11 | 12 | .sw 13 | 14 | *.diff 15 | *.patch 16 | 17 | *.dat 18 | *.ind 19 | *.tab 20 | *.json 21 | 22 | *.bmp -------------------------------------------------------------------------------- /src/aim1_mod_maker/aim_fixes.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {&idx,s} 7 | 8 | 9 | 10 | {idx} 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.github/workflows/sw.yml: -------------------------------------------------------------------------------- 1 | name: sw 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | # every day 8 | - cron: 0 0 * * * 9 | 10 | jobs: 11 | build: 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: [windows-latest] 17 | 18 | steps: 19 | - uses: actions/checkout@v1 20 | - uses: egorpugin/sw-action@master 21 | 22 | - name: build 23 | run: ./sw -static build 24 | 25 | - uses: actions/upload-artifact@v3 26 | with: 27 | name: sw 28 | path: .sw/out/**/*.exe 29 | -------------------------------------------------------------------------------- /src/common/bmp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #pragma pack(push, 1) 6 | 7 | struct bmp_header 8 | { 9 | int16_t bfType; 10 | int32_t bfSize; 11 | int16_t bfReserved1; 12 | int16_t bfReserved2; 13 | int32_t bfOffBits; 14 | }; 15 | 16 | struct bmp_info_header 17 | { 18 | int32_t biSize; 19 | int32_t biWidth; 20 | int32_t biHeight; 21 | int16_t biPlanes; 22 | int16_t biBitCount; 23 | int32_t biCompression; 24 | int32_t biSizeImage; 25 | int32_t biXPelsPerMeter; 26 | int32_t biYPelsPerMeter; 27 | int32_t biClrUsed; 28 | int32_t biClrImportant; 29 | }; 30 | 31 | struct rgb_quad 32 | { 33 | int8_t rgbBlue; 34 | int8_t rgbGreen; 35 | int8_t rgbRed; 36 | int8_t rgbReserved; 37 | }; 38 | 39 | struct bmp_info 40 | { 41 | 42 | bmp_info_header bmiHeader; 43 | rgb_quad bmiColors[1]; 44 | }; 45 | 46 | #pragma pack(pop) 47 | -------------------------------------------------------------------------------- /src/common/mmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct stream { 7 | primitives::templates2::mmap_file &m; 8 | uint8_t *p{m.p}; 9 | 10 | template 11 | operator T &() { 12 | auto &r = *(T *)p; 13 | p += sizeof(T); 14 | return r; 15 | } 16 | template 17 | auto span(size_t len) { 18 | auto s = std::span((T *)p, len); 19 | p += sizeof(T) * len; 20 | return s; 21 | } 22 | template 23 | void operator=(const T &v) { 24 | memcpy(p, (uint8_t*)&v, sizeof(v)); 25 | p += sizeof(v); 26 | } 27 | template 28 | void read(std::vector &v) { 29 | memcpy(v.data(), p, sizeof(T) * v.size()); 30 | p += sizeof(T) * v.size(); 31 | } 32 | void skip(size_t len) { 33 | p += len; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /formats/tab.ksy: -------------------------------------------------------------------------------- 1 | meta: 2 | id: aim_db_tab 3 | endian: le 4 | 5 | seq: 6 | - id: header 7 | type: tab 8 | - id: tables 9 | type: table 10 | repeat: expr 11 | repeat-expr: header.n_tables 12 | - id: fields 13 | type: field 14 | repeat: expr 15 | repeat-expr: header.n_fields 16 | 17 | types: 18 | char20: 19 | seq: 20 | - id: str 21 | type: str 22 | encoding: ASCII 23 | size: 0x20 24 | 25 | tab: 26 | seq: 27 | - id: n_tables 28 | type: u4 29 | - id: n_fields 30 | type: u4 31 | 32 | table: 33 | seq: 34 | - id: id 35 | type: u4 36 | - id: name 37 | type: char20 38 | - id: unk 39 | type: u4 40 | 41 | field: 42 | seq: 43 | - id: table_id 44 | type: u4 45 | - id: id 46 | type: u4 47 | - id: name 48 | type: char20 49 | - id: field_type 50 | type: u4 51 | -------------------------------------------------------------------------------- /src/aim1_mod_maker/aim.exe.injections.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef DONT_OPTIMIZE 6 | #pragma optimize("", off) 7 | #endif 8 | 9 | constexpr auto call_command_length = 5; 10 | 11 | // public enums 12 | enum aim1_fix : uint32_t { 13 | script_function__ISGLIDER = 0x0043A1F6, 14 | trade_actions_weapon_checks = 0x004072FA, 15 | setup_proper_weapon_slots_for_a_glider = 0x004D62E4, 16 | put_weapon_into_the_right_slot_after_purchase = 0x00417A6D, 17 | sell_correct_weapon = 0x004176BC, 18 | empty_light_weapon_message = 0x004067C4, 19 | empty_heavy_weapon_message = 0x0040688B, 20 | can_leave_trade_window = 0x0040C20E, 21 | }; 22 | // set different size if your injection takes more than default 5 bytes 23 | uint32_t get_injection_size(uint32_t key) { 24 | switch (key) { 25 | case aim1_fix::script_function__ISGLIDER: return 10; 26 | case aim1_fix::can_leave_trade_window: return 6; 27 | default: return call_command_length; 28 | } 29 | } 30 | 31 | #ifdef DONT_OPTIMIZE 32 | #pragma optimize("", on) 33 | #endif 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A.I.M. Tools 2 | This repository contains different tools for A.I.M. games. 3 | 4 | # List of tools 5 | 1. AIM unpaker. Unpacks any .pak archive from AIM1/AIM2/AIM:R games. 6 | 1. AIM paker. Makes archive for AIM1 game (without compression). 7 | 1. DB extractor. Converts (db|quest) databases from any AIM game into .sql file to be executed with sqlite3 DBMS. 8 | 1. MMO extractor (object extractor). Extracts all data about objects on the map from .mmo file. 9 | 1. MMP extractor. Extract texture-, alpha-, height- maps etc. 10 | 1. MMM extractor. Minimap -> BMP. 11 | 1. Models converter: AIM1/2 format -> .OBJ + .MTL. Textures are included. 12 | 1. Script to TXT convertor. 13 | 1. TXT to Script convertor. 14 | 1. Texture converter: TM -> TGA. 15 | 1. MPJ loader (dummy implementation). 16 | 1. Save loader (dummy implementation). 17 | 1. AIM1 mod maker. Makes routine actions for you. 18 | 19 | # Build instructions 20 | 1. Download and add to PATH latest SW https://software-network.org/ 21 | 2. Clone this repository 22 | 3. Open the source directory 23 | 4. Execute in console `sw generate` 24 | 5. Open generated solution file (Visual Studio solution file) 25 | 6. Build the project 26 | -------------------------------------------------------------------------------- /src/mpj_loader/mpj_loader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mpj_loader 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "mpj.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | using namespace std; 32 | 33 | int main(int argc, char *argv[]) 34 | { 35 | cl::opt p(cl::Positional, cl::desc(""), cl::Required); 36 | 37 | cl::ParseCommandLineOptions(argc, argv); 38 | 39 | mpj m; 40 | m.load(p); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /src/common/tga.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "buffer.h" 4 | 5 | #include 6 | #include 7 | 8 | #pragma pack(push, 1) 9 | 10 | // http://paulbourke.net/dataformats/tga/ 11 | struct tga 12 | { 13 | uint8_t idlength; // label length 14 | uint8_t colourmaptype = 0; 15 | uint8_t datatypecode = 2; 16 | uint16_t colourmaporigin = 0; 17 | uint16_t colourmaplength = 0; 18 | uint8_t colourmapdepth = 0; 19 | uint16_t x_origin = 0; 20 | uint16_t y_origin = 0; 21 | uint16_t width; 22 | uint16_t height; 23 | uint8_t bitsperpixel = 32; 24 | uint8_t imagedescriptor = 0x28; 25 | 26 | tga() 27 | { 28 | idlength = (uint8_t)strlen(label()); 29 | } 30 | constexpr const char *label() const 31 | { 32 | return "AIMTMConverter"; 33 | } 34 | 35 | void write(buffer &b) 36 | { 37 | b.write(idlength); 38 | b.write(colourmaptype); 39 | b.write(datatypecode); 40 | b.write(colourmaporigin); 41 | b.write(colourmaplength); 42 | b.write(colourmapdepth); 43 | b.write(x_origin); 44 | b.write(y_origin); 45 | b.write(width); 46 | b.write(height); 47 | b.write(bitsperpixel); 48 | b.write(imagedescriptor); 49 | b.write(label(), idlength); 50 | } 51 | }; 52 | 53 | #pragma pack(pop) 54 | -------------------------------------------------------------------------------- /src/aim1_mod_activator/mod_activator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mod_activator 3 | * Copyright (C) 2024 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | void activate(auto &&fn) { 24 | // files will get new timestamp automatically 25 | // our unpack_file() does not preserve timestamps 26 | unpack_file(fn, fs::current_path()); 27 | } 28 | 29 | int main(int argc, char *argv[]) 30 | { 31 | cl::opt p(cl::Positional, cl::desc(""), cl::value_desc("file"), cl::Required); 32 | 33 | cl::ParseCommandLineOptions(argc, argv); 34 | 35 | if (fs::is_regular_file(p)) { 36 | activate(p); 37 | } else { 38 | throw std::runtime_error("Bad fs object"); 39 | } 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /src/mmo_extractor2/mmo_extractor2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mmo_extractor2 (only for aim1) 3 | * Copyright (C) 2024 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "mmo2.h" 20 | #include "objects.h" 21 | 22 | #include 23 | #include 24 | 25 | void read_mmo(const path &fn) 26 | { 27 | mmo_storage2 s{fn}; 28 | s.load(); 29 | } 30 | 31 | int main(int argc, char *argv[]) 32 | { 33 | cl::opt p(cl::Positional, cl::desc("<.mmo file or directory with .mmo files>"), cl::value_desc("file or directory"), cl::Required); 34 | 35 | cl::ParseCommandLineOptions(argc, argv); 36 | 37 | if (fs::is_regular_file(p)) 38 | read_mmo(p); 39 | else if (fs::is_directory(p)) 40 | { 41 | auto files = enumerate_files_like(p, ".*\\.[Mm][Mm][Oo]", false); 42 | for (auto &file : files) 43 | { 44 | std::cerr << "processing: " << file << "\n"; 45 | read_mmo(file); 46 | } 47 | } 48 | else 49 | throw std::runtime_error("Bad fs object"); 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /src/common/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM tools 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | // MultiByteToWideChar: https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072(v=vs.85).aspx 26 | // code pages: https://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx 27 | // https://www.ibm.com/docs/en/rational-soft-arch/9.6.1?topic=overview-locales-code-pages-supported 28 | static const std::map code_pages 29 | { 30 | { "en", 0 }, 31 | { "cs", 1250 }, 32 | { "ru", 1251 }, 33 | { "de", 1252 }, 34 | { "fr", 1252 }, 35 | { "et", 1257 }, 36 | }; 37 | 38 | std::string str2utf8(const std::string &codepage_str, int cp); 39 | std::wstring str2utf16(const std::string &codepage_str, int cp); 40 | 41 | std::string str2str(const std::string &codepage_str, int cp_from, int cp_to); 42 | 43 | struct progress_bar { 44 | const size_t max_elements; 45 | const int displaylen; 46 | int displaycur{}; 47 | int i{}; 48 | 49 | void step() { 50 | auto progress_bar_pos = std::round((double)++i / max_elements * displaylen); 51 | for (int i = displaycur; i < progress_bar_pos; ++i) { 52 | std::cout << "#"; 53 | } 54 | displaycur = progress_bar_pos; 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /src/common/color.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM tools 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | typedef uint16_t color_rgb565; 24 | 25 | struct color 26 | { 27 | union 28 | { 29 | struct 30 | { 31 | uint8_t b; 32 | uint8_t g; 33 | uint8_t r; 34 | uint8_t a; 35 | }; 36 | uint8_t byte[4]; 37 | uint32_t data; 38 | }; 39 | 40 | color() {} 41 | color(uint8_t b, uint8_t g, uint8_t r, uint8_t a) 42 | : b(b), g(g), r(r), a(a) 43 | {} 44 | color(const color_rgb565 &c) 45 | { 46 | static uint8_t Table5[] = { 47 | 0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99, 107, 115, 123, 132, 48 | 140, 148, 156, 165, 173, 181, 189, 197, 206, 214, 222, 230, 239, 247, 255 }; 49 | static uint8_t Table6[] = { 50 | 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 45, 49, 53, 57, 61, 65, 69, 51 | 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 130, 134, 138, 52 | 142, 146, 150, 154, 158, 162, 166, 170, 174, 178, 182, 186, 190, 194, 198, 53 | 202, 206, 210, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 255 }; 54 | 55 | b = Table5[c & 0x1F]; 56 | g = Table6[(c >> 5) & 0x3F]; 57 | r = Table5[(c >> 11) & 0x1F]; 58 | } 59 | 60 | operator uint32_t() const 61 | { 62 | return data; 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /src/save_loader/save_loader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM save_loader 3 | * Copyright (C) 2016 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "save.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | // for aim2 1.4.30 30 | 31 | int main(int argc, char *argv[]) 32 | { 33 | cl::opt p(cl::Positional, cl::desc(""), cl::Required); 34 | 35 | cl::ParseCommandLineOptions(argc, argv); 36 | 37 | //save_changes.mech_org = "ORG_PLAYER"; 38 | 39 | //save_changes.money = 999999999.0f; 40 | //save_changes.upgrade_equ_for_player = true; 41 | 42 | auto func = [](auto &p) 43 | { 44 | buffer f(read_file(p)); 45 | save_changes.out = buffer(f.buf()); 46 | 47 | save s; 48 | s.load(f); 49 | 50 | //writeFile(p.u8string(), save_changes.out.buf()); 51 | }; 52 | 53 | if (fs::is_regular_file(p)) 54 | func(p); 55 | else if (fs::is_directory(p)) 56 | { 57 | auto files = enumerate_files_like(p, ".*", false); 58 | for (auto &f : files) 59 | { 60 | if (f.extension() != ".sav" && f.extension() != "") 61 | continue; 62 | std::cout << "processing: " << f << "\n"; 63 | func(f); 64 | } 65 | } 66 | else 67 | throw std::runtime_error("Bad fs object"); 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /src/common/common.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {name1} - {name2} 7 | 8 | 9 | 10 | 11 | 12 | {*(vector3<float>*)this} 13 | 14 | 15 | 16 | 17 | 18 | {*(vector3<float>*)this} 19 | 20 | 21 | {*(vector3<float>*)this} w={w} 22 | 23 | 24 | 25 | 26 | 27 | n_tables 28 | n_fields 29 | 30 | n_tables 31 | (db2::tab::table*)(&n_fields+1) 32 | 33 | 34 | n_fields 35 | (db2::tab::field*)((db2::tab::table*)(&n_fields+1)+n_tables) 36 | 37 | 38 | 39 | 40 | 41 | {id},{name,s} 42 | 43 | 44 | 45 | 46 | {table_id},{id},{name,s},{type} 47 | 48 | 49 | 50 | 51 | n_values 52 | 53 | n_values 54 | (db2::value*)(&n_values+1) 55 | 56 | 57 | 58 | 59 | 60 | {table_id},{name,s} 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/mod_reader/mod_reader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mod_converter 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | using namespace std; 36 | 37 | // options 38 | cl::opt all_formats("a", cl::desc("all formats")); 39 | cl::opt silent("silent", cl::desc("silent")); 40 | cl::opt printMaxPolygonBlock("m", cl::desc("print max polygon block")); 41 | 42 | void convert_model(const path &fn) 43 | { 44 | buffer b(read_file(fn)); 45 | block bl; 46 | bl.loadPayload(b); 47 | 48 | if (!b.eof()) 49 | { 50 | stringstream ss; 51 | ss << hex << b.index() << " != " << hex << b.size(); 52 | throw std::logic_error(ss.str()); 53 | } 54 | } 55 | 56 | int main(int argc, char *argv[]) 57 | { 58 | cl::opt p(cl::Positional, cl::desc(""), cl::Required); 59 | 60 | cl::ParseCommandLineOptions(argc, argv); 61 | 62 | if (fs::is_regular_file(p)) 63 | convert_model(p); 64 | else if (fs::is_directory(p)) 65 | { 66 | auto files = enumerate_files(p, false); 67 | for (auto &f : files) 68 | { 69 | if (f.has_extension()) 70 | continue; 71 | std::cout << "processing: " << f << "\n"; 72 | convert_model(f); 73 | } 74 | } 75 | else 76 | throw std::runtime_error("Bad fs object"); 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /src/unpaker/decode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #undef LOBYTE 4 | #define LOBYTE(x) (*((uint8_t *)&(x))) 5 | 6 | char decode_f2(char *input, int size, char *output) { 7 | int c; // eax@1 8 | char v4; // bl@1 9 | char *v5; // edx@1 10 | char *v6; // esi@2 11 | char *v7; // edx@4 12 | int c_1; // edi@4 13 | int c_2; // edi@7 14 | char *v10; // ecx@8 15 | int c_3; // edi@8 16 | 17 | LOBYTE(c) = size; 18 | v4 = *input; 19 | v5 = input + 1; 20 | if (input + 1 < &input[size]) { 21 | v6 = output; 22 | do { 23 | LOBYTE(c) = *v5; 24 | if (*v5 == v4) { 25 | c = (uint8_t)v5[1]; 26 | v7 = v5 + 1; 27 | c_1 = (uint8_t)v7[1]; 28 | v5 = v7 + 2; 29 | if (c != 255 || c_1 != 255) { 30 | c_2 = ((c & 0xF) << 8) + c_1; 31 | c = (c >> 4) + 4; 32 | v10 = &v6[-c_2]; 33 | c_3 = c; 34 | do { 35 | LOBYTE(c) = *v10; 36 | *v6++ = *v10++; 37 | --c_3; 38 | } while (c_3); 39 | } else { 40 | *v6++ = v4; 41 | } 42 | } else { 43 | *v6++ = c; 44 | ++v5; 45 | } 46 | } while (v5 < &input[size]); 47 | } 48 | return c; 49 | } 50 | 51 | template 52 | auto decode_rle(const T *input, int size, T *output) { 53 | if (size < 2) { 54 | return (uint8_t *)output; 55 | } 56 | const auto base = input; 57 | const auto rle_indicator = (uint8_t)*input++; 58 | while (1) { 59 | auto c = *input++; 60 | //msvc bad warn. workaround vvvvvvvv 61 | if ((sizeof(T) == 1 ? c : ((uint16_t)c >> 8)) != rle_indicator) { 62 | *output++ = c; 63 | } else { 64 | uint32_t count = sizeof(T) == 1 ? *input++ : (c & 0xFF); 65 | if (count == 0xFF) { 66 | *output++ = sizeof(T) == 1 ? rle_indicator : *input++; 67 | } else { 68 | for (int i = 0; i < count + 3; i++) { 69 | *output++ = *input; 70 | } 71 | ++input; 72 | } 73 | } 74 | if ((uint8_t *)input >= (uint8_t *)base + size) { 75 | return (uint8_t *)output; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/mmm_extractor/mmm_extractor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mmm_extractor 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | using namespace std; 33 | 34 | struct mmm 35 | { 36 | uint32_t unk1; 37 | uint32_t unk2; 38 | dxt5 data; 39 | 40 | void load(const buffer &b) 41 | { 42 | READ(b, unk1); 43 | READ(b, unk2); 44 | data.load(b); 45 | } 46 | }; 47 | 48 | mmm read_mmm(const path &fn) 49 | { 50 | buffer b(read_file(fn)); 51 | mmm m; 52 | m.load(b); 53 | 54 | if (!b.eof()) 55 | { 56 | std::stringstream ss; 57 | ss << std::hex << b.index() << " != " << hex << b.size(); 58 | throw std::logic_error(ss.str()); 59 | } 60 | return m; 61 | } 62 | 63 | void process_mmm(const path &fn) 64 | { 65 | auto m = read_mmm(fn); 66 | write_mat_bmp(path(fn) += ".bmp", m.data.unpack_mmm()); 67 | } 68 | 69 | int main(int argc, char *argv[]) 70 | { 71 | cl::opt p(cl::Positional, cl::desc(""), cl::Required); 72 | 73 | cl::ParseCommandLineOptions(argc, argv); 74 | 75 | if (fs::is_regular_file(p)) 76 | process_mmm(p); 77 | else if (fs::is_directory(p)) 78 | { 79 | auto files = enumerate_files_like(p, ".*\\.mmm", false); 80 | for (auto &f : files) 81 | { 82 | std::cout << "processing: " << f << "\n"; 83 | process_mmm(f); 84 | } 85 | } 86 | else 87 | throw std::runtime_error("Bad fs object"); 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /src/common/common.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM tools 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "common.h" 20 | 21 | #include 22 | 23 | #ifdef _WIN32 24 | #include 25 | #endif 26 | 27 | std::string str2utf8(const std::string &codepage_str, int cp) 28 | { 29 | #ifdef _WIN32 30 | auto utf16_str = str2utf16(codepage_str, cp); 31 | int utf8_size = WideCharToMultiByte(CP_UTF8, 0, utf16_str.c_str(), 32 | utf16_str.length(), nullptr, 0, 33 | nullptr, nullptr); 34 | std::string utf8_str(utf8_size, '\0'); 35 | WideCharToMultiByte(CP_UTF8, 0, utf16_str.c_str(), 36 | utf16_str.length(), &utf8_str[0], utf8_size, 37 | nullptr, nullptr); 38 | return utf8_str; 39 | #else 40 | SW_UNIMPLEMENTED; 41 | #endif 42 | } 43 | 44 | std::wstring str2utf16(const std::string &codepage_str, int cp) 45 | { 46 | #ifdef _WIN32 47 | int size; 48 | std::wstring utf16_str; 49 | 50 | size = MultiByteToWideChar(cp, MB_PRECOMPOSED, codepage_str.c_str(), 51 | codepage_str.length(), nullptr, 0); 52 | utf16_str = std::wstring(size, '\0'); 53 | MultiByteToWideChar(cp, MB_PRECOMPOSED, codepage_str.c_str(), 54 | codepage_str.length(), &utf16_str[0], size); 55 | 56 | return utf16_str; 57 | #else 58 | SW_UNIMPLEMENTED; 59 | #endif 60 | } 61 | 62 | std::string str2str(const std::string &codepage_str, int cp_from, int cp_to) { 63 | #ifdef _WIN32 64 | auto utf16_str = str2utf16(codepage_str, cp_from); 65 | int dest_size = WideCharToMultiByte(cp_to, 0, utf16_str.c_str(), utf16_str.length(), nullptr, 0, nullptr, nullptr); 66 | std::string dest_str(dest_size, '\0'); 67 | WideCharToMultiByte(cp_to, 0, utf16_str.c_str(), utf16_str.length(), &dest_str[0], dest_size, nullptr, nullptr); 68 | return dest_str; 69 | #else 70 | SW_UNIMPLEMENTED; 71 | #endif 72 | } 73 | -------------------------------------------------------------------------------- /src/db_extractor2/db_extractor2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM db_extractor 3 | * Copyright (C) 2024 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "db2.h" 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | void copy_dbs() { 33 | auto from_to = [](auto &&from, const std::string &to) { 34 | path base = from; 35 | base /= "data"; 36 | const path out = "d:/dev/ue/AIM/translations/aim1/"; 37 | db2 db{base / "quest"}; 38 | auto f = db.create(); 39 | f.open(); 40 | auto m = f.to_map(code_pages.at(to.substr(0,2))); 41 | write_file(path{out} += "/quest_"s + to + ".json", m.to_json().dump(1)); 42 | }; 43 | from_to("h:/Games/AIM", "ru_RU"); 44 | for (auto &&lang : { 45 | "cs_CZ", 46 | "de_DE", 47 | "en_US", 48 | "et_EE", 49 | "fr_FR", 50 | }) { 51 | from_to("h:/Games/AIM/1/dbs/"s + lang, lang); 52 | } 53 | } 54 | 55 | int main(int argc, char *argv[]) { 56 | //copy_dbs(); 57 | 58 | cl::opt db_fn(cl::Positional, cl::desc(""), cl::Required); 59 | cl::opt codepage(cl::Positional, cl::desc(""), cl::Required); 60 | 61 | cl::ParseCommandLineOptions(argc, argv); 62 | 63 | path fn = db_fn; 64 | fn = fs::absolute(fn); 65 | if (fn.extension() != ".json") { 66 | db2 db{fn}; 67 | auto f = db.create(); 68 | f.open(); 69 | auto m = f.to_map(codepage); 70 | write_file(path{fn} += ".json", m.to_json().dump(1)); 71 | } else { 72 | db2::files_type::db2_internal db; 73 | db.load_from_json(fn); 74 | db.save(fn.parent_path() / fn.stem(), codepage); 75 | } 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /src/mmp_extractor/mmp_extractor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mmp_extractor 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "mmp.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | using namespace std; 33 | 34 | int main(int argc, char *argv[]) 35 | { 36 | cl::opt p(cl::Positional, cl::desc(""), cl::Required); 37 | cl::opt texture_ids(cl::Positional, cl::desc("")); 38 | cl::opt split_colormap("split_colormap", cl::desc("split colormap into separate images")); 39 | cl::opt tex_al("texture_alphamaps", cl::desc("write texture alpha maps")); 40 | 41 | cl::ParseCommandLineOptions(argc, argv); 42 | 43 | auto func = [&texture_ids, &split_colormap, &tex_al](auto &p) 44 | { 45 | mmp m; 46 | if (!texture_ids.empty()) 47 | m.loadTextureNames(texture_ids); 48 | m.load(p); 49 | m.process(); 50 | m.writeFileInfo(); 51 | m.writeTexturesList(); 52 | m.writeHeightMap(); 53 | //m.writeHeightMapSegmented(); 54 | m.writeTextureMap(); 55 | if (tex_al) 56 | m.writeTextureAlphaMaps(); 57 | m.writeTextureMapColored(); 58 | m.writeColorMap(); 59 | m.writeShadowMap(); 60 | m.writeNormalMap(); 61 | if (split_colormap) 62 | m.writeSplitColormap(); 63 | }; 64 | 65 | if (fs::is_regular_file(p)) 66 | func(p); 67 | else if (fs::is_directory(p)) 68 | { 69 | auto files = enumerate_files_like(p, ".*\\.mmp", false); 70 | for (auto &f : files) 71 | { 72 | std::cout << "processing: " << f << "\n"; 73 | func(f); 74 | } 75 | } 76 | else 77 | throw std::runtime_error("Bad fs object"); 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /src/common/db.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM db_extractor 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | enum class FieldType : uint32_t 34 | { 35 | String, 36 | Integer, 37 | Float, 38 | }; 39 | std::string getSqlType(FieldType type); 40 | 41 | struct table 42 | { 43 | uint32_t id; 44 | std::string name; 45 | uint32_t unk4; 46 | 47 | void load(const buffer &b); 48 | }; 49 | 50 | struct field 51 | { 52 | uint32_t table_id = -1; 53 | uint32_t id; 54 | std::string name; 55 | FieldType type; 56 | 57 | void load(const buffer &b); 58 | }; 59 | 60 | struct tab 61 | { 62 | uint32_t number_of_tables; 63 | uint32_t number_of_fields; 64 | 65 | std::map tables; 66 | std::map fields; 67 | 68 | void load(const buffer &b); 69 | }; 70 | 71 | struct field_value 72 | { 73 | uint32_t field_id; 74 | uint32_t size; 75 | 76 | int i = 0; 77 | float f = 0.f; 78 | std::string s; 79 | }; 80 | 81 | struct value 82 | { 83 | uint32_t table_id; 84 | std::string name; 85 | uint32_t offset; 86 | uint32_t data_size; 87 | std::vector fields; 88 | 89 | void load_index(const buffer &b); 90 | void load_fields(const tab &tab, buffer &b); 91 | }; 92 | 93 | namespace polygon4::tools::db 94 | { 95 | using value = std::variant; 96 | using record = std::map; 97 | using table = std::map; 98 | using processed_db = std::map; 99 | }; 100 | 101 | struct db 102 | { 103 | uint32_t number_of_values = 0; 104 | 105 | tab t; 106 | std::vector values; 107 | 108 | void load(const buffer &b); 109 | void open(const path &basename); 110 | polygon4::tools::db::processed_db process(int cp) const; 111 | }; 112 | -------------------------------------------------------------------------------- /src/txt2script/txt2script.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM script2txt2 (simpler version) 3 | * Copyright (C) 2024 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | int main(int argc, char *argv[]) { 29 | cl::opt p(cl::Positional, cl::desc(""), cl::Required); 30 | 31 | cl::ParseCommandLineOptions(argc, argv); 32 | 33 | auto func = [](path filename) { 34 | uint32_t sz{}; 35 | auto lines = read_lines(filename); 36 | for (auto &&line : lines) { 37 | sz += line.size() + 1; 38 | } 39 | uint32_t maxlines = lines.size(); 40 | 41 | uint32_t unk{}; 42 | auto fsz = sizeof(script) + sz + maxlines * sizeof(sz) + sizeof(unk); 43 | 44 | primitives::templates2::mmap_file f{filename.stem(), primitives::templates2::mmap_file::rw{}}; 45 | f.alloc_raw(fsz); 46 | stream s{f}; 47 | script scr{}; 48 | scr.nlines = lines.size(); 49 | scr.raw_text_size = sz; 50 | scr.file_size = fsz - sizeof(scr.file_size); 51 | s = scr; 52 | 53 | for (auto &&line : lines) { 54 | boost::to_upper(line); // can be bad? 55 | boost::trim(line); 56 | //boost::replace_all(line, " ", ""); // causes crashes! 57 | memcpy((char *)s.p, line.c_str(), line.size() + 1); 58 | s.skip(line.size() + 1); 59 | } 60 | 61 | s = maxlines; 62 | uint32_t offset{}; 63 | for (auto &&line : lines) { 64 | s = offset; 65 | offset += line.size() + 1; 66 | } 67 | s = unk; 68 | }; 69 | 70 | if (fs::is_regular_file(p)) { 71 | func(p.string()); 72 | } else if (fs::is_directory(p)) { 73 | auto files = enumerate_files_like(p, ".*\\.scr.txt", false); 74 | auto files2 = enumerate_files_like(p, ".*\\.QST.txt", false); 75 | files.insert(files2.begin(), files2.end()); 76 | for (auto &f : files) { 77 | std::cout << "processing: " << f << "\n"; 78 | func(f.string()); 79 | } 80 | } else { 81 | throw std::runtime_error("Bad fs object"); 82 | } 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /src/common/objects.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM obj_extractor 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "objects.h" 20 | 21 | #include 22 | 23 | Segment *Segment::create_segment(const buffer &b) 24 | { 25 | ObjectType segment_type; 26 | READ(b, segment_type); 27 | 28 | Segment *segment = 0; 29 | switch (segment_type) 30 | { 31 | case ObjectType::ROAD: 32 | segment = new SegmentObjects; 33 | break; 34 | case ObjectType::BUILDING: 35 | segment = new SegmentObjects; 36 | break; 37 | case ObjectType::TREE: 38 | segment = new SegmentObjects; 39 | break; 40 | case ObjectType::STONE: 41 | segment = new SegmentObjects; 42 | break; 43 | case ObjectType::HELPER: 44 | segment = new SegmentObjects; 45 | break; 46 | case ObjectType::IMAGE: 47 | segment = new SegmentObjects; 48 | break; 49 | case ObjectType::LAMP: 50 | segment = new SegmentObjects; 51 | break; 52 | case ObjectType::SOUND: 53 | segment = new SegmentObjects; 54 | break; 55 | case ObjectType::ANOMALY: 56 | segment = new SegmentObjects; 57 | break; 58 | case ObjectType::TOWER: 59 | segment = new SegmentObjects; 60 | break; 61 | case ObjectType::BOUNDARY: 62 | segment = new SegmentObjects; 63 | break; 64 | case ObjectType::SOUND_ZONE: 65 | segment = new SegmentObjects; 66 | break; 67 | case ObjectType::unk0: 68 | segment = new SegmentObjects; 69 | break; 70 | case ObjectType::TANK: 71 | segment = new SegmentObjects; 72 | break; 73 | default: 74 | SW_UNIMPLEMENTED; 75 | } 76 | if (segment) 77 | { 78 | segment->segment_type = segment_type; 79 | READ(b, segment->segment_len); 80 | READ(b, segment->n_objects); 81 | } 82 | return segment; 83 | } 84 | 85 | void Objects::load(const buffer &b) 86 | { 87 | uint32_t n_segments = 0; 88 | READ(b, n_segments); 89 | 90 | for (uint32_t s = 0; s < n_segments; s++) 91 | { 92 | auto seg = Segment::create_segment(b); 93 | if (!seg) 94 | break; 95 | seg->load(buffer(b, seg->segment_len)); 96 | segments.push_back(seg); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/name_generator/name_generator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM name_generator 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | class NameGenerator 24 | { 25 | static const int letmin = 2; 26 | static const int letmax = 3; 27 | 28 | public: 29 | NameGenerator() 30 | : rng(std::random_device()()) 31 | { 32 | } 33 | 34 | std::string generate(int sylmin = 2, int sylmax = 3) 35 | { 36 | static const std::string consonants = "bcdfghjklmnpqrstvwxz"; 37 | static const std::string vowels = "aeiouy"; 38 | using dist = std::uniform_int_distribution<>; 39 | dist c_distr(0, consonants.size() - 1); 40 | dist v_distr(0, vowels.size() - 1); 41 | dist cv_distr(0, 1); 42 | dist let_distr(letmin, letmax); 43 | auto n_syllables = dist(sylmin, sylmax)(rng); 44 | std::string name; 45 | for (int s = 0; s < n_syllables; s++) 46 | { 47 | auto n_letters = let_distr(rng); 48 | int n_consonants = 0, n_vowels = 0; 49 | std::string syllable; 50 | for (int l = 0; l < n_letters; l++) 51 | { 52 | auto vowel = cv_distr(rng); 53 | syllable += vowel ? vowels[v_distr(rng)] : consonants[c_distr(rng)]; 54 | n_consonants += !vowel; 55 | n_vowels += vowel; 56 | } 57 | if (n_consonants == n_letters) 58 | { 59 | auto pos = dist(0, n_letters - 1)(rng); 60 | syllable[pos] = vowels[v_distr(rng)]; 61 | } 62 | else if (n_vowels == n_letters) 63 | { 64 | auto pos = dist(0, n_letters - 1)(rng); 65 | syllable[pos] = consonants[c_distr(rng)]; 66 | } 67 | name += syllable; 68 | } 69 | return check_name(name) ? name : generate(sylmin, sylmax); 70 | } 71 | 72 | private: 73 | std::mt19937 rng; 74 | 75 | bool check_name(const std::string &name) const 76 | { 77 | auto banned = { "hu", "piz", "eb", "bl", "mu" }; 78 | for (auto &b : banned) 79 | if (name.find(b) != std::string::npos) 80 | return false; 81 | return true; 82 | } 83 | }; 84 | 85 | int main(int argc, char *argv[]) 86 | { 87 | int n = argc == 1 ? 1 : std::stoi(argv[1]); 88 | NameGenerator ng; 89 | for (int i = 0; i < n; i++) 90 | std::cout << ng.generate() << (i != n-1 ? "\n" : ""); 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /src/tm_converter/tm_converter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM tm_converter 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | using namespace std; 37 | 38 | void convert_simple(buffer &dst, const buffer &src, int width, int height) 39 | { 40 | int size = width * height * 2; 41 | for (int i = 0; i < size; i++) 42 | { 43 | uint8_t c; 44 | READ(src, c); 45 | uint8_t lo = c & 0x0F; 46 | uint8_t hi = (c & 0xF0) >> 4; 47 | dst.write(uint8_t((lo << 4) | lo)); 48 | dst.write(uint8_t((hi << 4) | hi)); 49 | } 50 | } 51 | 52 | void convert(const path &fn) 53 | { 54 | int width, height; 55 | int dxt5_flag = 0; 56 | 57 | buffer src(read_file(fn)); 58 | READ(src, width); 59 | READ(src, height); 60 | src.seek(0x10); 61 | src._read(&dxt5_flag, 1); 62 | src.seek(0x4C); 63 | 64 | auto s = path(fn) += ".bmp"; 65 | mat m(width, height); 66 | if (dxt5_flag) 67 | { 68 | dxt5 d; 69 | d.width = width; 70 | d.height = height; 71 | d.load_blocks(src); 72 | m = d.unpack_tm(); 73 | } 74 | else 75 | { 76 | buffer dst2; 77 | convert_simple(dst2, src, width, height); 78 | dst2.reset(); 79 | memcpy(&m(0,0), dst2.getPtr(), dst2.size()); 80 | m = m.flip(); // flip tga (normal rows order) to bmp (inverse rows order) 81 | } 82 | write_mat_bmp(s, m); 83 | } 84 | 85 | int main(int argc, char *argv[]) 86 | { 87 | cl::list list(cl::Positional, cl::desc(""), cl::Required, cl::OneOrMore); 88 | 89 | cl::ParseCommandLineOptions(argc, argv); 90 | 91 | for (auto &&p : list) 92 | if (fs::is_regular_file(p)) 93 | convert(p); 94 | else if (fs::is_directory(p)) 95 | { 96 | auto files = enumerate_files_like(p, ".*\\.TM", false); 97 | for (auto &f : files) 98 | { 99 | std::cout << "processing: " << to_printable_string(f) << "\n"; 100 | convert(f); 101 | } 102 | } 103 | else 104 | throw std::runtime_error("Bad fs object"); 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /src/common/types.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mmp_extractor 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "types.h" 20 | 21 | #include 22 | 23 | void weather::load(const buffer &b) 24 | { 25 | READ_STRING(b, name); 26 | READ_STRING(b, unk0); 27 | READ(b, unk1); 28 | READ(b, smoke_1); 29 | READ(b, smoke_3); 30 | READ(b, smokeType); 31 | switch (smokeType) 32 | { 33 | case SmokeType::biexp: 34 | READ(b, unk2); 35 | b.skip(4); 36 | break; 37 | default: 38 | READ(b, unk2); 39 | break; 40 | } 41 | READ_STRING(b, cloud_layer1); 42 | READ_STRING(b, cloud_layer2); 43 | READ(b, cloud_layer1_speed); 44 | READ(b, cloud_layer2_speed); 45 | READ(b, cloud_layer1_direction); 46 | READ(b, cloud_layer2_direction); 47 | READ_STRING(b, sun); 48 | READ(b, general_color); 49 | READ(b, sun_color); 50 | READ(b, moon_color); 51 | READ_STRING(b, moon); 52 | READ(b, probability); 53 | READ_STRING(b, day_night_gradient_name); 54 | READ_STRING(b, dawn_dusk_gradient_name); 55 | READ(b, dawn_dusk_color); 56 | READ(b, effects); 57 | READ(b, smoke_2); 58 | READ(b, smoke_4); 59 | READ(b, slider_3); 60 | READ(b, slider_1); 61 | switch (smokeType) 62 | { 63 | case SmokeType::biexp: 64 | { 65 | float unk81[10]; 66 | READ(b, unk81); 67 | memcpy(unk8, unk81, sizeof(f32) * 10); 68 | } 69 | break; 70 | default: 71 | READ(b, unk8); 72 | break; 73 | } 74 | } 75 | 76 | void water::load(const buffer &b) 77 | { 78 | READ(b, unk0); 79 | READ_STRING(b, name1); 80 | READ(b, unk1); 81 | READ(b, unk2); 82 | READ(b, unk3); 83 | READ(b, unk4); 84 | READ_STRING(b, name2); 85 | READ(b, unk5); 86 | } 87 | 88 | void water_group::load(const buffer &b) 89 | { 90 | while (!b.eof()) 91 | { 92 | water w; 93 | w.load(b); 94 | segments.push_back(w); 95 | } 96 | } 97 | 98 | void Organization::load(const buffer &b) 99 | { 100 | READ(b, unk0); 101 | READ_STRING(b, name); 102 | READ(b, count); 103 | READ(b, trade_war); 104 | READ(b, defence_attack); 105 | 106 | // incorrect? 107 | READ(b, configs[1].count_in_group); 108 | READ(b, configs[2].count_in_group); 109 | READ(b, configs[0].count_in_group); 110 | 111 | READ(b, average_rating); 112 | READ(b, is_free); 113 | READ(b, is_foreign); 114 | READ(b, unk1); 115 | for (auto &c : configs) 116 | c.load(b); 117 | } 118 | -------------------------------------------------------------------------------- /src/rgb_converter/rgb_converter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM rgb_converter (0.03 demo textures files) 3 | * Copyright (C) 2024 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "mmap.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #pragma pack(push, 1) 34 | struct rgb { 35 | struct header { 36 | uint32_t magic; 37 | uint32_t width; 38 | uint32_t height; 39 | uint32_t type; 40 | uint32_t unk2; 41 | uint32_t unk3; 42 | }; 43 | 44 | static void convert_model(const path &fn) { 45 | primitives::templates2::mmap_file f{fn}; 46 | stream s{f}; 47 | 48 | header h = s; 49 | // maybe take something from tm_converter? 50 | if (memcmp(&h.magic, "SGTC", sizeof(h.magic) == 0)) { 51 | if (h.type == 1) { 52 | auto bytes = s.span(h.width * h.height); 53 | } 54 | if (h.type == 6) { 55 | auto bytes = s.span(h.width * h.height); 56 | } 57 | } 58 | if (memcmp(&h.magic, "SGTF", sizeof(h.magic) == 0)) { 59 | if (h.type == 2) { 60 | auto bytes = s.span(h.width * h.height); 61 | } 62 | } 63 | 64 | int a = 5; 65 | a++; 66 | } 67 | }; 68 | #pragma pack(pop) 69 | 70 | int main(int argc, char *argv[]) 71 | { 72 | cl::opt p(cl::Positional, cl::desc("<.rgb file or dir>"), cl::Required); 73 | 74 | cl::ParseCommandLineOptions(argc, argv); 75 | 76 | if (fs::is_regular_file(p)) { 77 | rgb::convert_model(p); 78 | } else if (fs::is_directory(p)) { 79 | auto files = enumerate_files(p, false); 80 | for (auto &f : FilesSorted(files.begin(), files.end())) 81 | { 82 | if (f.extension() != ".rgb") { 83 | continue; 84 | } 85 | std::cout << "processing: " << f << "\n"; 86 | try 87 | { 88 | rgb::convert_model(f); 89 | } 90 | catch (std::exception &e) 91 | { 92 | std::cout << "error: " << e.what() << "\n"; 93 | } 94 | } 95 | } 96 | else 97 | throw std::runtime_error("No such file or directory: " + to_printable_string(normalize_path(p))); 98 | 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /src/common/objects.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM obj_extractor 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "buffer.h" 28 | #include "types.h" 29 | 30 | enum class ObjectType : uint32_t 31 | { 32 | TEXTURE = 1, 33 | //MODEL, 34 | //SURFACE, 35 | STONE = 3, 36 | TREE = 4, 37 | 38 | //GLIDER, 39 | HELPER = 7, 40 | ROAD = 8, 41 | //WEAPON, 42 | //CONFIG, 43 | 44 | //SHELL, // buildings 45 | BUILDING = 11, 46 | IMAGE, 47 | LAMP = 13, 48 | //EXPLOSION, // road lights 49 | //EQUIPMENT, 50 | //ORGANIZATION, 51 | 52 | //COVERING = 18, 53 | SOUND = 19, 54 | //GOODS, // anomaly? 55 | ANOMALY = 20, // radiation? 56 | 57 | unk0 = 21, // anomaly? 58 | TOWER = 22, 59 | BOUNDARY = 23, 60 | SOUND_ZONE = 24, 61 | 62 | TANK = 27, 63 | }; 64 | 65 | struct Segment 66 | { 67 | ObjectType segment_type; 68 | uint32_t segment_len = 0; 69 | uint32_t n_objects = 0; 70 | 71 | virtual ~Segment(){} 72 | static Segment *create_segment(const buffer &b); 73 | virtual void load(const buffer &b) = 0; 74 | }; 75 | 76 | template 77 | struct SegmentObjects : public Segment 78 | { 79 | std::vector objects; 80 | 81 | virtual void load(const buffer &b) 82 | { 83 | for (uint32_t i = 0; i < n_objects; i++) 84 | { 85 | T o; 86 | o.load(b); 87 | objects.push_back(o); 88 | } 89 | } 90 | }; 91 | 92 | struct Common 93 | { 94 | // m_rotate_z[2].z - model scale on the map 95 | vector4 m_rotate_z[3]; 96 | vector4 position; 97 | 98 | void load(const buffer &b) 99 | { 100 | READ(b, m_rotate_z); 101 | READ(b, position); 102 | } 103 | }; 104 | 105 | struct MapObject : public Common 106 | { 107 | std::string name1; 108 | std::string name2; 109 | 110 | void load(const buffer &b) 111 | { 112 | Common::load(b); 113 | 114 | READ_STRING(b, name1); 115 | READ_STRING(b, name2); 116 | } 117 | }; 118 | 119 | struct MapObjectWithArray : public MapObject 120 | { 121 | uint32_t len = 0; 122 | std::vector unk0; 123 | 124 | void load(const buffer &b) 125 | { 126 | MapObject::load(b); 127 | 128 | READ(b, len); 129 | unk0.resize(len); 130 | for (uint32_t i = 0; i < len; i++) 131 | READ(b, unk0[i]); 132 | } 133 | }; 134 | 135 | struct Sound : public Common 136 | { 137 | uint32_t unk1[11]; 138 | char name1[0x14]; 139 | 140 | void load(const buffer &b) 141 | { 142 | Common::load(b); 143 | 144 | READ(b, unk1); 145 | READ(b, name1); 146 | } 147 | }; 148 | 149 | struct Road : public MapObjectWithArray {}; 150 | struct Boundary : public MapObjectWithArray {}; 151 | 152 | #define KNOWN_OBJECT(name) \ 153 | struct name : public MapObject {} 154 | 155 | KNOWN_OBJECT(Stone); 156 | KNOWN_OBJECT(Helper); 157 | KNOWN_OBJECT(Building); 158 | KNOWN_OBJECT(Tree); 159 | KNOWN_OBJECT(Lamp); 160 | KNOWN_OBJECT(Image); 161 | KNOWN_OBJECT(Anomaly); 162 | KNOWN_OBJECT(Tower); 163 | KNOWN_OBJECT(SoundZone); 164 | KNOWN_OBJECT(Tank); 165 | 166 | #define UNKNOWN_OBJECT(name) \ 167 | struct name : public MapObject { void load(const buffer &b){ int pos = b.index(); assert(false); } } 168 | 169 | UNKNOWN_OBJECT(unk0); 170 | 171 | struct Objects 172 | { 173 | std::vector segments; 174 | 175 | void load(const buffer &b); 176 | }; 177 | -------------------------------------------------------------------------------- /src/common/buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM tools 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #define READ(b, var) b.read(var) 27 | #define READ_N(b, var, sz) b.read(var, sz) 28 | 29 | #define READ_STRING(b, var) var = b.read_string() 30 | #define READ_STRING_N(b, var, sz) var = b.read_string(sz) 31 | 32 | #define READ_WSTRING(b, var) var = b.read_wstring() 33 | #define READ_WSTRING_N(b, var, sz) var = b.read_wstring(sz) 34 | 35 | #define READ_PASCAL_STRING(b, var) var = b.read_pascal_string() 36 | 37 | #define WRITE(b, var) b.write(&var) 38 | 39 | std::string version(); 40 | void writeFile(const std::string &fn, const std::vector &data); 41 | 42 | class buffer 43 | { 44 | public: 45 | buffer(); 46 | buffer(size_t size); 47 | buffer(const std::string &s); 48 | buffer(const std::vector &buf, uint32_t data_offset = 0); 49 | buffer(const buffer &rhs, uint32_t size); 50 | buffer(const buffer &rhs, uint32_t size, uint32_t offset); 51 | 52 | template 53 | uint32_t read(T &dst, uint32_t size = 1) const 54 | { 55 | return _read((void *)&dst, size * sizeof(T), 0); 56 | } 57 | std::string read_string(uint32_t blocksize = 0x20) const; 58 | std::wstring read_wstring(uint32_t blocksize = 0x20) const; 59 | template 60 | uint32_t readfrom(T *dst, uint32_t size, uint32_t offset) const 61 | { 62 | return _read(dst, size * sizeof(T), offset); 63 | } 64 | 65 | template 66 | uint32_t write(const T &src) 67 | { 68 | return _write(&src, sizeof(T)); 69 | } 70 | template 71 | uint32_t write(const T *src, uint32_t size) 72 | { 73 | return _write(src, size * sizeof(T)); 74 | } 75 | template 76 | uint32_t write(const T *src) 77 | { 78 | return _write(src, sizeof(T)); 79 | } 80 | uint32_t write(const std::string &s) 81 | { 82 | auto sz = s.size() + 1; 83 | _write(s.c_str(), sz); 84 | skip(0x20 - sz); 85 | return 0x20; 86 | } 87 | 88 | template 89 | void read_vector(std::vector &v, int n) const 90 | { 91 | v.clear(); 92 | v.reserve(n); 93 | for (int i = 0; i < n; i++) 94 | { 95 | T t; 96 | t.load(*this); 97 | v.push_back(t); 98 | } 99 | } 100 | 101 | template 102 | void read_vector(std::vector &v) const 103 | { 104 | SizeType n = 0; 105 | read(n); 106 | read_vector(v, n); 107 | } 108 | 109 | std::string read_pascal_string() const 110 | { 111 | uint32_t n = 0; 112 | read(n); 113 | std::string s(n, 0); 114 | _read(s.data(), n); 115 | return s; 116 | } 117 | 118 | void seek(uint32_t size) const; // setpos 119 | void skip(int n) const; 120 | uint32_t end() const { return end_; } 121 | bool eof() const; 122 | bool check_pos(int index) const; 123 | void reset() const; 124 | 125 | uint32_t index() const; 126 | uint32_t size() const; 127 | const std::vector &buf() const; 128 | 129 | const uint8_t *getPtr() const { return ptr; } 130 | 131 | uint32_t _read(void *dst, uint32_t size, uint32_t offset = 0) const; 132 | uint32_t _write(const void *src, uint32_t size); 133 | 134 | private: 135 | std::shared_ptr> buf_; 136 | mutable uint32_t index_ = 0; 137 | mutable uint8_t *ptr = 0; 138 | mutable uint32_t data_offset = 0; 139 | mutable uint32_t size_ = 0; 140 | uint32_t end_; 141 | }; 142 | -------------------------------------------------------------------------------- /src/script2txt/script2txt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM script2txt2 (simpler version) 3 | * Copyright (C) 2024 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | int main(int argc, char *argv[]) { 29 | cl::opt p(cl::Positional, cl::desc(""), cl::Required); 30 | 31 | cl::ParseCommandLineOptions(argc, argv); 32 | 33 | auto func = [](auto filename) { 34 | primitives::templates2::mmap_file f{filename}; 35 | stream s{f}; 36 | script scr = s; 37 | std::vector lines; 38 | int sz{}; 39 | for (int i = 0; i < scr.nlines; ++i) { 40 | sz += lines.emplace_back((const char *)f.p + sizeof(script) + sz).size() + 1; 41 | } 42 | 43 | // write script 44 | { 45 | filename += ".txt"; 46 | if (std::ofstream ofile(filename, std::ios::binary); ofile) { 47 | std::string indent, space = " "s; 48 | auto inc = [&]() { 49 | indent += space; 50 | }; 51 | auto dec = [&]() { 52 | if (!indent.empty()) { 53 | indent.resize(indent.size() - space.size()); 54 | return true; 55 | } 56 | return false; 57 | }; 58 | int procs{}; 59 | bool prev_newline{}; 60 | for (auto &&l : lines) { 61 | auto else_ = l == "ELSE"sv; 62 | auto proc = l.starts_with("PROC"sv); 63 | auto end = l == "END"sv; 64 | auto lbrace = l == "{"sv; 65 | auto rbrace = l == "}"sv; 66 | 67 | if (else_ && prev_newline) { 68 | ofile.seekp(-1, std::ios::cur); 69 | } 70 | prev_newline = false; 71 | 72 | if (rbrace) { 73 | if (!dec()) { 74 | ofile << "// script2txt2 comment: unbalanced!\n"; 75 | } 76 | } 77 | if (end && procs) { 78 | if (!dec()) { 79 | ofile << "// script2txt2 comment: unbalanced!\n"; 80 | } 81 | } 82 | 83 | ofile << indent << l << "\n"; 84 | 85 | if ((end || rbrace) && indent.empty()) { 86 | ofile << "\n"; 87 | prev_newline = true; 88 | } 89 | if (end && procs) { 90 | procs = 0; 91 | } 92 | if (lbrace || proc) { 93 | indent += space; 94 | } 95 | if (proc) { 96 | procs = 1; 97 | } 98 | } 99 | } 100 | } 101 | }; 102 | 103 | if (fs::is_regular_file(p)) { 104 | func(p.string()); 105 | } else if (fs::is_directory(p)) { 106 | auto files = enumerate_files_like(p, ".*\\.scr", false); 107 | auto files2 = enumerate_files_like(p, ".*\\.SCR", false); 108 | files.insert(files2.begin(), files2.end()); 109 | files2 = enumerate_files_like(p, ".*\\.QST", false); 110 | files.insert(files2.begin(), files2.end()); 111 | for (auto &f : files) { 112 | std::cout << "processing: " << f << "\n"; 113 | func(f.string()); 114 | } 115 | } else { 116 | throw std::runtime_error("Bad fs object"); 117 | } 118 | 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /src/db_extractor/db_extractor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM db_extractor 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "db.h" 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | void create_sql(path p, const polygon4::tools::db::processed_db &db) 31 | { 32 | std::ofstream ofile(p += ".sql"); 33 | if (!ofile) 34 | return; 35 | 36 | std::string master_table_name = "DB_TABLE_LIST"; 37 | const std::string id = "ID"; 38 | const std::string row_type = "TEXT_ID"; 39 | 40 | // db master table 41 | ofile << "drop table if exists " << master_table_name << ";\n"; 42 | ofile << "create table \"" << master_table_name << "\"\n"; 43 | ofile << "(\n"; 44 | ofile << " \"" + id + "\" INTEGER,\n"; 45 | ofile << " \"" + row_type + "\" TEXT\n"; 46 | ofile << ");\n"; 47 | ofile << "\n"; 48 | 49 | for (auto &[name,table] : db) 50 | { 51 | static int idx = 1; 52 | ofile << "insert into \"" << master_table_name << "\" values ("; 53 | ofile << "'" << idx++ << "', "; 54 | ofile << "'" << name << "'"; 55 | ofile << ");\n"; 56 | } 57 | ofile << "\n"; 58 | 59 | // db tables 60 | for (auto &[name, t] : db) 61 | { 62 | ofile << "drop table if exists " << name << ";\n"; 63 | ofile << "create table \"" << name << "\"\n"; 64 | ofile << "(\n"; 65 | std::string s; 66 | s += " \"" + id + "\" INTEGER,\n"; 67 | s += " \"" + row_type + "\" TEXT,\n"; 68 | std::map fields; 69 | for (auto &[_, f] : t) 70 | { 71 | for (auto &[n, v] : f) 72 | fields[n] = (FieldType)v.index(); 73 | } 74 | for (auto &[n, t] : fields) 75 | s += " \"" + n + "\" " + getSqlType(t) + ",\n"; 76 | s.resize(s.size() - 2); 77 | ofile << s << "\n"; 78 | ofile << ");\n"; 79 | ofile << "\n"; 80 | } 81 | 82 | // db tables 83 | std::map idx; 84 | for (auto &[tn, t] : db) 85 | { 86 | for (auto &[rn, row] : t) 87 | { 88 | ofile << "insert into \"" << tn << "\" ("; 89 | std::string s; 90 | s += "'" + id + "', "; 91 | s += "'" + row_type + "', "; 92 | for (auto &[n, v] : row) 93 | s += "'" + n + "', "; 94 | s.resize(s.size() - 2); 95 | ofile << s << ") values ("; 96 | s.clear(); 97 | s += "'" + std::to_string(++idx[tn]) + "', "; 98 | s += "'" + rn + "', "; 99 | for (auto &[n, v] : row) 100 | { 101 | s += "'"; 102 | switch ((FieldType)v.index()) 103 | { 104 | case FieldType::String: 105 | s += std::get(v); 106 | break; 107 | case FieldType::Integer: 108 | s += std::to_string(std::get(v)); 109 | break; 110 | case FieldType::Float: 111 | s += std::to_string(std::get(v)); 112 | break; 113 | default: 114 | SW_UNIMPLEMENTED; 115 | } 116 | s += "', "; 117 | } 118 | s.resize(s.size() - 2); 119 | ofile << s << ");\n"; 120 | } 121 | } 122 | } 123 | 124 | int main(int argc, char *argv[]) 125 | { 126 | cl::opt db_fn(cl::Positional, cl::desc(""), cl::Required); 127 | cl::opt codepage(cl::Positional, cl::desc(""), cl::Required); 128 | 129 | cl::ParseCommandLineOptions(argc, argv); 130 | 131 | db db; 132 | db.open(db_fn); 133 | create_sql(db_fn, db.process(codepage)); 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /src/mod_converter2/mod_converter2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mod_converter 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "mmap.h" 20 | #include "model.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #pragma pack(push, 1) 35 | struct mod { 36 | using char20 = char[0x20]; 37 | struct block { 38 | struct header { 39 | using texture = char20; 40 | 41 | BlockType type; 42 | char20 name; 43 | texture mask; 44 | texture spec; 45 | texture tex3; 46 | texture tex4; 47 | uint32_t all_lods; 48 | uint32_t unk2[3]; 49 | uint32_t unk3; 50 | uint32_t size; 51 | float unk4[10]; 52 | }; 53 | 54 | header h; 55 | uint32_t n_animations; 56 | material m; 57 | MaterialType mat_type; 58 | }; 59 | 60 | int n_blocks; 61 | char header_[0x40]; 62 | 63 | auto blocks() { 64 | auto base = (uint8_t*)&header_ + sizeof(header_); 65 | std::vector b; 66 | auto n = n_blocks; 67 | while (n--) { 68 | b.push_back((block*)base); 69 | base += sizeof(block::header) + b.back()->h.size; 70 | } 71 | return b; 72 | } 73 | }; 74 | #pragma pack(pop) 75 | 76 | auto read_model(const path &fn) { 77 | primitives::templates2::mmap_file f{fn, primitives::templates2::mmap_file::rw{}}; 78 | auto &m = *(mod*)f.p; 79 | auto v = m.blocks(); 80 | std::set textures; 81 | for (auto &&b : v) { 82 | if (b->h.type != BlockType::VisibleObject) { 83 | continue; 84 | } 85 | textures.insert(b->h.mask); 86 | textures.insert(b->h.spec); 87 | // we do not use tex3,tex4 atm 88 | // 89 | switch (b->mat_type) { 90 | case MaterialType::Texture: // works in m1 91 | case MaterialType::TextureWithGlareMap: // works in m1 92 | case MaterialType::AlphaTextureNoGlare: 93 | case MaterialType::AlphaTextureWithOverlap: 94 | case MaterialType::TextureWithGlareMap2: // works in m1 95 | case MaterialType::AlphaTextureDoubleSided: 96 | case MaterialType::MaterialOnly: 97 | case MaterialType::TextureWithDetalizationMap: 98 | case MaterialType::DetalizationObjectStone: 99 | case MaterialType::TextureWithDetalizationMapWithoutModulation: 100 | case MaterialType::TiledTexture: 101 | break; 102 | case MaterialType::TextureWithGlareMapAndMask: 103 | b->mat_type = MaterialType::TextureWithGlareMap; 104 | break; 105 | case MaterialType::TextureWithMask: 106 | b->mat_type = MaterialType::Texture; 107 | break; 108 | default: 109 | std::cout << b->h.name << ": " 110 | << "warning: unknown material type " << (int)b->mat_type << " \n"; 111 | break; 112 | } 113 | } 114 | textures.erase("_DEFAULT_"); // no tex 115 | textures.erase("TEX_TOV_AKTIVMETAL_ANIM"); // this one used as no-texture as well 116 | write_lines(path{fn}+=".textures.txt",textures); 117 | } 118 | 119 | void convert_model(const path &fn) 120 | { 121 | read_model(fn); 122 | } 123 | 124 | int main(int argc, char *argv[]) 125 | { 126 | cl::opt p(cl::Positional, cl::desc(""), cl::Required); 127 | 128 | cl::ParseCommandLineOptions(argc, argv); 129 | 130 | if (fs::is_regular_file(p)) 131 | convert_model(p); 132 | else if (fs::is_directory(p)) 133 | { 134 | auto files = enumerate_files(p, false); 135 | for (auto &f : FilesSorted(files.begin(), files.end())) 136 | { 137 | if (f.has_extension()) 138 | continue; 139 | std::cout << "processing: " << f << "\n"; 140 | try 141 | { 142 | convert_model(f); 143 | } 144 | catch (std::exception &e) 145 | { 146 | std::cout << "error: " << e.what() << "\n"; 147 | } 148 | } 149 | } 150 | else 151 | throw std::runtime_error("No such file or directory: " + to_printable_string(normalize_path(p))); 152 | 153 | return 0; 154 | } 155 | -------------------------------------------------------------------------------- /src/mpj_loader/mpj.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mpj_loader 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | enum class SegmentType : uint32_t 32 | { 33 | none = 0, 34 | MapData = 1, 35 | Surface = 2, 36 | Weather = 4, 37 | Objects = 6, 38 | unk7 = 7, 39 | Water = 8, 40 | unk9 = 9, 41 | BuildingGoods = 10, 42 | MapMusic = 11, 43 | Organizations = 12, 44 | Goods = 13, 45 | }; 46 | 47 | struct segment 48 | { 49 | SegmentType type; 50 | uint32_t unk0; 51 | uint32_t size; 52 | 53 | virtual ~segment() {} 54 | 55 | static segment *create_segment(const buffer &b); 56 | virtual void load(const buffer &b) = 0; 57 | }; 58 | 59 | struct map_data : public segment 60 | { 61 | std::vector unk1; 62 | std::vector unk2; 63 | std::vector unk3; 64 | 65 | virtual void load(const buffer &b) override; 66 | }; 67 | 68 | struct surface : public segment 69 | { 70 | struct value 71 | { 72 | std::string name; 73 | uint32_t unk0; 74 | }; 75 | std::vector unk1; 76 | 77 | virtual void load(const buffer &b) override; 78 | }; 79 | 80 | struct weather_data : public segment 81 | { 82 | weather_group wg; 83 | 84 | virtual void load(const buffer &b) override; 85 | }; 86 | 87 | struct objects_data : public segment 88 | { 89 | Objects objects; 90 | 91 | virtual void load(const buffer &b) override; 92 | }; 93 | 94 | struct segment7 : public segment 95 | { 96 | std::vector data; 97 | 98 | virtual void load(const buffer &b) override; 99 | }; 100 | 101 | struct water_data : public segment 102 | { 103 | water_group wg; 104 | 105 | virtual void load(const buffer &b) override; 106 | }; 107 | 108 | struct segment9 : public segment 109 | { 110 | std::vector data; 111 | 112 | virtual void load(const buffer &b) override; 113 | }; 114 | 115 | struct building_goods : public segment 116 | { 117 | struct bg_internal 118 | { 119 | BuildingGoods bg; 120 | uint32_t unk0; 121 | 122 | void load(const buffer &b) 123 | { 124 | bg.load(b); 125 | READ(b, unk0); 126 | } 127 | }; 128 | uint32_t unk1; // record format? aim2 ? 129 | uint32_t n; 130 | std::vector bgs; 131 | 132 | virtual void load(const buffer &b) override; 133 | }; 134 | 135 | struct map_music : public segment 136 | { 137 | std::vector mms; 138 | uint32_t unk1; 139 | 140 | virtual void load(const buffer &b) override; 141 | }; 142 | 143 | struct organizations : public segment 144 | { 145 | std::vector orgs; 146 | std::vector organizationBases; 147 | 148 | virtual void load(const buffer &b) override; 149 | }; 150 | 151 | struct gliders_n_goods : public segment 152 | { 153 | struct Good 154 | { 155 | std::string name; 156 | int unk0[3]; 157 | 158 | void load(const buffer &b) 159 | { 160 | READ_STRING(b, name); 161 | READ(b, unk0); 162 | } 163 | }; 164 | 165 | struct Goods 166 | { 167 | uint32_t n_goods; 168 | std::vector goods; 169 | 170 | void load(const buffer &b) 171 | { 172 | READ(b, n_goods); 173 | goods.resize(n_goods); 174 | for (auto &g : goods) 175 | g.load(b); 176 | } 177 | }; 178 | 179 | uint32_t n_good_groups; 180 | uint32_t n_gliders; 181 | std::vector gliders; 182 | uint32_t unk1; 183 | std::vector goods; 184 | 185 | virtual void load(const buffer &b) override; 186 | }; 187 | 188 | struct header 189 | { 190 | char magic[4]; 191 | uint32_t unk0; 192 | uint32_t unk1; 193 | uint32_t unk2; 194 | uint32_t unk3; 195 | uint32_t width; 196 | uint32_t height; 197 | char unk4[0x3F4]; 198 | std::vector segments; 199 | 200 | void load(const buffer &b); 201 | }; 202 | 203 | struct mpj 204 | { 205 | header h; 206 | 207 | // 208 | path filename; 209 | 210 | void load(const buffer &b); 211 | void load(const path &filename); 212 | }; 213 | -------------------------------------------------------------------------------- /src/common/buffer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM tools 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "buffer.h" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | const int build_version = 0 27 | //#include 28 | ; 29 | 30 | // use as getVersionString() from settings? 31 | std::string version() 32 | { 33 | using namespace std; 34 | 35 | string s; 36 | s = to_string(0) + "." + 37 | to_string(1) + "." + 38 | to_string(0) + "." + 39 | to_string(build_version); 40 | return s; 41 | } 42 | 43 | void writeFile(const std::string &fn, const std::vector &data) 44 | { 45 | FILE *f = fopen(fn.c_str(), "wb"); 46 | if (!f) 47 | throw std::runtime_error("Cannot open file " + fn); 48 | fwrite(data.data(), 1, data.size(), f); 49 | fclose(f); 50 | } 51 | 52 | buffer::buffer() 53 | { 54 | } 55 | 56 | buffer::buffer(size_t size) 57 | : buf_(new std::vector(size)) 58 | { 59 | size_ = buf_->size(); 60 | skip(0); 61 | } 62 | 63 | buffer::buffer(const std::string &s) 64 | : buf_(new std::vector(&s[0], &s[s.size()])) 65 | { 66 | skip(0); 67 | size_ = buf_->size(); 68 | end_ = index_ + size_; 69 | } 70 | 71 | buffer::buffer(const std::vector &buf, uint32_t data_offset) 72 | : buf_(new std::vector(buf)), data_offset(data_offset) 73 | { 74 | skip(0); 75 | size_ = buf_->size(); 76 | end_ = index_ + size_; 77 | } 78 | 79 | buffer::buffer(const buffer &rhs, uint32_t size) 80 | : buf_(rhs.buf_) 81 | { 82 | index_ = rhs.index_; 83 | data_offset = rhs.data_offset; 84 | ptr = rhs.ptr; 85 | size_ = size; 86 | end_ = index_ + size_; 87 | rhs.skip(size); 88 | } 89 | 90 | buffer::buffer(const buffer &rhs, uint32_t size, uint32_t offset) 91 | : buf_(rhs.buf_) 92 | { 93 | index_ = offset; 94 | data_offset = offset; 95 | size_ = size; 96 | ptr = (uint8_t *)buf_->data() + index_; 97 | end_ = index_ + size_; 98 | } 99 | 100 | std::string buffer::read_string(uint32_t blocksize) const 101 | { 102 | std::vector data(blocksize); 103 | _read(data.data(), data.size()); 104 | return (const char *)data.data(); 105 | } 106 | 107 | std::wstring buffer::read_wstring(uint32_t blocksize) const 108 | { 109 | std::vector data(blocksize); 110 | _read(data.data(), data.size() * sizeof(wchar_t)); 111 | return (const wchar_t *)data.data(); 112 | } 113 | 114 | uint32_t buffer::_read(void *dst, uint32_t size, uint32_t offset) const 115 | { 116 | if (!buf_) 117 | throw std::logic_error("buffer: not initialized"); 118 | if (index_ >= end_) 119 | throw std::logic_error("buffer: out of range"); 120 | if (index_ + offset + size > end_) 121 | throw std::logic_error("buffer: too much data"); 122 | memcpy(dst, buf_->data() + index_ + offset, size); 123 | skip(size + offset); 124 | return size; 125 | } 126 | 127 | uint32_t buffer::_write(const void *src, uint32_t size) 128 | { 129 | if (!buf_) 130 | { 131 | buf_ = std::make_shared>(size); 132 | end_ = size_ = buf_->size(); 133 | } 134 | if (index_ > end_) 135 | throw std::logic_error("buffer: out of range"); 136 | if (index_ + size > end_) 137 | { 138 | buf_->resize(index_ + size); 139 | end_ = size_ = buf_->size(); 140 | } 141 | memcpy((uint8_t *)buf_->data() + index_, src, size); 142 | skip(size); 143 | return size; 144 | } 145 | 146 | void buffer::skip(int n) const 147 | { 148 | if (!buf_) 149 | throw std::logic_error("buffer: not initialized"); 150 | index_ += n; 151 | data_offset += n; 152 | ptr = (uint8_t *)buf_->data() + index_; 153 | } 154 | 155 | void buffer::reset() const 156 | { 157 | index_ = 0; 158 | data_offset = 0; 159 | if (buf_) 160 | ptr = (uint8_t *)buf_->data(); 161 | } 162 | 163 | void buffer::seek(uint32_t size) const 164 | { 165 | reset(); 166 | skip(size); 167 | } 168 | 169 | bool buffer::check_pos(int index) const 170 | { 171 | return index_ == index; 172 | } 173 | 174 | bool buffer::eof() const 175 | { 176 | return index_ == end_; 177 | } 178 | 179 | uint32_t buffer::index() const 180 | { 181 | return index_; 182 | } 183 | 184 | uint32_t buffer::size() const 185 | { 186 | return size_; 187 | } 188 | 189 | const std::vector &buffer::buf() const 190 | { 191 | if (!buf_) 192 | throw std::logic_error("buffer: not initialized"); 193 | return *buf_; 194 | } 195 | -------------------------------------------------------------------------------- /src/common/mat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM tools 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | template 33 | class mat 34 | { 35 | public: 36 | using type = T; 37 | 38 | public: 39 | mat(int w = 0, int h = 0) 40 | { 41 | width = w < 0 ? 0 : w; 42 | height = h < 0 ? 0 : h; 43 | data.resize(width * height, T()); 44 | } 45 | 46 | T &operator()(int i) 47 | { 48 | return data[i]; 49 | } 50 | const T &operator()(int i) const 51 | { 52 | return (*const_cast(this))(i); 53 | } 54 | 55 | T &operator()(int row, int col) 56 | { 57 | assert(!(row >= height || col >= width || row < 0 || col < 0)); 58 | return data[row * width + col]; 59 | } 60 | const T &operator()(int row, int col) const 61 | { 62 | return (*const_cast(this))(row, col); 63 | } 64 | 65 | void clean() 66 | { 67 | std::fill(data.begin(), data.end(), T()); 68 | } 69 | 70 | int getWidth() const { return width; } 71 | int getHeight() const { return height; } 72 | int size() const { return width * height; } 73 | int getBytesLength() const { return size() * sizeof(T); } 74 | int getPos(const T *const elem) const { return elem - &data[0]; } 75 | const std::vector &getData() const { return data; } 76 | std::vector &getData() { return data; } 77 | 78 | auto begin() { return data.begin(); } 79 | auto end() { return data.end(); } 80 | auto begin() const { return data.begin(); } 81 | auto end() const { return data.end(); } 82 | 83 | // left/right 84 | mat mirror() const 85 | { 86 | int cols = width; 87 | int rows = height; 88 | mat m(width, height); 89 | for (int row = 0; row < rows; row++) 90 | { 91 | for (int col = 0; col < cols; col++) 92 | { 93 | auto &o = operator()(row * cols + col); 94 | auto &n = m(row * cols + (cols - 1 - col)); 95 | n = o; 96 | } 97 | } 98 | return m; 99 | } 100 | 101 | // up/down 102 | mat flip() const 103 | { 104 | int cols = width; 105 | int rows = height; 106 | mat m(width, height); 107 | for (int row = 0; row < rows; row++) 108 | { 109 | for (int col = 0; col < cols; col++) 110 | { 111 | auto &o = operator()(row * cols + col); 112 | auto &n = m((rows - 1 - row) * cols + col); 113 | n = o; 114 | } 115 | } 116 | return m; 117 | } 118 | 119 | private: 120 | std::vector data; 121 | int width; 122 | int height; 123 | }; 124 | 125 | inline void write_mat_bmp(const path &filename, int width, int height, int bits, const uint8_t *b, size_t s) 126 | { 127 | auto f = primitives::filesystem::fopen(filename, "wb"); 128 | if (f == nullptr) 129 | return; 130 | bmp_header h = { 0 }; 131 | h.bfType = 0x4D42; 132 | h.bfSize = sizeof(bmp_header) + sizeof(bmp_info_header) + s; 133 | h.bfOffBits = sizeof(bmp_header) + sizeof(bmp_info_header); 134 | bmp_info_header i = { 0 }; 135 | i.biSize = sizeof(i); 136 | i.biWidth = width; 137 | i.biHeight = height; 138 | i.biPlanes = 1; 139 | i.biBitCount = bits; 140 | i.biCompression = 0; 141 | i.biSizeImage = 0; 142 | i.biXPelsPerMeter = 0; 143 | i.biYPelsPerMeter = 0; 144 | i.biClrUsed = 0; 145 | i.biClrImportant = 0; 146 | fwrite(&h, sizeof(bmp_header), 1, f); 147 | fwrite(&i, sizeof(bmp_info_header), 1, f); 148 | fwrite(b, s, 1, f); 149 | fclose(f); 150 | } 151 | 152 | template 153 | void write_mat_bmp(const path &filename, const mat &m) 154 | { 155 | write_mat_bmp(filename, m.getWidth(), m.getHeight(), sizeof(T) * CHAR_BIT, (const uint8_t *)&m(0, 0), m.size() * sizeof(T)); 156 | } 157 | 158 | template 159 | void write_mat_tga(const path &filename, const mat &m) 160 | { 161 | auto f = primitives::filesystem::fopen(filename, "wb"); 162 | if (f == nullptr) 163 | return; 164 | 165 | tga t; 166 | t.width = m.getWidth(); 167 | t.height = m.getHeight(); 168 | 169 | // header 170 | fwrite(&t, sizeof(tga), 1, f); 171 | fwrite(t.label(), t.idlength, 1, f); 172 | 173 | // data 174 | fwrite(&m(0, 0), m.size() * sizeof(T), 1, f); 175 | fclose(f); 176 | } 177 | -------------------------------------------------------------------------------- /src/common/db.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM db_extractor 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "db.h" 20 | 21 | #include "buffer.h" 22 | #include "common.h" 23 | 24 | #include 25 | 26 | std::string getSqlType(FieldType type) 27 | { 28 | switch (type) 29 | { 30 | case FieldType::String: 31 | return "TEXT"; 32 | case FieldType::Integer: 33 | return "INTEGER"; 34 | case FieldType::Float: 35 | return "REAL"; 36 | default: 37 | SW_UNIMPLEMENTED; 38 | } 39 | } 40 | 41 | void table::load(const buffer &b) 42 | { 43 | READ(b, id); 44 | READ_STRING(b, name); 45 | READ(b, unk4); 46 | } 47 | 48 | void field::load(const buffer &b) 49 | { 50 | if (b.eof()) 51 | return; 52 | READ(b, table_id); 53 | READ(b, id); 54 | READ_STRING(b, name); 55 | READ(b, type); 56 | } 57 | 58 | void tab::load(const buffer &b) 59 | { 60 | READ(b, number_of_tables); 61 | READ(b, number_of_fields); 62 | 63 | auto n = number_of_tables; 64 | while (n--) 65 | { 66 | table t; 67 | t.load(b); 68 | tables[t.id] = t; 69 | } 70 | 71 | n = number_of_fields; 72 | while (n--) 73 | { 74 | field t; 75 | t.load(b); 76 | if (t.table_id == -1) 77 | continue; 78 | fields[t.id] = t; 79 | } 80 | } 81 | 82 | void value::load_index(const buffer &b) 83 | { 84 | READ(b, table_id); 85 | READ_STRING(b, name); 86 | READ(b, offset); 87 | READ(b, data_size); 88 | } 89 | 90 | void value::load_fields(const tab &tab, buffer &b) 91 | { 92 | buffer data(b, data_size, offset); 93 | while (!data.eof()) 94 | { 95 | field_value fv; 96 | READ(data, fv.field_id); 97 | READ(data, fv.size); 98 | auto i = tab.fields.find(fv.field_id); 99 | if (i == tab.fields.end()) 100 | continue; 101 | 102 | buffer data2(data, fv.size); 103 | switch (i->second.type) 104 | { 105 | case FieldType::String: 106 | fv.s.resize(fv.size); 107 | READ_N(data2, fv.s[0], fv.s.size()); 108 | break; 109 | case FieldType::Integer: 110 | if (sizeof(fv.i) <= fv.size) 111 | READ(data2, fv.i); 112 | else 113 | std::cerr << "small int field: " << fv.size << "\n"; 114 | break; 115 | case FieldType::Float: 116 | if (sizeof(fv.f) <= fv.size) 117 | READ(data2, fv.f); 118 | else 119 | std::cerr << "small float field: " << fv.size << "\n"; 120 | break; 121 | default: 122 | SW_UNIMPLEMENTED; 123 | } 124 | fields.push_back(fv); 125 | } 126 | } 127 | 128 | void db::load(const buffer &b) 129 | { 130 | READ(b, number_of_values); 131 | 132 | auto n = number_of_values; 133 | while (n--) 134 | { 135 | value t; 136 | t.load_index(b); 137 | values.push_back(t); 138 | } 139 | } 140 | 141 | void db::open(const path &p) 142 | { 143 | t.load(buffer(read_file(path(p) += ".tab"))); 144 | load(buffer(read_file(path(p) += ".ind"))); 145 | buffer b(read_file(path(p) += ".dat")); 146 | for (auto &v : values) 147 | v.load_fields(t, b); 148 | } 149 | 150 | polygon4::tools::db::processed_db db::process(int cp) const 151 | { 152 | auto process_string = [&](const std::string &s) 153 | { 154 | return str2utf8(s.c_str(), cp); 155 | }; 156 | 157 | polygon4::tools::db::processed_db pdb; 158 | for (auto &v : values) 159 | { 160 | auto tbl = t.tables.find(v.table_id); 161 | if (tbl == t.tables.end()) 162 | continue; 163 | polygon4::tools::db::record r; 164 | for (auto &f : v.fields) 165 | { 166 | auto fld = t.fields.find(f.field_id); 167 | if (fld == t.fields.end()) 168 | continue; 169 | auto name = process_string(fld->second.name); 170 | switch (fld->second.type) 171 | { 172 | case FieldType::String: 173 | r[name] = process_string(f.s); 174 | break; 175 | case FieldType::Integer: 176 | r[name] = f.i; 177 | break; 178 | case FieldType::Float: 179 | r[name] = f.f; 180 | break; 181 | default: 182 | SW_UNIMPLEMENTED; 183 | } 184 | } 185 | auto table_name = process_string(tbl->second.name); 186 | auto row_name = process_string(v.name); 187 | pdb[table_name][row_name] = r; 188 | } 189 | return pdb; 190 | } 191 | -------------------------------------------------------------------------------- /src/common/dxt5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM tools 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "buffer.h" 24 | #include "color.h" 25 | #include "mat.h" 26 | 27 | // https://en.wikipedia.org/wiki/S3_Texture_Compression 28 | // decompressor https://github.com/Benjamin-Dobell/s3tc-dxt-decompression 29 | // compressor https://github.com/nothings/stb/blob/master/stb_dxt.h 30 | // or modified https://github.com/Cyan4973/RygsDXTc/blob/master/stb_dxt.h 31 | struct dxt5_block 32 | { 33 | union 34 | { 35 | uint64_t alpha_part; 36 | uint8_t a[8]; 37 | }; 38 | union 39 | { 40 | uint64_t color_part; 41 | uint16_t c[4]; 42 | }; 43 | 44 | // 45 | uint8_t alpha_table[8]; 46 | color color_table[4]; 47 | union 48 | { 49 | color pixels[16]; 50 | color pixel_mat[4][4]; 51 | }; 52 | 53 | dxt5_block() {} 54 | void load(const buffer &b) 55 | { 56 | READ(b, alpha_part); 57 | READ(b, color_part); 58 | } 59 | void unpack() 60 | { 61 | // alpha 62 | auto &a = alpha_table; 63 | a[0] = this->a[0]; 64 | a[1] = this->a[1]; 65 | if (a[0] > a[1]) 66 | { 67 | a[2] = (uint8_t)(double(6 * a[0] + 1 * a[1]) / 7.0); 68 | a[3] = (uint8_t)(double(5 * a[0] + 2 * a[1]) / 7.0); 69 | a[4] = (uint8_t)(double(4 * a[0] + 3 * a[1]) / 7.0); 70 | a[5] = (uint8_t)(double(3 * a[0] + 4 * a[1]) / 7.0); 71 | a[6] = (uint8_t)(double(2 * a[0] + 5 * a[1]) / 7.0); 72 | a[7] = (uint8_t)(double(1 * a[0] + 6 * a[1]) / 7.0); 73 | } 74 | else 75 | { 76 | a[2] = (uint8_t)(double(4 * a[0] + 1 * a[1]) / 5.0); 77 | a[3] = (uint8_t)(double(3 * a[0] + 2 * a[1]) / 5.0); 78 | a[4] = (uint8_t)(double(2 * a[0] + 3 * a[1]) / 5.0); 79 | a[5] = (uint8_t)(double(1 * a[0] + 4 * a[1]) / 5.0); 80 | a[6] = 0; 81 | a[7] = 255; 82 | } 83 | 84 | // color 85 | auto &c = color_table; 86 | c[0] = color(this->c[0]); 87 | c[1] = color(this->c[1]); 88 | if (this->c[0] > this->c[1]) 89 | { 90 | c[2] = interpolate(c[0], c[1], 2.f / 3.f); 91 | c[3] = interpolate(c[0], c[1], 1.f / 3.f); 92 | } 93 | else 94 | { 95 | c[2] = interpolate(c[0], c[1], 1.f / 2.f); 96 | c[3].data = 0; 97 | } 98 | 99 | // result 100 | for (int p = 0; p < 16; p++) 101 | { 102 | pixels[p] = c[(color_part >> (32 + p * 2)) & 0b11]; 103 | pixels[p].a = a[(alpha_part >> (16 + p * 3)) & 0b111]; 104 | } 105 | } 106 | static color interpolate(color c0, color c1, float m) 107 | { 108 | color r; 109 | for (int i = 0; i < 4; i++) 110 | r.byte[i] = uint8_t(c0.byte[i] * m + c1.byte[i] * (1 - m)); 111 | return r; 112 | } 113 | }; 114 | 115 | struct dxt5 116 | { 117 | uint32_t width; 118 | uint32_t height; 119 | std::vector blocks; 120 | 121 | void load(const buffer &b) 122 | { 123 | READ(b, width); 124 | READ(b, height); 125 | load_blocks(b); 126 | } 127 | void load_blocks(const buffer &b) 128 | { 129 | blocks.resize(width * height / 16); 130 | for (auto &d : blocks) 131 | { 132 | d.load(b); 133 | d.unpack(); 134 | } 135 | } 136 | mat unpack_mmm() 137 | { 138 | mat m(width, height); 139 | auto big_xsegs = width / 64; 140 | auto big_ysegs = height / 64; 141 | for (size_t seg = 0; seg < blocks.size(); seg++) 142 | { 143 | auto &d = blocks[seg]; 144 | int big_seg = seg / 256; 145 | auto big_xseg = big_seg % big_xsegs; 146 | auto big_yseg = big_seg / big_xsegs; 147 | auto xseg = seg % 16 + big_xseg * 16; 148 | auto yseg = seg % 256 / 16 + big_yseg * 16; 149 | for (int i = 0; i < 4; i++) 150 | memcpy(&m(height - 1 - (yseg * 4 + i), xseg * 4), d.pixel_mat[i], 16); 151 | } 152 | return m; 153 | } 154 | mat unpack_tm() 155 | { 156 | mat m(width, height); 157 | auto xsegs = width / 4; 158 | for (size_t seg = 0; seg < blocks.size(); seg++) 159 | { 160 | auto &d = blocks[seg]; 161 | auto xseg = seg % xsegs; 162 | auto yseg = seg / xsegs; 163 | for (int i = 0; i < 4; i++) 164 | memcpy(&m(height - 1 - (yseg * 4 + i), xseg * 4), d.pixel_mat[i], 16); 165 | } 166 | return m; 167 | } 168 | }; 169 | -------------------------------------------------------------------------------- /src/mod_converter/mod_converter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mod_converter 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include "model.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | /* 37 | TODO: 38 | - ue4 .obj import not working (model looks ugly) 39 | - .fbx export with link faces not working 40 | */ 41 | 42 | // options 43 | bool silent = false; 44 | bool printMaxPolygonBlock = false; 45 | 46 | cl::opt p(cl::Positional, cl::desc(""), cl::value_desc("file or directory"), cl::Required); 47 | cl::opt all_formats("af", cl::desc("All formats (.obj, .fbx)")); 48 | // link_faces is not currently complete, after processing we have bad uvs 49 | cl::opt link_faces("lf", cl::desc("Link faces (default: true)")/*, cl::init(true)*/); 50 | 51 | yaml root; 52 | cl::opt stats("i", cl::desc("Gather information from (models)")); 53 | 54 | // https://twitter.com/FreyaHolmer/status/644881436982575104 55 | // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_axis_system_html 56 | cl::opt AS(cl::desc("Choose axis system (.fbx only):"), 57 | cl::values( 58 | #define axisval(x, y) \ 59 | cl::OptionEnumValue{ #x, (int)AxisSystem::x, y } 60 | #define axisalias(x, a) \ 61 | cl::OptionEnumValue{ #x, (int)AxisSystem::a, "Same as " #a } 62 | 63 | axisval(eMayaZUp, "UpVector = ZAxis, FrontVector = -ParityOdd, CoordSystem = RightHanded ( also 3dsMax, Blender)\n" 64 | "(Blender: when importing, disable 'Use Pre/Post Rotation')"), 65 | axisalias(eMax, eMayaZUp), 66 | axisalias(eBlender, eMayaZUp), 67 | 68 | axisval(eMayaYUp, "UpVector = YAxis, FrontVector = ParityOdd, CoordSystem = RightHanded (default, also MotionBuilder, OpenGL)"), 69 | axisalias(eMotionbuilder, eMayaYUp), 70 | axisalias(eOpenGL, eMayaYUp), 71 | 72 | axisval(eDirectX, "UpVector = YAxis, FrontVector = ParityOdd, CoordSystem = LeftHanded ( also Lightwave)"), 73 | axisalias(eLightwave, eDirectX), 74 | 75 | axisval(eWindows3DViewer, "UpVector = ZAxis, FrontVector = ParityOdd, CoordSystem = RightHanded ( also Lightwave)") 76 | 77 | #undef axisval 78 | #undef axisalias 79 | ) 80 | , cl::init(AxisSystem::Default) 81 | ); 82 | 83 | auto read_model(const path &fn) 84 | { 85 | buffer b(read_file(fn)); 86 | model m; 87 | if (fn.extension() == ".mod") // single block file from m2 sdk viewer 88 | { 89 | block bl = {}; 90 | bl.printable = true; // allow to output 91 | bl.h.name = to_printable_string(fn.stem()); 92 | bl.loadPayloadAndProcess(b); 93 | m.blocks.push_back(bl); 94 | } 95 | else 96 | { 97 | m.load(b); 98 | } 99 | if (link_faces) 100 | m.linkFaces(); 101 | 102 | if (!b.eof()) 103 | { 104 | std::stringstream ss; 105 | ss << std::hex << b.index() << " != " << std::hex << b.size(); 106 | throw std::logic_error(ss.str()); 107 | } 108 | 109 | return m; 110 | } 111 | 112 | void convert_model(const model &m, const path &fn) 113 | { 114 | // write all 115 | if (all_formats) 116 | m.print(to_printable_string(fn), AS); 117 | m.printFbx(to_printable_string(fn), AS); 118 | } 119 | 120 | void convert_model(const path &fn) 121 | { 122 | auto m = read_model(fn); 123 | 124 | if (stats) 125 | { 126 | m.save(root[to_printable_string(fn.filename())]); 127 | return; 128 | } 129 | 130 | convert_model(m, fn); 131 | } 132 | 133 | int main(int argc, char *argv[]) 134 | { 135 | cl::opt mr("mr", cl::desc("AIM Racing MOD file")); 136 | 137 | cl::ParseCommandLineOptions(argc, argv); 138 | 139 | if (mr) 140 | gameType = GameType::AimR; 141 | 142 | if (fs::is_regular_file(p)) 143 | convert_model(p); 144 | else if (fs::is_directory(p)) 145 | { 146 | auto files = enumerate_files(p, false); 147 | for (auto &f : FilesSorted(files.begin(), files.end())) 148 | { 149 | if (f.has_extension()) 150 | continue; 151 | std::cout << "processing: " << f << "\n"; 152 | try 153 | { 154 | convert_model(f); 155 | } 156 | catch (std::exception &e) 157 | { 158 | std::cout << "error: " << e.what() << "\n"; 159 | } 160 | } 161 | } 162 | else 163 | throw std::runtime_error("No such file or directory: " + to_printable_string(normalize_path(p))); 164 | 165 | if (stats) 166 | { 167 | write_file((fs::is_regular_file(p) ? path(p) += ".txt" : (p / "model_information.yml")) , YAML::Dump(root)); 168 | } 169 | 170 | return 0; 171 | } 172 | -------------------------------------------------------------------------------- /src/mpj_loader/mpj.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mpj_loader 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "mpj.h" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | segment *segment::create_segment(const buffer &b) 31 | { 32 | SegmentType type; 33 | READ(b, type); 34 | if (type == SegmentType::none) 35 | READ(b, type); 36 | 37 | segment *segment = nullptr; 38 | switch (type) 39 | { 40 | case SegmentType::MapData: 41 | segment = new map_data; 42 | break; 43 | case SegmentType::Surface: 44 | segment = new surface; 45 | break; 46 | case SegmentType::Weather: 47 | segment = new weather_data; 48 | break; 49 | case SegmentType::Objects: 50 | segment = new objects_data; 51 | break; 52 | case SegmentType::Water: 53 | segment = new water_data; 54 | break; 55 | case SegmentType::unk7: 56 | segment = new segment7; 57 | break; 58 | case SegmentType::unk9: 59 | segment = new segment9; 60 | break; 61 | case SegmentType::BuildingGoods: 62 | segment = new building_goods; 63 | break; 64 | case SegmentType::MapMusic: 65 | segment = new map_music; 66 | break; 67 | case SegmentType::Organizations: 68 | segment = new organizations; 69 | break; 70 | case SegmentType::Goods: 71 | segment = new gliders_n_goods; 72 | break; 73 | default: 74 | assert(false); 75 | break; 76 | } 77 | if (segment) 78 | { 79 | segment->type = type; 80 | READ(b, segment->unk0); 81 | READ(b, segment->size); 82 | } 83 | return segment; 84 | } 85 | 86 | void map_data::load(const buffer &b) 87 | { 88 | auto sz = b.size(); 89 | assert(sz % 3 == 0); 90 | sz /= 3; 91 | #define READ_SEG(v) \ 92 | v.resize(sz / sizeof(decltype(v)::value_type)); \ 93 | READ_N(b, v[0], v.size()) 94 | 95 | READ_SEG(unk1); 96 | READ_SEG(unk2); 97 | READ_SEG(unk3); 98 | #undef READ_SEG 99 | } 100 | 101 | void surface::load(const buffer &b) 102 | { 103 | while (!b.eof()) 104 | { 105 | value v; 106 | READ_STRING(b, v.name); 107 | READ(b, v.unk0); 108 | unk1.push_back(v); 109 | } 110 | } 111 | 112 | void weather_data::load(const buffer &b) 113 | { 114 | wg.load(b); 115 | } 116 | 117 | void objects_data::load(const buffer &b) 118 | { 119 | objects.load(b); 120 | } 121 | 122 | void segment7::load(const buffer &b) 123 | { 124 | auto n = b.size() / sizeof(decltype(data)::value_type); 125 | data.resize(n); 126 | READ_N(b, data[0], n); 127 | } 128 | 129 | void water_data::load(const buffer &b) 130 | { 131 | wg.load(b); 132 | } 133 | 134 | void segment9::load(const buffer &b) 135 | { 136 | auto n = b.size() / sizeof(decltype(data)::value_type); 137 | data.resize(n); 138 | READ_N(b, data[0], n); 139 | } 140 | 141 | void building_goods::load(const buffer &b) 142 | { 143 | READ(b, unk1); 144 | READ(b, n); 145 | bgs.resize(n); 146 | for (auto &bg : bgs) 147 | bg.load(b); 148 | } 149 | 150 | void map_music::load(const buffer &b) 151 | { 152 | while (!b.eof()) 153 | { 154 | MapMusic mm; 155 | mm.load(b); 156 | mms.push_back(mm); 157 | READ(b, unk1); // or read after cycle? 158 | } 159 | } 160 | 161 | void organizations::load(const buffer &b) 162 | { 163 | b.read_vector(orgs); 164 | b.read_vector(organizationBases); 165 | } 166 | 167 | void gliders_n_goods::load(const buffer &b) 168 | { 169 | READ(b, n_good_groups); 170 | READ(b, n_gliders); 171 | gliders.resize(n_gliders); 172 | for (auto &g : gliders) 173 | g.load(b); 174 | READ(b, unk1); 175 | goods.resize(n_good_groups); 176 | for (auto &g : goods) 177 | g.load(b); 178 | } 179 | 180 | void header::load(const buffer &b) 181 | { 182 | READ(b, magic); 183 | if (memcmp(magic, "MPRJ", 4) != 0) 184 | throw std::runtime_error("This file is not a mechanoid project"); 185 | READ(b, unk0); 186 | READ(b, unk1); 187 | READ(b, unk2); 188 | READ(b, unk3); 189 | READ(b, width); 190 | READ(b, height); 191 | READ(b, unk4); 192 | 193 | auto seg_size = 64; 194 | auto xsegs = width / seg_size; 195 | if ((width - 1) % 64 != 0) 196 | xsegs++; 197 | auto ysegs = height / seg_size; 198 | if ((width - 1) % 64 != 0) 199 | ysegs++; 200 | 201 | while (!b.eof()) 202 | { 203 | auto seg = segment::create_segment(b); 204 | if (!seg) 205 | break; 206 | seg->load(buffer(b, seg->size)); 207 | segments.push_back(seg); 208 | } 209 | 210 | if (!b.eof()) 211 | { 212 | throw std::logic_error("End of file was not reached"); 213 | } 214 | } 215 | 216 | void mpj::load(const buffer &b) 217 | { 218 | h.load(b); 219 | } 220 | 221 | void mpj::load(const path &fn) 222 | { 223 | filename = fn; 224 | buffer b(read_file(filename)); 225 | load(b); 226 | } 227 | -------------------------------------------------------------------------------- /src/paker/paker.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM paker (for AIM1 game only) 3 | * Copyright (C) 2024 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | using namespace std; 36 | 37 | // Currently we pack everything as single segment for simplicity. 38 | // And without any compression. 39 | 40 | int main(int argc, char *argv[]) { 41 | cl::opt name(cl::Positional, cl::desc(""), cl::Required); 42 | // game produces warnings on empty .pak files 43 | cl::list in_files(cl::Positional, cl::desc(""), cl::Required, cl::OneOrMore); 44 | 45 | cl::ParseCommandLineOptions(argc, argv); 46 | 47 | struct file { 48 | path fn; 49 | String alias; 50 | auto operator<=>(const file &) const = default; 51 | }; 52 | std::set files; 53 | for (auto &&f : in_files) { 54 | auto s = f; 55 | boost::replace_all(s, "/", "\\"); 56 | boost::to_lower(s); 57 | auto v = split_string(s, "="); 58 | if (v.size() > 2 || v.empty()) { 59 | throw std::runtime_error("bad input filename: "s + f); 60 | } 61 | if (v.size() == 1) { 62 | path p{s}; 63 | String alias; 64 | // some heuristics 65 | // also consider Scripts (source code): `/Script` 66 | if (p.extension() == ".qst" || p.extension() == ".scr") { 67 | alias = "script\\bin\\" + p.filename().string(); 68 | } 69 | if (p.extension() == ".mmp" || p.extension() == ".mmo" || p.extension() == ".mmm") { 70 | alias = p.filename().string(); 71 | } 72 | if (p.extension() == ".ogg") { 73 | alias = "data\\sound\\" + p.filename().string(); 74 | } 75 | if (p.extension() == ".tm") { 76 | alias = "data\\tm\\" + p.filename().string(); 77 | } 78 | // models do not have exts 79 | if (p.extension() == ""s) { 80 | alias = "data\\models\\" + p.filename().string(); 81 | } 82 | files.emplace(v[0], alias); 83 | } else if (v.size() == 2) { 84 | files.emplace(v[0], v[1]); 85 | } 86 | } 87 | 88 | size_t total{}; 89 | auto aligned_size = [](auto sz) { 90 | // align like 0x4000 does not work 91 | // neither align works 92 | //constexpr auto align = 128; 93 | //return (sz + align - 1) / align * align; 94 | return sz; 95 | }; 96 | for (auto &[f,_] : files) { 97 | total += aligned_size(fs::file_size(f)); 98 | } 99 | uint32_t block_size = pak::default_block_size; 100 | uint32_t block_size_len = sizeof(block_size); 101 | // NOTE: make it single segment; 102 | block_size = total + block_size_len; 103 | uint32_t block_size_minus_len = block_size - block_size_len; // minus block len 104 | auto get_nsegs = [&](auto sz) { 105 | auto nsegs = sz / block_size_minus_len; 106 | if (nsegs == 0 || (sz % block_size_minus_len) != 0) { 107 | ++nsegs; 108 | } 109 | return nsegs; 110 | }; 111 | auto nsegs = get_nsegs(total); 112 | total = 0; 113 | total += files.size() * sizeof(pak::file_description); 114 | total += nsegs * (sizeof(pak::segment) + block_size); 115 | total += sizeof(pak); 116 | 117 | pak p{}; 118 | p.block_size = block_size; 119 | p.n_files = files.size(); 120 | p.n_blocks = nsegs; 121 | 122 | if (!name.parent_path().empty()) { 123 | fs::create_directories(name.parent_path()); 124 | } 125 | std::ofstream{name}; 126 | fs::resize_file(name, total); 127 | primitives::templates2::mmap_file f{name, primitives::templates2::mmap_file::rw{}}; 128 | memset(f.p, 0, f.sz); 129 | 130 | stream s{f}; 131 | s = p; 132 | size_t offset = 0; 133 | for (auto &[name,alias] : files) { 134 | pak::file_description d{}; 135 | auto str = alias.empty() ? name.string() : alias; 136 | strcpy(d.name, str.c_str()); 137 | d.size = fs::file_size(name); 138 | d.offset = offset; 139 | offset += aligned_size(d.size); 140 | s = d; 141 | } 142 | for (int i = 0; i < nsegs; ++i) { 143 | pak::segment seg{}; 144 | seg.algorithm = 0; // NOTE: no compression! 145 | seg.offset = sizeof(pak) + files.size() * sizeof(pak::file_description) + nsegs * sizeof(pak::segment) + i * block_size; 146 | s = seg; 147 | } 148 | // each data segment starts with its size 149 | s = block_size_minus_len; // for single seg 150 | for (auto &[name,_] : files) { 151 | std::cout << "processing: " << name << "\n"; 152 | primitives::templates2::mmap_file f{name}; 153 | uint32_t sz_to_copy = f.sz; 154 | memcpy(s.p, f.p, sz_to_copy); 155 | s.skip(sz_to_copy); 156 | } 157 | f.close(); 158 | 159 | return 0; 160 | } 161 | -------------------------------------------------------------------------------- /sw.cpp: -------------------------------------------------------------------------------- 1 | #pragma sw require header org.sw.demo.lexxmark.winflexbison.bison 2 | 3 | void build(Solution &s) 4 | { 5 | auto &tools = s.addProject("Polygon4.Tools", "master"); 6 | tools += Git("https://github.com/aimrebirth/tools", "", "{v}"); 7 | 8 | auto cppstd = cpp23; 9 | 10 | auto &common = tools.addStaticLibrary("common"); 11 | { 12 | common += cppstd; 13 | common.setRootDirectory("src/common"); 14 | common += ".*"_rr; 15 | common.Public += "pub.egorpugin.primitives.filesystem"_dep; 16 | common.Public += "pub.egorpugin.primitives.templates2"_dep; 17 | } 18 | 19 | auto add_exe_base = [&](const String &name, String dirname = {}) -> decltype(auto) 20 | { 21 | if (dirname.empty()) 22 | dirname = name; 23 | auto &t = tools.addExecutable(name); 24 | t.PackageDefinitions = true; 25 | t += cppstd; 26 | t.setRootDirectory("src/" + dirname); 27 | t += ".*"_rr; // to take .natvis files explicitly 28 | return t; 29 | }; 30 | auto add_exe = [&](const String &name, const String &dirname = {}) -> decltype(auto) 31 | { 32 | auto &t = add_exe_base(name, dirname); 33 | t.Public += "pub.egorpugin.primitives.sw.main"_dep; 34 | return t; 35 | }; 36 | auto add_exe_with_common = [&](const String &name, const String &dirname = {}) -> decltype(auto) 37 | { 38 | auto &t = add_exe(name, dirname); 39 | t.Public += common; 40 | return t; 41 | }; 42 | auto add_exe_with_data_manager = [&](const String &name, const String &dirname = {}) -> decltype(auto) 43 | { 44 | auto &t = add_exe_with_common(name, dirname); 45 | t.Public += "pub.lzwdgc.Polygon4.DataManager-master"_dep; 46 | return t; 47 | }; 48 | 49 | add_exe_with_data_manager("db_add_language") += "pub.egorpugin.primitives.executor"_dep; 50 | add_exe_with_data_manager("db_extractor"); 51 | add_exe_with_data_manager("db_extractor2") += "org.sw.demo.nlohmann.json.natvis"_dep; 52 | add_exe_with_data_manager("mmm_extractor"); 53 | add_exe_with_data_manager("mmo_extractor"); 54 | add_exe_with_common("mmo_extractor2"); 55 | add_exe_with_common("mmp_extractor") += "org.sw.demo.intel.opencv.highgui"_dep; 56 | add_exe_with_common("mpj_loader"); 57 | add_exe_with_common("paker"); 58 | add_exe_with_common("script2txt"); 59 | add_exe_with_common("txt2script"); 60 | add_exe_with_common("tm_converter"); 61 | add_exe_with_common("tm_converter2") += 62 | "org.sw.demo.BenjaminDobell.s3tc_dxt_decompression-master"_dep, 63 | "org.sw.demo.intel.opencv.highgui"_dep 64 | ; 65 | add_exe("name_generator"); 66 | add_exe_with_common("save_loader"); 67 | add_exe_with_common("bms_converter"); 68 | add_exe_with_common("rgb_converter"); 69 | add_exe_with_common("unpaker") += 70 | "org.sw.demo.oberhumer.lzo.lzo"_dep, 71 | "org.sw.demo.xz_utils.lzma"_dep 72 | ; 73 | 74 | // not so simple targets 75 | auto &model = tools.addStaticLibrary("model"); 76 | { 77 | model += cppstd; 78 | model.setRootDirectory("src/model"); 79 | model.Public += common, 80 | "org.sw.demo.unicode.icu.i18n"_dep, 81 | "org.sw.demo.eigen"_dep, 82 | "pub.egorpugin.primitives.yaml"_dep, 83 | "pub.egorpugin.primitives.sw.settings"_dep 84 | ; 85 | } 86 | 87 | add_exe_with_common("mod_converter2") += model; 88 | 89 | auto &language_switcher = tools.addExecutable("aim1.language_switcher"); 90 | { 91 | auto &t = language_switcher; 92 | t += cppstd; 93 | t += "src/aim1_language_switcher/.*"_rr; 94 | t += "UNICODE"_def; 95 | t += "gdi32.lib"_slib, "ole32.lib"_slib, "user32.lib"_slib; 96 | if (auto L = t.getSelectedTool()->as(); L) 97 | L->Subsystem = vs::Subsystem::Windows; 98 | } 99 | 100 | auto &aim1_mod_activator = add_exe_with_common("aim1.mod_activator", "aim1_mod_activator"); 101 | aim1_mod_activator += "pub.egorpugin.primitives.pack"_dep; 102 | 103 | auto &aim1_mod_maker = add_exe_with_common("aim1.mod_maker", "aim1_mod_maker"); // actually a library 104 | aim1_mod_maker.Public += "pub.egorpugin.primitives.command"_dep; 105 | aim1_mod_maker.Public += "org.sw.demo.nlohmann.json.natvis"_dep; 106 | aim1_mod_maker.Interface.ForceIncludeFiles.push_back("aim1_mod_maker.h"); 107 | 108 | auto &aim1_mod_maker_injections = aim1_mod_maker.addStaticLibrary("injections"); 109 | aim1_mod_maker_injections.setRootDirectory("src/aim1_mod_maker"); 110 | aim1_mod_maker_injections += "aim\\.exe.*"_rr; 111 | 112 | auto &aim1_community_fix = tools.addExecutable("examples.mods.aim1.community_fix"); 113 | { 114 | auto &t = aim1_community_fix; 115 | t.PackageDefinitions = true; 116 | t += cppstd; 117 | t += "examples/mods/aim1_community_fix/.*"_rr; 118 | t += aim1_mod_maker; 119 | } 120 | 121 | add_exe("mod_reader") += model; 122 | 123 | path sdk = "d:/arh/apps/Autodesk/FBX/FBX SDK/2019.0"; 124 | if (fs::exists(sdk)) { 125 | auto &mod_converter = add_exe("mod_converter"); 126 | mod_converter += model; 127 | mod_converter += "org.sw.demo.xmlsoft.libxml2"_dep; // fbx 2020 sdk requires libxml2 128 | mod_converter += IncludeDirectory(sdk / "include"); 129 | String cfg = "release"; 130 | if (mod_converter.getBuildSettings().Native.ConfigurationType == ConfigurationType::Debug) 131 | cfg = "debug"; 132 | String arch = "x64"; 133 | if (mod_converter.getBuildSettings().TargetOS.Arch == ArchType::x86) 134 | arch = "x86"; 135 | String md = "md"; 136 | if (mod_converter.getBuildSettings().Native.MT) 137 | md = "mt"; 138 | mod_converter += LinkLibrary(sdk / ("lib/vs2015/" + arch + "/" + cfg + "/libfbxsdk-" + md + ".lib")); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/mmp_extractor/mmp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mmp_extractor 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | using Height = float; 34 | 35 | enum class HeaderSegmentType : uint32_t 36 | { 37 | //unk0 = 0, 38 | water = 1, 39 | weather = 2, 40 | }; 41 | 42 | struct header_segment 43 | { 44 | HeaderSegmentType type; 45 | uint32_t unk0; // version? 46 | uint32_t len; 47 | 48 | virtual void load(const buffer &b) = 0; 49 | }; 50 | 51 | struct unk_segment : public header_segment { 52 | std::string name; 53 | 54 | void load(const buffer &b) override { 55 | uint32_t unk0[6]; 56 | 57 | READ(b, unk0); 58 | READ_STRING_N(b, name, 0xA0); 59 | } 60 | }; 61 | 62 | struct water_segment : public header_segment 63 | { 64 | water_group wg; 65 | 66 | void load(const buffer &b) override; 67 | }; 68 | 69 | struct weather_segment : public header_segment 70 | { 71 | weather_group wg; 72 | 73 | void load(const buffer &b) override { 74 | wg.load(b); 75 | } 76 | }; 77 | 78 | struct header 79 | { 80 | enum class ver : uint32_t { 81 | aim12 = 0x100, 82 | aim_racing = 0x101, 83 | }; 84 | 85 | ver version; 86 | std::wstring name1; 87 | std::wstring name2; 88 | uint32_t width; 89 | uint32_t length; 90 | uint32_t n_header_segs; 91 | std::string name; // default horizont name? 92 | std::vector segments; 93 | 94 | void load(const buffer &b); 95 | 96 | private: 97 | header_segment *create_segment(const buffer &b); 98 | }; 99 | 100 | // see https://docs.unrealengine.com/latest/INT/Engine/Landscape/TechnicalGuide/index.html 101 | struct segment 102 | { 103 | static const int len = 65; 104 | static const int size = len * len; 105 | 106 | static const int mini_len = 33; 107 | static const int mini_size = mini_len * mini_len; 108 | 109 | struct description 110 | { 111 | vector3f min; 112 | vector3f max; 113 | float unk0[5]; 114 | uint32_t unk1[7]; 115 | }; 116 | 117 | struct info 118 | { 119 | uint16_t render_flags; 120 | uint16_t texture_index; 121 | 122 | uint16_t getTexture() const { return texture_index & 0x0fff; } // first 4 bits are unk (flags?) 123 | }; 124 | 125 | using shadow = color; 126 | 127 | struct normal 128 | { 129 | int16_t x; 130 | int16_t y; 131 | }; 132 | 133 | struct flagged_heightmap 134 | { 135 | uint16_t height; 136 | uint16_t flag; 137 | }; 138 | 139 | struct mini_lod 140 | { 141 | flagged_heightmap Heightmap[mini_size]; 142 | color Colormap[mini_size]; 143 | uint32_t unk0[mini_size]; // shadowmap? 144 | normal unk1[mini_size]; // normals? 145 | }; 146 | 147 | struct data 148 | { 149 | uint32_t MagicNumber; 150 | mini_lod mlod; 151 | Height Heightmap[size]; 152 | info Infomap[size]; 153 | color Colormap[size]; // diffuse color of material 154 | shadow Shadowmap[size]; 155 | normal Normalmap[size]; 156 | }; 157 | 158 | struct mini_lod2 159 | { 160 | flagged_heightmap Heightmap[mini_len][mini_len]; 161 | color Colormap[mini_len][mini_len]; 162 | uint32_t unk0[mini_len][mini_len]; // shadowmap? 163 | normal unk1[mini_len][mini_len]; // normals? 164 | }; 165 | 166 | struct data2 167 | { 168 | uint32_t MagicNumber; 169 | mini_lod2 mlod; 170 | Height Heightmap[len][len]; 171 | info Infomap[len][len]; 172 | color Colormap[len][len]; 173 | shadow Shadowmap[len][len]; 174 | normal Normalmap[len][len]; 175 | }; 176 | 177 | description desc; 178 | data d; 179 | data2 d2; 180 | 181 | void load(const buffer &b); 182 | }; 183 | 184 | struct mmp 185 | { 186 | header h; 187 | std::vector segments; 188 | 189 | // 190 | path filename; 191 | int xsegs; 192 | int ysegs; 193 | std::map textures; 194 | std::map textures_map; 195 | std::map> alpha_maps; 196 | std::map textures_map_colored; 197 | std::map textures_names; 198 | Height h_min = 0; 199 | Height h_max = 0; 200 | double ue4_z_scale = 0; 201 | mat heightmap; 202 | mat heightmap32; 203 | //mat heightmap_segmented; 204 | mat texmap; 205 | mat texmap_colored; 206 | mat colormap; 207 | mat shadowmap; 208 | mat normalmap; 209 | 210 | void load(const buffer &b); 211 | void load(const path &filename); 212 | void loadTextureNames(const path &filename); 213 | 214 | void process(); 215 | 216 | void writeFileInfo(); 217 | void writeTexturesList(); 218 | void writeHeightMap(); 219 | void writeHeightMapSegmented(); 220 | void writeTextureMap(); 221 | void writeTextureAlphaMaps(); 222 | void writeTextureMapColored(); 223 | void writeColorMap(); 224 | void writeShadowMap(); 225 | void writeNormalMap(); 226 | void writeSplitColormap() const; 227 | }; 228 | -------------------------------------------------------------------------------- /src/mmo_extractor/other.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM obj_extractor 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | // if we move this inside MechGroup, compilation error - default ctor is deleted 33 | struct MechGroup_unk_type01 34 | { 35 | uint32_t unk0 = 0; 36 | float unk1 = 0; 37 | }; 38 | 39 | struct MechGroup 40 | { 41 | std::string name; 42 | std::string org; 43 | std::vector mechanoids; 44 | 45 | // used only in m1.loc1 and for sinigr 46 | // probably an old field 47 | std::string org_ru; 48 | 49 | std::variant, uint32_t> type_data; 50 | 51 | bool hidden; 52 | 53 | void load(const buffer &b) 54 | { 55 | READ_STRING(b, name); 56 | READ_STRING(b, org); 57 | uint32_t type = 0; 58 | READ(b, type); 59 | uint32_t number_of_mechanoids = 0; 60 | READ(b, number_of_mechanoids); 61 | READ_STRING_N(b, org_ru, 0x70); 62 | 63 | switch (type) 64 | { 65 | case 0: 66 | case 1: 67 | { 68 | MechGroup_unk_type01 t; 69 | READ(b, t.unk0); 70 | READ(b, t.unk1); 71 | type_data = t; 72 | } 73 | break; 74 | case 2: 75 | { 76 | std::vector t; 77 | uint32_t len = 0; 78 | READ(b, len); 79 | t.resize(len); 80 | for (uint32_t i = 0; i < len; i++) 81 | READ(b, t[i]); 82 | type_data = t; 83 | } 84 | break; 85 | case 3: // 3 = free mechanoids only? 86 | case 4: 87 | { 88 | uint32_t t; 89 | READ(b, t); 90 | type_data = t; 91 | } 92 | break; 93 | default: 94 | assert(false); 95 | } 96 | 97 | for (uint32_t i = 0; i < number_of_mechanoids; i++) 98 | { 99 | std::string t; 100 | READ_STRING_N(b, t, 0x20); 101 | mechanoids.push_back(t); 102 | } 103 | READ(b, hidden); 104 | } 105 | }; 106 | 107 | struct MechGroups 108 | { 109 | std::vector mechGroups; 110 | char unk0[0x30]; // prefix? 111 | 112 | void load(const buffer &b) 113 | { 114 | if (gameType == GameType::Aim2) 115 | { 116 | uint32_t length = 0; 117 | READ(b, length); 118 | } 119 | uint32_t n = 0; 120 | READ(b, n); 121 | READ(b, unk0); 122 | 123 | for (uint32_t s = 0; s < n; s++) 124 | { 125 | MechGroup mg; 126 | mg.load(b); 127 | mechGroups.push_back(mg); 128 | } 129 | } 130 | }; 131 | 132 | struct MapGoods 133 | { 134 | uint32_t unk2 = 0; 135 | uint32_t unk3 = 0; 136 | 137 | std::vector bgs; 138 | 139 | void load(const buffer &b) 140 | { 141 | uint32_t length = 0; 142 | READ(b, length); 143 | READ(b, unk2); 144 | if (gameType != GameType::Aim2) 145 | READ(b, unk3); 146 | 147 | uint32_t n = 0; 148 | READ(b, n); 149 | for (uint32_t i = 0; i < n; i++) 150 | { 151 | BuildingGoods bg; 152 | bg.load(b); 153 | bgs.push_back(bg); 154 | if (gameType == GameType::Aim2) 155 | READ(b, unk2); 156 | } 157 | } 158 | }; 159 | 160 | struct MapSound 161 | { 162 | std::string name; 163 | float unk1[4]; 164 | uint32_t unk2 = 0; 165 | float unk3[4]; 166 | 167 | void load(const buffer &b) 168 | { 169 | READ_STRING(b, name); 170 | READ(b, unk1); 171 | READ(b, unk2); 172 | READ(b, unk3); 173 | } 174 | }; 175 | 176 | struct MapSounds 177 | { 178 | std::vector sounds; 179 | 180 | void load(const buffer &b) 181 | { 182 | b.read_vector(sounds); 183 | } 184 | }; 185 | 186 | struct Price 187 | { 188 | enum class ItemType : uint32_t 189 | { 190 | Glider = 1, 191 | Equipment, 192 | Weapon, 193 | Ammo, 194 | }; 195 | 196 | std::string tov_name; 197 | ItemType type; 198 | ModificatorMask mask; 199 | float price; 200 | float unk2 = 0.0f; // count ? 201 | float probability; // of appearence 202 | 203 | void load(const buffer &b) 204 | { 205 | READ_STRING(b, tov_name); 206 | READ(b, type); 207 | READ(b, mask); 208 | READ(b, price); 209 | READ(b, unk2); 210 | READ(b, probability); 211 | } 212 | }; 213 | 214 | struct BuildingPrice 215 | { 216 | std::string name; 217 | std::vector prices; 218 | 219 | void load(const buffer &b) 220 | { 221 | READ_STRING(b, name); 222 | b.read_vector(prices); 223 | } 224 | }; 225 | 226 | struct BuildingPrices 227 | { 228 | std::vector prices; 229 | std::vector buildingPrices; 230 | 231 | void load(const buffer &b) 232 | { 233 | b.read_vector(prices); 234 | b.read_vector(buildingPrices); 235 | } 236 | }; 237 | 238 | struct Prices 239 | { 240 | uint32_t unk0 = 0; 241 | BuildingPrices buildingPrices; 242 | 243 | void load(const buffer &b) 244 | { 245 | uint32_t len = 0; 246 | READ(b, len); 247 | READ(b, unk0); 248 | buildingPrices.load(b); 249 | } 250 | }; 251 | -------------------------------------------------------------------------------- /src/bms_converter/bms_converter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM bms_converter (0.03 demo binary model files) 3 | * Copyright (C) 2024 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "mmap.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #pragma pack(push, 1) 34 | struct bms { 35 | struct header { 36 | uint32_t magic; 37 | uint32_t version; 38 | uint32_t n_animations; 39 | uint32_t unk; 40 | }; 41 | struct animation_header { 42 | uint32_t unk1; // type? 43 | uint32_t n_anim1; 44 | uint32_t n_anim2; 45 | }; 46 | struct vec2 { 47 | float x, y; 48 | void operator+=(const vec2 &v) { 49 | x += v.x; 50 | y += v.y; 51 | } 52 | }; 53 | struct vec3 : vec2 { 54 | float z; 55 | void operator+=(const vec3 &v) { 56 | vec2::operator+=(v); 57 | z += v.z; 58 | } 59 | }; 60 | struct v : vec3 {}; 61 | struct vn : vec3 {}; 62 | struct vt : vec2 {}; 63 | struct vertex { 64 | v coords; 65 | vn normal; 66 | vt tex; 67 | }; 68 | using vertex_id = uint16_t; 69 | struct animation_data { 70 | vertex_id id; 71 | v coords; 72 | vn normal; 73 | }; 74 | using charC = char[0xC]; 75 | using animation_name = charC; 76 | 77 | static void convert_model(const path &fn) { 78 | primitives::templates2::mmap_file f{fn}; 79 | stream s{f}; 80 | 81 | header h = s; 82 | auto animation_names = s.span(h.n_animations); 83 | uint32_t unk1 = s; 84 | auto animations = s.span(h.n_animations); 85 | uint32_t n_vertices = s; 86 | uint32_t n_faces = s; 87 | auto vertices_read = s.span(n_vertices); 88 | auto faces = s.span(n_faces); 89 | 90 | std::vector vertices(vertices_read.begin(), vertices_read.end()); 91 | auto print = [&faces](const path &fn, auto &&vertices) { 92 | std::ofstream ofile{path{fn} += ".obj"}; 93 | for (auto &&v : vertices) { 94 | ofile << std::format("v {} {} {}\n", v.coords.x, v.coords.y, v.coords.z); 95 | } 96 | ofile << "\n"; 97 | for (auto &&v : vertices) { 98 | ofile << std::format("vt {} {}\n", v.tex.x, v.tex.y); 99 | } 100 | ofile << "\n"; 101 | for (auto &&v : vertices) { 102 | ofile << std::format("vn {} {} {}\n", v.normal.x, v.normal.y, v.normal.z); 103 | } 104 | ofile << "\n"; 105 | // ofile << "g obj\n"; 106 | // ofile << "s 1\n"; 107 | for (auto &&f : std::views::chunk(faces, 3)) { 108 | ofile << std::format("f {}/{}/{} {}/{}/{} {}/{}/{}\n" 109 | , f[0] + 1, f[0] + 1, f[0] + 1 110 | , f[1] + 1, f[1] + 1, f[1] + 1 111 | , f[2] + 1, f[2] + 1, f[2] + 1 112 | ); 113 | } 114 | // ofile << "g\n"; 115 | }; 116 | auto apply_animation = [&](auto &&anim) { 117 | for (auto &&a : anim) { 118 | auto &v = vertices[a.id]; 119 | v.coords += a.coords; 120 | v.normal += a.normal; 121 | } 122 | }; 123 | 124 | print(path{fn}, vertices); 125 | for (int id = 0; auto &&d : animations) { 126 | vertices.assign(vertices_read.begin(), vertices_read.end()); 127 | auto anim1 = s.span(d.n_anim1); 128 | apply_animation(anim1); 129 | print(path{fn} += std::format(".anim.{}.1", animation_names[id]), vertices); 130 | 131 | vertices.assign(vertices_read.begin(), vertices_read.end()); 132 | auto anim2 = s.span(d.n_anim2); 133 | apply_animation(anim2); 134 | print(path{fn} += std::format(".anim.{}.2", animation_names[id]), vertices); 135 | 136 | vertices.assign(vertices_read.begin(), vertices_read.end()); 137 | apply_animation(anim1); 138 | apply_animation(anim2); 139 | print(path{fn} += std::format(".anim.{}.merged", animation_names[id]), vertices); 140 | 141 | ++id; 142 | } 143 | } 144 | }; 145 | #pragma pack(pop) 146 | 147 | int main(int argc, char *argv[]) 148 | { 149 | cl::opt p(cl::Positional, cl::desc("<.bms file or dir>"), cl::Required); 150 | 151 | cl::ParseCommandLineOptions(argc, argv); 152 | 153 | if (fs::is_regular_file(p)) { 154 | bms::convert_model(p); 155 | } else if (fs::is_directory(p)) { 156 | auto files = enumerate_files(p, false); 157 | for (auto &f : FilesSorted(files.begin(), files.end())) 158 | { 159 | if (f.extension() != ".bms") { 160 | continue; 161 | } 162 | std::cout << "processing: " << f << "\n"; 163 | try 164 | { 165 | bms::convert_model(f); 166 | } 167 | catch (std::exception &e) 168 | { 169 | std::cout << "error: " << e.what() << "\n"; 170 | } 171 | } 172 | } 173 | else 174 | throw std::runtime_error("No such file or directory: " + to_printable_string(normalize_path(p))); 175 | 176 | return 0; 177 | } 178 | -------------------------------------------------------------------------------- /src/tm_converter2/tm_converter2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM tm_converter 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #ifdef _WIN32 40 | #include 41 | #endif 42 | 43 | /* 44 | aim icons 45 | TEX_KT_ICONS.TM 46 | 64 x 32 47 | 16 icon in a column 48 | */ 49 | 50 | // TODO: add dxt5 compressor from stb libs (stb_dxt5) 51 | // see org.sw.demo.stb.all 52 | 53 | using namespace std; 54 | 55 | #pragma pack(push, 1) 56 | struct tm_file { 57 | int32_t width; 58 | int32_t height; 59 | // https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dformat 60 | #ifdef _WIN32 61 | D3DFORMAT d3dformat; 62 | #else 63 | uint32_t d3dformat; 64 | #endif 65 | // see for some infos https://learn.microsoft.com/en-us/previous-versions/ms889290(v=msdn.10) 66 | // IDirect3DDevice8::CreateTexture 67 | // or not? or number of helper images or total imgs 68 | uint32_t levels; 69 | // or not only dxt5? maybe enum? 70 | uint32_t dxt5_compression; 71 | // TEX_NIGHT13.TM has 1 here 72 | // TEX_kt_icons.TM has 8 here 73 | // maybe levels? 74 | // number of anims? icon variants? 75 | uint32_t unk0; 76 | uint8_t unkX[0x4C-0x18]; 77 | }; 78 | #pragma pack(pop) 79 | 80 | struct kt_icons { 81 | // width 64 82 | // height 32 83 | // icons in column 16 84 | }; 85 | 86 | void convert(const path &fn) { 87 | primitives::templates2::mmap_file f{fn}; 88 | stream s{f}; 89 | tm_file tm = s; 90 | 91 | // format check 92 | switch (tm.d3dformat) { 93 | case D3DFMT_A8R8G8B8: 94 | break; 95 | case D3DFMT_X8R8G8B8: 96 | break; 97 | default: 98 | SW_UNIMPLEMENTED; 99 | } 100 | 101 | // compression check 102 | switch (tm.dxt5_compression) { 103 | case 0: 104 | break; 105 | case 1: 106 | break; 107 | default: 108 | SW_UNIMPLEMENTED; 109 | } 110 | 111 | auto save = [&](auto &&sub) { 112 | cv::Mat m(tm.height, tm.width, CV_8UC4); 113 | if (tm.dxt5_compression) { 114 | // this have some one off results 115 | // probaby we did different rounding in current (v1) impl 116 | BlockDecompressImageDXT5(tm.width, tm.height, s.p, (unsigned long *)m.data); 117 | s.skip(tm.width * tm.height); 118 | // only for dxt5 119 | m.forEach([](auto &v, auto *pos) { 120 | // ARGB -> BGRA (big endian) 121 | // but in bytes (little endian) 122 | // 0xBGRA -> 0xABGR 123 | auto &val = *(uint32_t *)&v; 124 | val = std::rotr(val, 8); 125 | }); 126 | if (tm.d3dformat == D3DFMT_X8R8G8B8) { 127 | // and shrink if possible 128 | // dont shrink? we need alpha for compression again? 129 | //cv::cvtColor(m, m, cv::COLOR_BGRA2BGR); 130 | } 131 | } 132 | else 133 | { 134 | // 'simple' files consists of: 135 | // 1. 'normal' image (bitmap) 136 | // 2. some kind of mask? 137 | // 3. rest? levels? 138 | // but every tm has multiple levels or so 139 | 140 | int size = tm.width * tm.height * 2; 141 | auto dst = m.data; 142 | for (int i = 0; i < size; ++i) { 143 | uint8_t c = s; 144 | uint8_t lo = c & 0x0F; 145 | uint8_t hi = (c & 0xF0) >> 4; 146 | *dst++ = (lo << 4) | lo; 147 | *dst++ = (hi << 4) | hi; 148 | } 149 | 150 | // extract icons 151 | //cv::Mat m2(m,cv::Rect(0,0,64,32)); 152 | } 153 | // opencv can't save to tga directly 154 | cv::imwrite((path(fn) += sub + ".png"s).string(), m); 155 | }; 156 | save(""); 157 | // not sure how to parse rest, probably dx8 generated texture levels 158 | // and we dont need them 159 | return; 160 | 161 | if (!tm.dxt5_compression) { 162 | save("_mask"); // what is it? 163 | } 164 | for (int i = 0; i < tm.unk0; ++i) { 165 | // obviosly small 166 | tm.width /= 2; 167 | tm.height /= 2; 168 | save(std::format("_small{}", i + 1)); 169 | if (!tm.dxt5_compression) { 170 | save(std::format("_small{}_mask", i + 1)); 171 | } 172 | } 173 | 174 | if (s.p - f.p != f.sz) { 175 | int a = 5; 176 | a++; 177 | SW_UNIMPLEMENTED; 178 | } 179 | } 180 | 181 | int main(int argc, char *argv[]) 182 | { 183 | cl::list list(cl::Positional, cl::desc(""), cl::Required, cl::OneOrMore); 184 | 185 | cl::ParseCommandLineOptions(argc, argv); 186 | 187 | for (auto &&p : list) { 188 | if (fs::is_regular_file(p)) { 189 | convert(p); 190 | } else if (fs::is_directory(p)) { 191 | auto files = enumerate_files_like(p, ".*\\.TM", false); 192 | for (auto &f : files) { 193 | std::cout << "processing: " << to_printable_string(f) << "\n"; 194 | convert(f); 195 | } 196 | } else { 197 | throw std::runtime_error("Bad fs object"); 198 | } 199 | } 200 | return 0; 201 | } 202 | -------------------------------------------------------------------------------- /src/common/mmo2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mmap.h" 4 | 5 | struct mmo_storage2 { 6 | struct object { 7 | uint32_t type; 8 | uint32_t size; 9 | uint32_t n_objects; 10 | }; 11 | struct mech_group { 12 | enum type { 13 | stationary, // or default or guard? 14 | guard = stationary, 15 | trader, 16 | hunter, 17 | free, 18 | people = free, 19 | companion, 20 | }; 21 | 22 | char name[0x20]; 23 | char org[0x20]; 24 | // probably state 25 | // alive 26 | // dead 27 | // in the base 28 | // in the other mech cargo 29 | uint32_t type; 30 | uint32_t n_mechs; 31 | char comment[0x70]; // icon???? 32 | uint32_t value1; 33 | }; 34 | struct map_good { 35 | char name[0x20]; 36 | char cond[0x40]; 37 | float buy_price; 38 | float sell_price; 39 | float unk[8]; 40 | uint32_t unk1; 41 | 42 | map_good() = default; 43 | map_good(const std::string &name, const std::string &condition = {}) { 44 | *this = map_good{}; 45 | strcpy(this->name, name.c_str()); 46 | strcpy(this->cond, condition.c_str()); 47 | } 48 | }; 49 | 50 | // our own data 51 | struct section { 52 | uint32_t offset{}; 53 | uint32_t size{}; 54 | 55 | void end(auto v) { 56 | size = v - offset; 57 | } 58 | }; 59 | struct { 60 | section objects; 61 | section mech_groups; 62 | section map_goods; 63 | section music_and_sounds; 64 | } sections; 65 | struct map_building { 66 | uint32_t offset; 67 | std::map building_goods; 68 | }; 69 | struct mech { 70 | std::string name; 71 | std::string org; 72 | std::string comment; 73 | uint32_t name_offset; 74 | uint32_t n_mechs_offset; 75 | uint32_t mechs_offset; 76 | uint32_t post_base_offset; 77 | uint32_t offset; 78 | uint32_t size; 79 | }; 80 | path fn; 81 | uint32_t n_mech_groups_offset; 82 | uint32_t mech_groups_offset; 83 | std::map mechs; 84 | std::map map_building_goods; 85 | 86 | mmo_storage2(const path &fn) : fn{fn} { 87 | } 88 | void load() { 89 | primitives::templates2::mmap_file f{fn}; 90 | stream s{f}; 91 | // objects 92 | { 93 | sections.objects.offset = s.p - f.p; 94 | uint32_t n_segments = s; 95 | while (n_segments--) { 96 | object o = s; 97 | s.skip(o.size); 98 | } 99 | sections.objects.end(s.p - f.p); 100 | } 101 | // mech_groups 102 | { 103 | sections.mech_groups.offset = s.p - f.p; 104 | n_mech_groups_offset = s.p - f.p; 105 | uint32_t n_mech_groups = s; 106 | // two ints 107 | // and ten more ints? 108 | s.skip(0x30); 109 | mech_groups_offset = s.p - f.p; 110 | while (n_mech_groups--) { 111 | auto start = s.p - f.p; 112 | mech_group &o = s; 113 | 114 | auto &m = mechs[o.name]; 115 | m.name = o.name; 116 | m.org = o.org; 117 | m.comment = o.comment; 118 | m.name_offset = (uint8_t*)o.name - f.p; 119 | m.n_mechs_offset = (uint8_t*)&o.n_mechs - f.p; 120 | m.post_base_offset = (uint8_t*)s.p - f.p; 121 | 122 | if (strcmp(o.name, "MINVACH-6") == 0) { 123 | int a = 5; 124 | a++; 125 | } 126 | if (strcmp(o.name, "DEKON") == 0) { 127 | int a = 5; 128 | a++; 129 | } 130 | if (strcmp(o.name, "ETRANNA-1") == 0) { 131 | int a = 5; 132 | a++; 133 | } 134 | if (strcmp(o.name, "SHUN-2") == 0) { 135 | int a = 5; 136 | a++; 137 | } 138 | 139 | // mech are spawned on random path when leaving the base 140 | // probably far from player 141 | // 142 | // 0 = stationary 143 | // 0 = CGuardMechGroup? 144 | // 1 = roaming between bases (and trading?) 145 | // 1 = CTransMechGroup! 146 | // 2 = patrol? or roaming and killing? 147 | // 2 = CHunterMechGroup! 148 | // 3 = ? free mechs? spawn pos? flag? free roaming? always 0 except single ORG_FREE mob in location5 149 | // 3 = CPeopleMechGroup 150 | // 4 = ? platforms? spawn pos? probably only single appearence in the game - location2 GL_PLATFORM 151 | // 4 = CGuardMechGroup? 152 | switch (o.type) { 153 | case mech_group::stationary: // alive 154 | { 155 | float height = s; // height? 156 | 157 | int a = 5; 158 | a++; 159 | } break; 160 | case mech_group::trader: { 161 | float unused_guessed = s; // height? 162 | 163 | int a = 5; 164 | a++; 165 | } break; 166 | // and seems pathes are generated ingame between bases 167 | case mech_group::hunter: { 168 | std::vector t; // road path 169 | t.resize(o.value1); 170 | s.read(t); 171 | } break; 172 | case mech_group::people: 173 | break; 174 | case mech_group::companion: 175 | break; 176 | default: 177 | assert(false); 178 | } 179 | 180 | auto n = o.n_mechs; 181 | m.mechs_offset = s.p - f.p; 182 | while (n--) { 183 | struct mech { 184 | char cfg_name[0x20]; 185 | }; 186 | mech m = s; 187 | } 188 | bool hidden = s; 189 | m.offset = start; 190 | m.size = (s.p - f.p) - start; 191 | } 192 | sections.mech_groups.end(s.p - f.p); 193 | } 194 | // map goods 195 | { 196 | sections.map_goods.offset = s.p - f.p; 197 | uint32_t size = s; 198 | uint32_t unk2 = s; 199 | float unk3 = s; 200 | uint32_t n_buildings = s; 201 | while (n_buildings--) { 202 | struct bld { 203 | char name[0x20]; 204 | }; 205 | bld b = s; 206 | map_building_goods[b.name].offset = s.p - f.p; 207 | uint32_t n_goods = s; 208 | while (n_goods--) { 209 | map_good g = s; 210 | map_building_goods[b.name].building_goods[g.name] = s.p - f.p; 211 | } 212 | } 213 | sections.map_goods.end(s.p - f.p); 214 | } 215 | // music & sounds section 216 | { 217 | sections.music_and_sounds.offset = s.p - f.p; 218 | uint32_t size = s; 219 | s.skip(size); 220 | sections.music_and_sounds.end(s.p - f.p); 221 | } 222 | } 223 | }; 224 | -------------------------------------------------------------------------------- /src/mmp_extractor/mmp_tex_aim1.txt: -------------------------------------------------------------------------------- 1 | 276 TEX_VERT_12 2 | 284 TEX_LAND108 3 | 285 TEX_PESOK_8 4 | 288 TEX_6_8-2 5 | 289 TEX_VERT5 6 | 291 TEX_VERT_1 7 | 292 TEX_TRAVA-8 8 | 293 TEX_16_8 9 | 299 TEX_PLITA8 10 | 314 TEX_SNOW2_8 11 | 321 TEX_SNOW3_8 12 | 321 TEX_SNOW3_8 13 | 325 TEX_SNOW4_8 14 | 345 TEX_SNOW_VERT_1 15 | 352 TEX_SNOW_VERT2 16 | 428 TEX_ROADS_8 17 | 437 TEX_ICE_8 18 | 455 TEX_MOH_BROUN_8 19 | 493 TEX_BEREG 20 | 502 TEX_BEREG_2 21 | 511 TEX_GLINA_8_2 22 | 520 TEX_LAVA_8_2 23 | 523 TEX_PESOK_V_8 24 | 532 TEX_BASALT_8 25 | 549 TEX_LAVA1_8 26 | 558 TEX_LAND_S_8 27 | 564 TEX_FUND8 28 | 567 TEX_VERT_LAVA_1 29 | 576 TEX_LAVA_2_8 30 | 585 TEX_VERT_LAVA_2 31 | 598 TEX_LAND_BLACK_8 32 | 607 TEX_TRAVA-8 33 | 616 TEX_SWAMP_8 34 | 625 TEX_SWAMP_2_8 35 | 634 TEX_LAND_3_8 36 | 643 TEX_CRYSTALL_8 37 | 652 TEX_ROAD_BROUNE_8 38 | 661 TEX_CRYST_8 39 | 670 TEX_16_8 40 | 690 TEX_6_8-2 41 | 699 TEX_LAND_V_8 42 | 708 TEX_LAND_V_8 43 | 736 TEX_PESOK_8 44 | 745 TEX_KORDILYER 45 | 785 TEX_PESOK_8 46 | 794 TEX_L3_ROAD_8 47 | 797 TEX_ROCKLAND_2 48 | 824 TEX_TRAVA-8 49 | 833 TEX_ROCK_LAND 50 | 841 TEX_SWAMP_8 51 | 844 TEX_IKRA-8 52 | 847 TEX_SNOW_VERT_3 53 | 850 TEX_VERT_SNOW_2 54 | 853 TEX_VERT_SNOW_3 55 | 856 TEX_L4_ROAD_8 56 | 859 TEX_TRAVA-8 57 | 862 TEX_TRAVA-8 58 | 864 TEX_16_8 59 | 868 TEX_PESOK_8 60 | 908 TEX_VERT_1 61 | 914 TEX_PESOK_8 62 | 927 TEX_BEREG 63 | 933 TEX_GLINA_8_2 64 | 943 TEX_L5_DUNA_81 65 | 953 TEX_PESOK_8 66 | 957 TEX_PESOK_8 67 | 960 TEX_PESOK_8 68 | 981 TEX_L5_DUNA_81 69 | 984 TEX_VERTIKAL 70 | 987 TEX_L5_DUNA_81 71 | 990 TEX_GLINA_8_2 72 | 999 TEX_LAND108 73 | 1002 TEX_L5_DUNA_8 74 | 1005 TEX_L5_DUNA_81 75 | 1014 TEX_L5_STONE_DUNA 76 | 1026 TEX_PESOK_8 77 | 1034 TEX_L5_DUNA_81 78 | 1037 TEX_L5_DUNA_81 79 | 1049 TEX_PESOK_8 80 | 1052 TEX_L5_ROAD_8 81 | 1056 TEX_PESOK_8 82 | 1067 TEX_PESOK_8 83 | 1070 TEX_PESOK_8 84 | 1079 TEX_LAND108 85 | 1082 TEX_LAND108 86 | 1089 TEX_PESOK_8 87 | 1092 TEX_PESOK_8 88 | 1095 TEX_BEREG_2 89 | 1104 TEX_GLINA_8_2 90 | 1110 TEX_LAVA_8_2 91 | 1123 TEX_L6_SAND_1 92 | 1129 TEX_LAND_V_8 93 | 1149 TEX_SWAMP_2_8 94 | 1153 TEX_SWAMP_2_8 95 | 1159 TEX_SWAMP_2_8 96 | 1166 TEX_SWAMP_2_8 97 | 1168 TEX_L3_ROAD_8 98 | 1171 TEX_SWAMP_2_8 99 | 1174 TEX_SWAMP_2_8 100 | 1180 TEX_BEREG 101 | 1183 TEX_SWAMP_2_8 102 | 1186 TEX_GLINA_8_2 103 | 1189 TEX_SWAMP_2_8 104 | 1198 TEX_SWAMP_2_8 105 | 1206 TEX_SWAMP_2_8 106 | 1213 TEX_LAND_3_8 107 | 1216 TEX_SWAMP_2_8 108 | 1225 TEX_L6_SAND_1 109 | 1229 TEX_VERTIKAL 110 | 1235 TEX_ROCKLAND_2 111 | 1245 TEX_BEREG_2 112 | 1248 TEX_VERTIKAL 113 | 1251 TEX_SWAMP_2_8 114 | 1254 TEX_SWAMP_2_8 115 | 1256 TEX_CRYST_8 116 | 1259 TEX_PLITA_8 117 | 1268 TEX_TRAVA 118 | 1279 TEX_L3_ROAD_8 119 | 1286 TEX_L3_ROAD_8 120 | 1292 TEX_SWAMP_2_8 121 | 1295 TEX_TRAVA 122 | 1299 TEX_L6_SAND_1 123 | 1308 TEX_L6_SAND_1 124 | 1311 TEX_L6_SAND_1 125 | 1316 TEX_L6_SAND_1 126 | 1319 TEX_TRAVA 127 | 1326 TEX_16_8 128 | 1337 TEX_PESOK_8 129 | 1340 TEX_PLITA_BLD_8 130 | 1343 TEX_L3_ROAD_8 131 | 1346 TEX_PLITA_BETON 132 | 1355 TEX_PLITA_8 133 | 1358 TEX_PESOK_8 134 | 1363 TEX_SWAMP_2_8 135 | 1371 TEX_SWAMP_2_8 136 | 1382 TEX_SWAMP_2_8 137 | 1385 TEX_SWAMP_2_8 138 | 1400 TEX_SWAMP_2_8 139 | 1410 TEX_SWAMP_2_8 140 | 1420 TEX_SWAMP_2_8 141 | 1423 TEX_SWAMP_2_8 142 | 1432 TEX_MOH_BROUN_8 143 | 1438 TEX_SWAMP_2_8 144 | 1440 TEX_SWAMP_2_8 145 | 1444 TEX_SWAMP_2_8 146 | 1447 TEX_GLINA_8_2 147 | 1458 TEX_SWAMP_2_8 148 | 1469 TEX_SWAMP_2_8 149 | 1472 TEX_SWAMP_2_8 150 | 1475 TEX_GLINA_8_2 151 | 1484 TEX_SWAMP_2_8 152 | 1488 TEX_SWAMP_2_8 153 | 1493 TEX_SWAMP_2_8 154 | 1504 TEX_GLINA_8_2 155 | 1510 TEX_SWAMP_2_8 156 | 1513 TEX_MOH_BROUN_8 157 | 1522 TEX_BASALT_8 158 | 1528 TEX_BASALT_8 159 | 1531 TEX_MAGMA_8 160 | 1543 TEX_LAVA_2_8 161 | 1552 TEX_LAVA1_8 162 | 1558 TEX_LAND_S_8 163 | 1569 TEX_PLITA_BETON 164 | 1575 TEX_CRYSTALL_8 165 | 1584 TEX_SWAMP_2_8 166 | 1589 TEX_SWAMP_2_8 167 | 1598 TEX_SWAMP_2_8 168 | 1601 TEX_LAVA_2_8 169 | 1604 TEX_SWAMP_2_8 170 | 1607 TEX_SWAMP_2_8 171 | 1610 TEX_L4_PROVAL_8 172 | 1613 TEX_L4_FLASH 173 | 1615 TEX_SWAMP_2_8 174 | 1618 TEX_GLINA_8_2 175 | 1621 TEX_SWAMP_2_8 176 | 1626 TEX_SWAMP_2_8 177 | 1632 TEX_SWAMP_2_8 178 | 1643 TEX_L7_MOUTH 179 | 1646 TEX_SNOW_VERT_3 180 | 1649 TEX_CRYSTALL_8 181 | 1661 TEX_L4_LAVA_STONE_8 182 | 1664 TEX_L8_KORNI_8 183 | 1669 TEX_SWAMP_2_8 184 | 1673 TEX_L8_PIMPLE_8 185 | 1676 TEX_L8_PIMPLE_VIOLET_8 186 | 1686 TEX_CRYSTALL_8 187 | 1689 TEX_L8_PIMPLE_ORANGE_8 188 | 1698 TEX_VERT_LAVA_1 189 | 1701 TEX_L8_ROCKS 190 | 1704 TEX_L8_KORNI_8 191 | 1713 TEX_L8_PIMPLE_8 192 | 1719 TEX_L8_PIMPLE_ORANGE_8 193 | 1721 TEX_L8_PIMPLE_ORANGE_8 194 | 1724 TEX_L8_PIMPLE_VIOLET_8 195 | 1727 TEX_L8_ROOTS_8 196 | 1730 TEX_SWAMP_2_8 197 | 1733 TEX_L8_FLESH_8 198 | 1736 TEX_L8_MOULD_ROCK 199 | 1739 TEX_PESOK_8 200 | 1742 TEX_L8_ROCKS_2 201 | 1745 TEX_L8_BOWELS 202 | 1748 TEX_16_8 203 | 1758 TEX_SWAMP_2_8 204 | 1768 TEX_L6_ROAD_90_8 205 | 1771 TEX_L6_ROAD_45_8 206 | 1775 TEX_L6_ROAD_FD_8 207 | 1782 TEX_L6_SAND_1 208 | 1793 TEX_PESOK_8 209 | 1801 TEX_PESOK_8 210 | 1803 TEX_L6_SAND_1 211 | 1806 TEX_SWAMP_2_8 212 | 1809 TEX_L8_BOWELS_2 213 | 1819 TEX_SWAMP_2_8 214 | 1829 TEX_SWAMP_2_8 215 | 1831 TEX_BEREG_2 216 | 1836 TEX_SWAMP_2_8 217 | 1839 TEX_TRAVA_8_ALT 218 | 1842 TEX_SWAMP_2_8 219 | 1845 TEX_LAND_3_8 220 | 1848 TEX_SWAMP_2_8 221 | 1859 TEX_L3_ROCKS_V 222 | 1870 TEX_L5_STONE_DUNE_G_8 223 | 1879 TEX_L6_SAND_1 224 | 1885 TEX_L6_SAND_1 225 | 1888 TEX_L6_SAND_CLIFF 226 | 1891 TEX_METAL_WALL 227 | 1894 TEX_METAL_WALL_2 228 | 1903 TEX_METAL_GORIZ_8 229 | 1906 TEX_METAL_RELS_8 230 | 1909 TEX_METAL_RELS_2_8 231 | 1920 TEX_L5_BIGWORM_V 232 | 1926 TEX_PLITA_8 233 | 1937 TEX_PESOK_8 234 | 1941 TEX_SWAMP_2_8 235 | 1952 TEX_TRAVA 236 | 1955 TEX_SWAMP_2_8 237 | 1959 TEX_SWAMP_2_8 238 | 1967 TEX_SWAMP_2_8 239 | 1974 TEX_SWAMP_2_8 240 | 1980 TEX_SWAMP_2_8 241 | 1987 TEX_SWAMP_2_8 242 | 1991 TEX_L6_SAND_1 243 | 1995 TEX_SWAMP_2_8 244 | 1999 TEX_L6_SAND_1 245 | 2010 TEX_L6_SAND_1 246 | 2014 TEX_PLITA8 247 | 2024 TEX_PESOK_8 248 | 2029 TEX_SNOW3_8 249 | 2034 TEX_ROADS_8 250 | 2040 TEX_SNOW4_8 251 | 2044 TEX_SNOW2_8 252 | 2050 TEX_L3_ROCKS_V 253 | 2055 TEX_SNOW4_8 254 | 2061 TEX_ICE_8 255 | 2065 TEX_ICE_8 256 | 2070 TEX_ICE_8 257 | 2074 TEX_LAND_3_8 258 | 2076 TEX_LAND_3_8 259 | 2082 TEX_LAND_3_8 260 | 2091 TEX_L3_ROCKS_V 261 | 2101 TEX_VERT_SNOW_3 262 | 2108 TEX_LAND_3_8 263 | 2113 TEX_L6_SAND_1 264 | 2123 TEX_SWAMP_2_8 265 | 2127 TEX_L6_SAND_1 266 | 2133 TEX_L6_STONES 267 | 2140 TEX_L6_STONES 268 | 2151 TEX_PESOK_8 269 | 2155 TEX_SNOW4_8 270 | 2158 TEX_SWAMP_2_8 271 | 2163 TEX_SNOW4_8 272 | 2173 TEX_LAVA1_8 273 | 2177 TEX_LAVA1_8 274 | 2180 TEX_PLITA_BLD_8 275 | 2189 TEX_METAL_WALL_2 276 | 2195 TEX_METAL_WALL 277 | 2197 TEX_METAL_GORIZ_8 278 | 2208 TEX_UG_LOW_WALL_GENERAL_8 279 | 2214 TEX_UG_PIPE_8 280 | 2224 TEX_UG_RAIL_8 281 | 2234 TEX_UG_FLOOR_CONVEYOR_8_V2 282 | 2238 TEX_METAL_GORIZ_8 283 | 2244 TEX_UG_FLOOR_PANEL_VENT_8 284 | 2251 TEX_UG_LOW_WALL_GENERAL_8 285 | 2257 TEX_METAL_WALL_2 286 | 2260 TEX_UG_FLOOR_CONVEYOR_8 287 | 2263 TEX_ROCK_LAND 288 | 2272 TEX_MOH_BROUN_8 289 | 2278 TEX_UG_LOW_WALL_GENERAL_8 290 | 2286 TEX_ROCKLAND_2 291 | 2288 TEX__TEST 292 | 2292 TEX_ROCKLAND_2 293 | 2300 TEX_L6_SAND_CLIFF 294 | 2305 TEX_SWAMP_2_8 295 | 2318 TEX_VERT_LAVA_2 296 | 2321 TEX_LDM_SAND 297 | 2336 TEX_LDM_SAND 298 | 2345 TEX_L6_SAND_CLIFF 299 | 2351 TEX_LDM_SAND 300 | 2353 TEX_MAGMA_8 301 | 2364 TEX_LDM_SAND 302 | 2370 TEX_LAND_3_8 303 | 2380 TEX_LAND_3_8 304 | 2383 TEX_L6_STONES 305 | 2386 TEX_SWAMP_2_8 306 | 2389 TEX_SWAMP_2_8 307 | 2393 TEX_L5_ROAD2_8 308 | 2400 TEX_VERT_LAVA_2 309 | 2407 TEX_L3_ROCKS_V 310 | 2412 TEX_L6_SAND_CLIFF 311 | 2414 TEX_SNOW2_8 312 | 2422 TEX_SWAMP_2_8 313 | 2422 TEX_SWAMP_2_8 314 | 2425 TEX_SNOW_VERT2 315 | 2428 TEX_L6_SAND_CLIFF 316 | 2428 TEX_L6_SAND_CLIFF 317 | 2434 TEX_CRYST_8 318 | 2437 TEX_MOH_BROUN_8 319 | 2437 TEX_MOH_BROUN_8 320 | 2443 TEX_LAND108 321 | 2443 TEX_LAND108 322 | 2453 TEX_LAND108 323 | 2456 TEX_L6_ROAD_90_8 324 | 2456 TEX_L6_ROAD_90_8 325 | 2464 TEX_MOH_BROUN_8 326 | 2464 TEX_MOH_BROUN_8 327 | 2467 TEX_METAL_GORIZ_8 328 | 2467 TEX_METAL_GORIZ_8 -------------------------------------------------------------------------------- /src/unpaker/unpaker.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM unpaker (for AIM and AIM2 games, AIM:R) 3 | * Copyright (C) 2023 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | 39 | #include "decode.h" 40 | 41 | using namespace std; 42 | 43 | void unpack_file(path fn, const Files &files_to_extract) { 44 | primitives::templates2::mmap_file f{fn}; 45 | stream s{f}; 46 | pak p = s; 47 | auto descs = s.span(p.n_files); 48 | auto segments = s.span(p.n_blocks); 49 | std::vector decoded; 50 | decoded.resize((segments.size() + 1) * p.block_size * 4); 51 | auto pp = decoded.data(); 52 | progress_bar pb{segments.size(), 50}; 53 | for (auto &&seg : segments) { 54 | s.p = f.p + seg.offset; 55 | uint32_t len = s; 56 | auto m2 = [&]() { 57 | enum decode_algorithm : uint32_t { 58 | none = 0x0, 59 | lzo = 0x1, 60 | lzma = 0x2, 61 | rlew = 0x4, // https://moddingwiki.shikadi.net/wiki/Id_Software_RLEW_compression 62 | }; 63 | switch (seg.algorithm) { 64 | case decode_algorithm::none: { 65 | memcpy(pp, s.p, len); 66 | pp += len; 67 | break; 68 | } 69 | case decode_algorithm::lzo: { 70 | size_t outsz; 71 | // use lzo1x_decompress_safe? 72 | auto r2 = lzo1x_decompress(s.p, len, pp, &outsz, 0); 73 | if (r2 != LZO_E_OK) { 74 | throw std::runtime_error{"lzo error"}; 75 | } 76 | pp += outsz; 77 | break; 78 | } 79 | case decode_algorithm::rlew: { 80 | auto base = s.p; 81 | uint16_t flag = s; 82 | while (s.p < base + len) { 83 | uint16_t w = s; 84 | if ((w & 0xFF00) == (flag << 8)) { 85 | uint16_t count = (uint8_t)w; 86 | if (count == 0xFF) { 87 | uint16_t w2 = s; 88 | *(decltype(w2) *)pp = w2; 89 | pp += sizeof(w2); 90 | continue; 91 | } 92 | uint16_t w2 = s; 93 | count += 3; 94 | while (count--) { 95 | *(decltype(w2)*)pp = w2; 96 | pp += sizeof(w2); 97 | } 98 | } else { 99 | *(decltype(w)*)pp = w; 100 | pp += sizeof(w); 101 | } 102 | } 103 | break; 104 | } 105 | case decode_algorithm::lzma: { 106 | uint8_t flags = s; 107 | 108 | lzma_stream strm{}; 109 | strm.next_in = s.p; 110 | strm.avail_in = len; 111 | strm.next_out = pp; 112 | strm.avail_out = p.block_size; 113 | 114 | auto r = lzma_lzip_decoder(&strm, 10'000'000, flags); 115 | if (r != LZMA_OK) { 116 | throw std::runtime_error{"lzma error"}; 117 | } 118 | r = lzma_code(&strm, LZMA_RUN); 119 | if (r != LZMA_STREAM_END) { 120 | throw std::runtime_error{"lzma error"}; 121 | } 122 | pp += strm.total_out; 123 | break; 124 | } 125 | default: 126 | throw std::runtime_error{"compression unsupported: "s + std::to_string(seg.algorithm)}; 127 | } 128 | }; 129 | auto m1 = [&]() { 130 | enum decode_algorithm : uint32_t { 131 | None = 0x0, 132 | RLE_2_bytes = 0x1, 133 | RLE_1_byte = 0x2, 134 | decode_algorithm_1 = 0x4, // not used, maybe lzo like in m2? 135 | decode_algorithm_2 = 0x8, 136 | }; 137 | auto in = s.p; 138 | auto size1 = len; 139 | std::vector vec; 140 | if (seg.algorithm & decode_algorithm_1) { 141 | // if you see this, check in git history decode_f1() 142 | throw std::runtime_error{"compression unsupported: "s + std::to_string(seg.algorithm)}; 143 | } 144 | if (seg.algorithm & decode_algorithm_2) { 145 | uint32_t size2 = s; 146 | vec.resize(std::max(size2 * 4, p.block_size)); 147 | decode_f2((char *)s.p, size2, (char *)vec.data()); 148 | in = vec.data(); 149 | } 150 | if (seg.algorithm & RLE_2_bytes) { 151 | pp = decode_rle((uint16_t *)in, size1, (uint16_t *)pp); 152 | } else if (seg.algorithm & RLE_1_byte) { 153 | pp = decode_rle((uint8_t *)in, size1, (uint8_t *)pp); 154 | } 155 | if (seg.algorithm == None) { 156 | memcpy(pp, s.p, size1); 157 | } 158 | }; 159 | if (p.magic == 0) { 160 | m1(); 161 | } else { 162 | m2(); 163 | } 164 | pb.step(); 165 | } 166 | std::cout << "\n"; 167 | auto dir = fn += ".dir"; 168 | fs::create_directories(dir); 169 | for (auto &&d : descs) { 170 | if (!files_to_extract.empty() && !files_to_extract.contains(d.name)) { 171 | continue; 172 | } 173 | auto fn = dir / d.name; 174 | fs::create_directories(fn.parent_path()); 175 | std::cout << "unpacking " << fn << "\n"; 176 | primitives::templates2::mmap_file f{fn, primitives::templates2::mmap_file::rw{}}; 177 | f.alloc_raw(d.size); 178 | memcpy(f.p, decoded.data() + d.offset, d.size); 179 | } 180 | } 181 | 182 | int main(int argc, char *argv[]) { 183 | cl::opt p(cl::Positional, cl::desc(""), cl::Required); 184 | cl::opt cl_files_to_extract(cl::Positional, cl::desc(""), cl::ZeroOrMore); 185 | 186 | cl::ParseCommandLineOptions(argc, argv); 187 | 188 | Files files_to_extract; 189 | for (auto &&f : cl_files_to_extract) { 190 | files_to_extract.insert(f); 191 | } 192 | 193 | if (fs::is_regular_file(p)) { 194 | unpack_file(p, files_to_extract); 195 | } else if (fs::is_directory(p)) { 196 | auto files = enumerate_files_like(p, ".*\\.pak", false); 197 | for (auto &f : files) { 198 | std::cout << "processing: " << f << "\n"; 199 | try { 200 | unpack_file(f, files_to_extract); 201 | } catch (std::exception &e) { 202 | std::cerr << e.what() << "\n"; 203 | } 204 | } 205 | } else { 206 | throw std::runtime_error("Bad fs object"); 207 | } 208 | return 0; 209 | } 210 | -------------------------------------------------------------------------------- /src/aim1_language_switcher/language_switcher.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std::literals; 5 | namespace fs = std::filesystem; 6 | fs::path datadir = fs::current_path(); 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | void err(const std::wstring &s) { 15 | MessageBox(0, s.c_str(), TEXT("ERROR"), MB_OK); 16 | exit(1); 17 | } 18 | struct cfg { 19 | fs::path cfgfn; 20 | const wchar_t *sectionstr = L"PATH"; 21 | const wchar_t *keystr = L"TextFile"; 22 | 23 | cfg() { 24 | if (fs::exists(datadir / "data")) { 25 | datadir /= "data"; 26 | } else if (datadir.filename() == "data") { 27 | } else { 28 | err(L"Can't find data directory!"); 29 | } 30 | cfgfn = datadir / "config" / "cfg.ini"; 31 | } 32 | std::wstring get_current_lang() { 33 | std::wstring fn(256,0); 34 | GetPrivateProfileStringW(sectionstr, keystr, 0, fn.data(), fn.size(), cfgfn.wstring().c_str()); 35 | if (GetLastError() != 0) { 36 | err(L"can't read file"); 37 | } 38 | fn = fn.c_str(); 39 | auto w = fs::path{fn}.stem().wstring(); 40 | std::transform(w.begin(), w.end(), w.begin(), ::towlower); 41 | auto tofind = L"quest_"s; 42 | if (w.starts_with(tofind)) { 43 | return w.substr(tofind.size()); 44 | } 45 | return L"no language set (your default language)"; 46 | } 47 | bool set_lang(auto &&lang) { 48 | if (lang.contains(L' ') || lang.contains(L" ")) { 49 | return false; 50 | } 51 | auto fn = std::format(L"Data\\Quest_{}.dat", lang); 52 | if (!WritePrivateProfileStringW(sectionstr, keystr, fn.c_str(), cfgfn.wstring().c_str())) { 53 | return false; 54 | } 55 | return true; 56 | } 57 | } c; 58 | 59 | #ifndef HINST_THISCOMPONENT 60 | EXTERN_C IMAGE_DOS_HEADER __ImageBase; 61 | #define HINST_THISCOMPONENT ((HINSTANCE) & __ImageBase) 62 | #endif 63 | 64 | struct DemoApp { 65 | HRESULT Initialize() { 66 | WNDCLASSEX wcex = {sizeof(WNDCLASSEX)}; 67 | wcex.style = CS_HREDRAW | CS_VREDRAW; 68 | wcex.lpfnWndProc = DemoApp::WndProc; 69 | wcex.cbClsExtra = 0; 70 | wcex.cbWndExtra = sizeof(LONG_PTR); 71 | wcex.hInstance = HINST_THISCOMPONENT; 72 | wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 73 | wcex.lpszMenuName = NULL; 74 | wcex.hCursor = LoadCursor(NULL, IDC_ARROW); 75 | wcex.lpszClassName = TEXT("DemoApp"); 76 | RegisterClassEx(&wcex); 77 | 78 | // Create the application window. 79 | int dpiX = 0; 80 | int dpiY = 0; 81 | int horpix{}, vertpix{}; 82 | HDC hdc = GetDC(NULL); 83 | if (hdc) { 84 | dpiX = GetDeviceCaps(hdc, LOGPIXELSX); 85 | dpiY = GetDeviceCaps(hdc, LOGPIXELSY); 86 | 87 | horpix = GetDeviceCaps(hdc, HORZRES); 88 | vertpix = GetDeviceCaps(hdc, VERTRES); 89 | ReleaseDC(NULL, hdc); 90 | } 91 | 92 | int width = 400; 93 | int height = 100; 94 | m_hwnd = CreateWindow(TEXT("DemoApp"), TEXT("AIM1 Language Switcher"), WS_OVERLAPPEDWINDOW 95 | , (horpix - width) / 2 96 | , (vertpix - height) / 2 97 | , static_cast(ceil(width * dpiX / 96.f)) 98 | , static_cast(ceil(height * dpiY / 96.f)) 99 | , NULL, NULL, HINST_THISCOMPONENT, this); 100 | 101 | auto hr = m_hwnd ? S_OK : E_FAIL; 102 | if (SUCCEEDED(hr)) { 103 | ShowWindow(m_hwnd, SW_SHOWNORMAL); 104 | UpdateWindow(m_hwnd); 105 | } 106 | 107 | // Create the Combobox 108 | int nwidth = 300; // Width of the window 109 | int nheight = 200; // Height of the window 110 | int xpos = (width - nwidth) / 2; // Horizontal position of the window. 111 | int ypos = 15;//(height - nheight) / 2; // Vertical position of the window. 112 | HWND hwndParent = m_hwnd; // Handle to the parent window 113 | 114 | HWND hWndComboBox = 115 | CreateWindow(WC_COMBOBOX, TEXT(""), CBS_DROPDOWN | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE 116 | | CBS_AUTOHSCROLL | WS_HSCROLL | WS_VSCROLL, 117 | xpos, ypos, nwidth, nheight, hwndParent, NULL, HINST_THISCOMPONENT, NULL); 118 | 119 | std::set langs; 120 | for (auto &&fn : fs::directory_iterator(datadir)) { 121 | if (!fs::is_regular_file(fn)) { 122 | continue; 123 | } 124 | auto q = fn.path().stem().wstring(); 125 | while (q.find('.') != -1) { 126 | q = fs::path{q}.stem(); 127 | } 128 | std::transform(q.begin(), q.end(), q.begin(), ::towlower); 129 | auto tofind = L"quest_"s; 130 | if (!q.starts_with(tofind)) { 131 | continue; 132 | } 133 | auto lang = q.substr(tofind.size()); 134 | langs.insert(lang); 135 | } 136 | auto [it,_] = langs.insert(c.get_current_lang()); 137 | auto dist = std::distance(langs.begin(), it); 138 | for (auto &&l : langs) { 139 | SendMessage(hWndComboBox, (UINT)CB_ADDSTRING, (WPARAM)0, (LPARAM)l.c_str()); 140 | } 141 | // Send the CB_SETCURSEL message to display an initial item in the selection field 142 | SendMessage(hWndComboBox, CB_SETCURSEL, (WPARAM)dist, (LPARAM)0); 143 | return hr; 144 | } 145 | void RunMessageLoop() { 146 | MSG msg; 147 | while (GetMessage(&msg, NULL, 0, 0)) { 148 | TranslateMessage(&msg); 149 | DispatchMessage(&msg); 150 | } 151 | } 152 | private: 153 | static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { 154 | LRESULT result = 0; 155 | if (message == WM_CREATE) { 156 | auto pcs = (LPCREATESTRUCT)lParam; 157 | auto pDemoApp = (DemoApp *)pcs->lpCreateParams; 158 | ::SetWindowLongPtr(hwnd, GWLP_USERDATA, PtrToUlong(pDemoApp)); 159 | result = 1; 160 | } else { 161 | auto pDemoApp = reinterpret_cast(static_cast(::GetWindowLongPtr(hwnd, GWLP_USERDATA))); 162 | bool wasHandled = false; 163 | if (pDemoApp) { 164 | switch (message) { 165 | case WM_COMMAND: 166 | if (HIWORD(wParam) == CBN_SELCHANGE) { 167 | // If the user makes a selection from the list: 168 | // Send CB_GETCURSEL message to get the index of the selected list item. 169 | // Send CB_GETLBTEXT message to get the item. 170 | // Display the item in a messagebox. 171 | int ItemIndex = SendMessage((HWND)lParam, (UINT)CB_GETCURSEL, (WPARAM)0, (LPARAM)0); 172 | TCHAR ListItem[256]{}; 173 | SendMessage((HWND)lParam, (UINT)CB_GETLBTEXT, (WPARAM)ItemIndex, (LPARAM)ListItem); 174 | std::wstring s = ListItem; 175 | if (c.set_lang(s)) { 176 | std::wstring msg = L"Language changed to " + s; 177 | MessageBox(hwnd, msg.c_str(), TEXT("Language Changed"), MB_OK); 178 | } 179 | } 180 | wasHandled = true; 181 | result = 0; 182 | break; 183 | case WM_DISPLAYCHANGE: 184 | InvalidateRect(hwnd, NULL, FALSE); 185 | wasHandled = true; 186 | result = 0; 187 | break; 188 | case WM_DESTROY: 189 | PostQuitMessage(0); 190 | wasHandled = true; 191 | result = 1; 192 | break; 193 | } 194 | } 195 | if (!wasHandled) { 196 | result = DefWindowProc(hwnd, message, wParam, lParam); 197 | } 198 | } 199 | return result; 200 | } 201 | 202 | private: 203 | HWND m_hwnd{}; 204 | }; 205 | 206 | int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { 207 | HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); 208 | if (SUCCEEDED(CoInitialize(NULL))) { 209 | { 210 | DemoApp app; 211 | if (SUCCEEDED(app.Initialize())) { 212 | app.RunMessageLoop(); 213 | } 214 | } 215 | CoUninitialize(); 216 | } 217 | return 0; 218 | } 219 | -------------------------------------------------------------------------------- /src/common/types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mmp_extractor 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "buffer.h" 24 | #include "color.h" 25 | 26 | using u8 = uint8_t; 27 | using u16 = uint16_t; 28 | using u32 = uint32_t; 29 | using f32 = float; 30 | 31 | enum class GameType 32 | { 33 | Aim1, 34 | Aim2, 35 | AimR, 36 | }; 37 | 38 | inline GameType gameType = GameType::Aim2; 39 | 40 | template 41 | struct vector3 42 | { 43 | T x; 44 | T y; 45 | T z; 46 | 47 | bool operator==(const vector3 &rhs) const 48 | { 49 | return std::tie(x,y,z) == std::tie(rhs.x,rhs.y,rhs.z); 50 | } 51 | }; 52 | 53 | using vector3f = vector3; 54 | 55 | struct vector4 56 | { 57 | float x = 0; 58 | float y = 0; 59 | float z = 0; 60 | float w = 0; 61 | }; 62 | 63 | enum class WeatherType : uint32_t 64 | { 65 | rain = 0x1, 66 | snow = 0x2, 67 | storm = 0x4, 68 | }; 69 | 70 | enum class SmokeType : uint32_t 71 | { 72 | none, 73 | exp, 74 | biexp, 75 | linear, 76 | }; 77 | 78 | /* 79 | somewhere from old demos 80 | [MAP_DEMO] 81 | filename=Data\Map\map1.bmp 82 | path=Data\Map\map1.pnt 83 | color=Data\Map\map1_color.bmp 84 | ;skyUp=00FFFF 85 | ;skyMiddle=404040 86 | ;skyDown=0 87 | ;cloud=FF8F8F 88 | skyUp=FF0080FF 89 | skyMiddle=FF808080 90 | skyDown=FF808080 91 | cloud=FF8F8F 92 | */ 93 | struct weather 94 | { 95 | struct atmospheric_effects 96 | { 97 | vector3f wind; 98 | WeatherType weatherType; 99 | float strength; 100 | float duration; 101 | float probability; 102 | }; 103 | 104 | std::string name; 105 | std::string unk0; 106 | uint32_t unk1[2]; 107 | color smoke_1; //3? 108 | color smoke_3; //1? 109 | SmokeType smokeType; 110 | uint32_t unk2[3]; 111 | std::string cloud_layer1; 112 | std::string cloud_layer2; 113 | float cloud_layer1_speed; 114 | float cloud_layer2_speed; 115 | vector3f cloud_layer1_direction; 116 | vector3f cloud_layer2_direction; 117 | std::string sun; 118 | color general_color; 119 | color sun_color; 120 | color moon_color; 121 | std::string moon; 122 | float probability; 123 | std::string day_night_gradient_name; 124 | std::string dawn_dusk_gradient_name; 125 | color dawn_dusk_color; 126 | atmospheric_effects effects; 127 | color smoke_2; 128 | color smoke_4; 129 | uint32_t slider_3; 130 | uint32_t slider_1; 131 | float unk8[11]; 132 | 133 | void load(const buffer &b); 134 | }; 135 | 136 | struct weather_group 137 | { 138 | uint32_t unk0; // racing 139 | uint32_t n_segs; 140 | std::string name; 141 | std::vector segments; 142 | 143 | void load(const buffer &b, bool aim_racing = false) { 144 | if (aim_racing) { 145 | READ(b, unk0); 146 | } 147 | READ(b, n_segs); 148 | segments.resize(n_segs); 149 | READ_STRING_N(b, name, 0xA0); 150 | for (auto &s : segments) 151 | s.load(b); 152 | } 153 | }; 154 | 155 | struct water 156 | { 157 | float unk0[6]; 158 | std::string name1; 159 | uint32_t unk1; 160 | float unk2; 161 | uint32_t unk3[16]; 162 | float unk4; 163 | std::string name2; 164 | uint32_t unk5[16]; 165 | 166 | void load(const buffer &b); 167 | }; 168 | 169 | struct water_group 170 | { 171 | std::vector segments; 172 | 173 | void load(const buffer &b); 174 | }; 175 | 176 | struct Good 177 | { 178 | enum class TovType : uint32_t 179 | { 180 | RawMaterial, 181 | Consumables, 182 | SemiFinished, 183 | }; 184 | 185 | std::string name; 186 | char condition_variable[0x40]; // when this var is set, we can access the good 187 | float unk1_2 = 0; 188 | float price = 0; // unk, quantity? 189 | float unk2[10]; 190 | float buy_price; // initial 191 | float sell_price; // initial 192 | TovType type; 193 | bool use_in_production; 194 | bool unk3; 195 | bool unk4; 196 | bool unk5; 197 | 198 | void load(const buffer &b) 199 | { 200 | READ_STRING(b, name); 201 | if (gameType == GameType::Aim1) 202 | READ(b, condition_variable); 203 | else 204 | READ(b, unk1_2); 205 | READ(b, price); 206 | if (gameType == GameType::Aim1) 207 | READ(b, unk2); 208 | else 209 | { 210 | READ(b, buy_price); 211 | READ(b, sell_price); 212 | READ(b, type); 213 | READ(b, use_in_production); 214 | READ(b, unk3); 215 | READ(b, unk4); 216 | READ(b, unk5); 217 | } 218 | } 219 | }; 220 | 221 | struct BuildingGoods 222 | { 223 | std::string name; 224 | 225 | std::vector goods; 226 | 227 | void load(const buffer &b) 228 | { 229 | READ_STRING(b, name); 230 | b.read_vector(goods); 231 | } 232 | }; 233 | 234 | struct MapMusic 235 | { 236 | std::string mainTheme; 237 | std::string name2; 238 | 239 | std::vector fightThemes; 240 | std::vector insertionThemes; 241 | 242 | void load(const buffer &b) 243 | { 244 | READ_STRING(b, mainTheme); 245 | READ_STRING(b, name2); 246 | 247 | auto read_values = [&b](auto &v, auto &n) 248 | { 249 | for (uint32_t i = 0; i < n; i++) 250 | v.push_back(b.read_string()); 251 | }; 252 | 253 | uint32_t n1 = 0; 254 | READ(b, n1); 255 | read_values(fightThemes, n1); 256 | 257 | uint32_t n2 = 0; 258 | READ(b, n2); 259 | read_values(insertionThemes, n2); 260 | } 261 | }; 262 | 263 | struct OrganizationConfig 264 | { 265 | int count_in_group; 266 | std::vector configs; 267 | 268 | void load(const buffer &b) 269 | { 270 | uint32_t n_configs = 0; 271 | READ(b, n_configs); 272 | configs.resize(n_configs, std::string(0x20, 0)); 273 | for (uint32_t i = 0; i < n_configs; i++) 274 | READ_N(b, configs[i][0], 0x20); 275 | } 276 | }; 277 | 278 | struct Organization 279 | { 280 | std::string name; 281 | int count; // on map? 282 | float trade_war; 283 | float defence_attack; 284 | float average_rating; 285 | bool is_free; 286 | bool is_foreign; 287 | OrganizationConfig configs[3]; 288 | 289 | uint32_t unk0 = 0; 290 | char unk1[0xE0 - 4-4-4-4*3-4-1-1]; 291 | 292 | void load(const buffer &b); 293 | }; 294 | 295 | struct OrganizationBase 296 | { 297 | std::string base_name; 298 | std::string org_name; 299 | uint32_t unk0 = 0; 300 | 301 | void load(const buffer &b) 302 | { 303 | READ_STRING(b, base_name); 304 | READ_STRING(b, org_name); 305 | READ(b, unk0); 306 | } 307 | }; 308 | 309 | struct ModificatorMask 310 | { 311 | enum class ItemType : uint8_t 312 | { 313 | Glider = 1, 314 | Weapon = 2, 315 | Reactor = 3, 316 | Engine = 4, 317 | EnergyShield = 5, 318 | }; 319 | 320 | uint8_t fight : 4; 321 | uint8_t trade : 4; 322 | uint8_t courier : 4; 323 | ItemType type : 4; 324 | 325 | uint16_t : 16; 326 | }; 327 | 328 | #pragma pack(push, 1) 329 | struct pak { 330 | static constexpr uint32_t default_block_size = 0x4000; 331 | 332 | struct segment { 333 | // some file offset? trash? crc? m1 has zlib crc table (png)? 334 | uint32_t unk1; 335 | uint32_t algorithm; 336 | uint32_t offset; 337 | }; 338 | struct file_description { 339 | char name[0x50]; 340 | uint32_t offset; 341 | uint32_t size; 342 | }; 343 | 344 | uint32_t magic; 345 | uint16_t unk0; 346 | uint32_t n_files; 347 | uint32_t n_blocks; 348 | uint32_t block_size; 349 | uint32_t unk1; 350 | }; 351 | #pragma pack(pop) 352 | 353 | #pragma pack(push, 1) 354 | struct script { 355 | static constexpr uint32_t default_block_size = 16000; 356 | 357 | uint32_t file_size; 358 | uint32_t unk0{default_block_size}; // stack size? always 16000? // section bits? 359 | uint32_t raw_text_size; 360 | uint32_t nlines; 361 | }; 362 | #pragma pack(pop) 363 | -------------------------------------------------------------------------------- /src/db_add_language/db_add_language.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM db_add_language 3 | * Copyright (C) 2017 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | // This program can add db localization into P4 (not into original game) 20 | // using original db 21 | // and google/yandex services for automatic translation. 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | 44 | static int get_cp(const std::string &cp) 45 | { 46 | auto i = code_pages.find(cp); 47 | if (i == code_pages.end()) 48 | throw std::runtime_error("No code page for lang: " + cp); 49 | return i->second; 50 | } 51 | 52 | struct string_index 53 | { 54 | P4String s; 55 | polygon4::detail::IdType i = -1; 56 | 57 | void setString(const std::string &rhs, int cp) 58 | { 59 | s = to_string(str2utf16(rhs, cp)); 60 | } 61 | }; 62 | 63 | using AimKV = std::map; 64 | using AimKVResolved = std::unordered_map; 65 | AimKVResolved kv_resolved; 66 | 67 | template 68 | static int levenshtein_distance(const T &s1, const T &s2) 69 | { 70 | // To change the type this function manipulates and returns, change 71 | // the return type and the types of the two variables below. 72 | auto s1len = s1.size(); 73 | auto s2len = s2.size(); 74 | 75 | decltype(s1len) column_start = 1; 76 | 77 | auto column = new decltype(s1len)[s1len + 1]; 78 | std::iota(column + column_start, column + s1len + 1, column_start); 79 | 80 | for (auto x = column_start; x <= s2len; x++) { 81 | column[0] = x; 82 | auto last_diagonal = x - column_start; 83 | for (auto y = column_start; y <= s1len; y++) { 84 | auto old_diagonal = column[y]; 85 | auto possibilities = { 86 | column[y] + 1, 87 | column[y - 1] + 1, 88 | last_diagonal + (s1[y - 1] == s2[x - 1] ? 0 : 1) 89 | }; 90 | column[y] = std::min(possibilities); 91 | last_diagonal = old_diagonal; 92 | } 93 | } 94 | auto result = column[s1len]; 95 | delete[] column; 96 | return result; 97 | } 98 | 99 | static auto open(const path &p) 100 | { 101 | db db; 102 | if (fs::exists(p / "quest.dat")) 103 | db.open(p / "quest"); 104 | return db; 105 | }; 106 | 107 | static AimKV get_kv(const db &db, int cp) 108 | { 109 | auto iter_tbl = std::find_if(db.t.tables.begin(), db.t.tables.end(), [](auto &t) { 110 | return t.second.name == "INFORMATION"; 111 | }); 112 | if (iter_tbl == db.t.tables.end()) 113 | throw std::runtime_error("Table INFORMATION was not found"); 114 | 115 | auto find_field = [&db, &iter_tbl](const std::string &name) 116 | { 117 | auto i = std::find_if(db.t.fields.begin(), db.t.fields.end(), [&iter_tbl, &name](auto &t) { 118 | return t.second.table_id == iter_tbl->second.id && t.second.name == name; 119 | }); 120 | if (i == db.t.fields.end()) 121 | throw std::runtime_error("Field " + name + " was not found"); 122 | return i->first; 123 | }; 124 | auto nid = find_field("NAME"); 125 | auto tid = find_field("TEXT"); 126 | 127 | AimKV kv; 128 | for (auto &v : db.values) 129 | { 130 | if (v.table_id != iter_tbl->second.id || v.name.empty()) 131 | continue; 132 | for (auto &f : v.fields) 133 | { 134 | if ((f.field_id == nid || f.field_id == tid) && !f.s.empty()) 135 | kv[v.name].setString(f.s, cp); 136 | } 137 | } 138 | return kv; 139 | } 140 | 141 | static AimKVResolved get_kv_resolved(const path &d, const polygon4::Storage &storage) 142 | { 143 | static const auto fn = "kv.resolved"; 144 | 145 | AimKVResolved mres; 146 | if (fs::exists(fn)) 147 | { 148 | std::ifstream f(fn); 149 | std::string s; 150 | polygon4::detail::IdType i; 151 | while (f) 152 | { 153 | f >> std::quoted(s); 154 | if (!f) 155 | break; 156 | f >> i; 157 | mres[s] = i; 158 | } 159 | } 160 | else 161 | { 162 | auto db1 = open(d / "ru" / "aim1"); 163 | auto db2 = open(d / "ru" / "aim2"); 164 | 165 | auto kv1 = get_kv(db1, get_cp("ru")); 166 | auto kv2 = get_kv(db2, get_cp("ru")); 167 | kv1.insert(kv2.begin(), kv2.end()); 168 | auto sz = kv1.size(); 169 | std::cout << "total kvs: " << sz << "\n"; 170 | 171 | Executor e; 172 | int i = 0; 173 | for (auto &kv : kv1) 174 | { 175 | e.push([&storage, &i, &sz, &kv]() 176 | { 177 | std::cout << "total kvs: " << ++i << "/" << sz << "\n"; 178 | std::map m; 179 | for (auto &s : storage.strings) 180 | m[levenshtein_distance(kv.second.s, s.second->string.ru)] = s.first; 181 | if (m.empty()) 182 | return; 183 | kv.second.i = m.begin()->second; 184 | }); 185 | } 186 | e.wait(); 187 | 188 | std::ofstream f(fn); 189 | for (auto &kv : kv1) 190 | { 191 | mres[kv.first] = kv.second.i; 192 | f << std::quoted(kv.first) << " " << kv.second.i << "\n"; 193 | } 194 | } 195 | 196 | // make unique ids 197 | std::unordered_map u; 198 | for (auto &kv : mres) 199 | u[kv.second] = kv.first; 200 | mres.clear(); 201 | for (auto &kv : u) 202 | mres[kv.second] = kv.first; 203 | 204 | return mres; 205 | } 206 | 207 | static void process_lang(polygon4::Storage &s, const path &p, polygon4::String polygon4::LocalizedString::*field) 208 | { 209 | auto db1 = open(p); 210 | auto db2 = open(p / "aim1"); 211 | auto db3 = open(p / "aim2"); 212 | 213 | AimKV kvm; 214 | auto get_kv = [&kvm, &p](auto &db) 215 | { 216 | AimKV kv1; 217 | if (db.number_of_values) 218 | { 219 | kv1 = ::get_kv(db, get_cp(to_printable_string(p.filename()))); 220 | kvm.insert(kv1.begin(), kv1.end()); 221 | } 222 | }; 223 | get_kv(db1); 224 | get_kv(db2); 225 | get_kv(db3); 226 | 227 | std::multimap dist; 228 | std::multimap dist2; 229 | for (auto &kv : kvm) 230 | { 231 | auto i = kv_resolved.find(kv.first); 232 | if (i == kv_resolved.end()) 233 | continue; 234 | auto &sold = s.strings[i->second]->string.*field; 235 | auto d = levenshtein_distance(sold, kv.second.s); 236 | dist.insert({ d, kv.first }); 237 | //if (d == 0) 238 | // continue; 239 | auto len_diff = abs((int)sold.size() - (int)kv.second.s.size()); 240 | auto min_len = (sold.size() + kv.second.s.size()) / 2.0; 241 | //d -= len_diff; 242 | //if (d == 0) 243 | // continue; 244 | dist2.insert({ d / double(min_len), kv.first }); 245 | } 246 | 247 | std::string str; 248 | for (auto &d2 : dist2) 249 | { 250 | auto &kv = *kvm.find(d2.second); 251 | auto i = kv_resolved.find(kv.first); 252 | if (i == kv_resolved.end()) 253 | continue; 254 | auto &sold = s.strings[i->second]->string.*field; 255 | //sold = kv.second.s; 256 | str += "id: " + std::to_string(i->second) + "\n"; 257 | str += "kd: " + std::to_string(d2.first) + "\n"; 258 | str += "key: " + i->first + "\n\n"; 259 | str += "old:\n"; 260 | str += sold + "\n"; 261 | str += "\n"; 262 | str += "new:\n"; 263 | str += kv.second.s + "\n"; 264 | str += "\n================================================\n\n"; 265 | } 266 | /*for (auto &kv : kvm) 267 | { 268 | auto i = kv_resolved.find(kv.first); 269 | if (i == kv_resolved.end()) 270 | continue; 271 | auto &sold = s.strings[i->second]->string.*field; 272 | //sold = kv.second.s; 273 | str += "id: " + std::to_string(i->second) + "\n"; 274 | str += "key: " + i->first + "\n\n"; 275 | str += "old:\n"; 276 | str += wstring2string(sold) + "\n"; 277 | str += "\n"; 278 | str += "new:\n"; 279 | str += wstring2string(kv.second.s) + "\n"; 280 | str += "\n================================================\n\n"; 281 | }*/ 282 | write_file(p / (p.filename() += "_diff.txt"), str); 283 | } 284 | 285 | int main(int argc, char *argv[]) 286 | { 287 | cl::opt db_fn(cl::Positional, cl::desc(""), cl::Required); 288 | cl::opt dir_to_lang_dbs(cl::Positional, cl::desc(""), cl::Required); 289 | 290 | cl::ParseCommandLineOptions(argc, argv); 291 | 292 | fs::current_path(dir_to_lang_dbs); 293 | 294 | auto storage = polygon4::initStorage(); 295 | auto database = std::make_unique(db_fn); 296 | storage->load(*database, {}); 297 | kv_resolved = get_kv_resolved(dir_to_lang_dbs, *storage.get()); 298 | 299 | // to check correctness 300 | process_lang(*storage.get(), dir_to_lang_dbs / "ru", &polygon4::LocalizedString::ru); 301 | 302 | for (auto &f : fs::directory_iterator(dir_to_lang_dbs)) 303 | { 304 | if (!fs::is_directory(f)) 305 | continue; 306 | 307 | auto p = f.path(); 308 | 309 | if (0); 310 | #define ADD_LANGUAGE(l, n) else if (p.filename() == #l && p.filename() != "ru") \ 311 | {process_lang(*storage.get(), p, &polygon4::LocalizedString::l);} 312 | #include 313 | #undef ADD_LANGUAGE 314 | else 315 | { 316 | std::cerr << "No such lang: " << to_printable_string(p.filename()) << "\n"; 317 | continue; 318 | } 319 | } 320 | 321 | return 0; 322 | } 323 | -------------------------------------------------------------------------------- /src/model/model.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AIM mod_converter 3 | * Copyright (C) 2015 lzwdgc 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "types.h" 22 | 23 | //#include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | const std::string texture_extension = ".TM.bmp"; 31 | 32 | class buffer; 33 | 34 | enum 35 | { 36 | F_WIND_TRANSFORM = 0x4, 37 | }; 38 | 39 | enum class AdditionalParameter : uint32_t 40 | { 41 | None, 42 | DetalizationCoefficient, 43 | }; 44 | 45 | enum class ModelRotation : uint32_t 46 | { 47 | None, 48 | Vertical, 49 | Horizontal, 50 | Other, 51 | }; 52 | 53 | enum class BlockType : uint32_t 54 | { 55 | VisibleObject, 56 | HelperObject, 57 | BitmapAlpha, 58 | BitmapGrass, 59 | ParticleEmitter, 60 | }; 61 | 62 | enum class MaterialType : uint32_t 63 | { 64 | Texture = 0x0, 65 | TextureWithGlareMap = 0x1, 66 | AlphaTextureNoGlare = 0x2, // TextureWithoutGlareMap 67 | AlphaTextureWithOverlap = 0x3, 68 | TextureWithGlareMap2 = 0x4, // ??? also <- 4 69 | AlphaTextureDoubleSided = 0x6, 70 | DetalizationObjectGrass = 0x8, 71 | Fire = 0x9, 72 | MaterialOnly = 0x14, 73 | TextureWithDetalizationMap = 0x1A, // from Viewer: (AdditionalParameter 1) 74 | DetalizationObjectStone = 0x1F, 75 | TextureWithDetalizationMapWithoutModulation = 0x20, // from Viewer: (AdditionalParameter 1) 76 | TiledTexture = 0x22, 77 | TextureWithGlareMapAndMask = 0x32, 78 | TextureWithMask = 0x35, 79 | Fire2 = 0x3D, 80 | }; 81 | 82 | enum class AxisSystem 83 | { 84 | // UpVector = YAxis, FrontVector = ParityOdd, CoordSystem = RightHanded 85 | // default, AIM 86 | eMayaYUp, 87 | eMotionBuilder = eMayaYUp, 88 | eOpenGL = eMayaYUp, 89 | 90 | // UpVector = ZAxis, FrontVector = -ParityOdd, CoordSystem = RightHanded 91 | eMayaZUp, 92 | eMax = eMayaZUp, 93 | eBlender = eMayaZUp, // TODO: check: actually Blender might have +ParityOdd 94 | 95 | // UpVector = YAxis, FrontVector = ParityOdd, CoordSystem = LeftHanded 96 | eDirectX, 97 | eLightwave = eDirectX, 98 | 99 | // UpVector = ZAxis, FrontVector = ParityOdd, CoordSystem = RightHanded 100 | eWindows3DViewer, 101 | 102 | // special 103 | Default = eMayaYUp, 104 | }; 105 | 106 | template 107 | struct aim_vector3 : vector3 108 | { 109 | using base = vector3; 110 | }; 111 | 112 | using aim_vector3f = aim_vector3; 113 | 114 | struct aim_vector4 : aim_vector3f 115 | { 116 | using base = aim_vector3f; 117 | 118 | float w = 1.0f; 119 | 120 | std::string print() const; 121 | void load(const buffer &b, uint32_t flags = 0); 122 | }; 123 | 124 | struct vertex_normal : aim_vector3f 125 | { 126 | void load(const buffer &b); 127 | }; 128 | 129 | struct uv 130 | { 131 | float u; 132 | float v; 133 | 134 | void load(const buffer &b); 135 | 136 | bool operator==(const uv &rhs) const { return std::tie(u, v) == std::tie(rhs.u, rhs.v); } 137 | }; 138 | 139 | struct vertex 140 | { 141 | aim_vector4 coordinates; 142 | vertex_normal normal; 143 | uv texture_coordinates; 144 | 145 | void load(const buffer &b, uint32_t flags); 146 | }; 147 | 148 | struct face 149 | { 150 | uint16_t vertex_list[3]; 151 | 152 | void load(const buffer &b); 153 | 154 | bool operator==(const face &rhs) const 155 | { 156 | return std::equal( 157 | std::begin(vertex_list), std::end(vertex_list), 158 | std::begin(rhs.vertex_list), std::end(rhs.vertex_list) 159 | ); 160 | } 161 | }; 162 | 163 | struct model_data 164 | { 165 | std::vector vertices; 166 | std::vector faces; // triangles 167 | 168 | void load(const buffer &b, uint32_t flags); 169 | }; 170 | 171 | struct processed_model_data 172 | { 173 | struct face 174 | { 175 | struct point 176 | { 177 | // indices 178 | uint16_t vertex; 179 | uint16_t normal; 180 | uint16_t uv; 181 | 182 | bool operator==(const point &rhs) const 183 | { 184 | return std::tie(vertex, normal, uv) == std::tie(rhs.vertex, rhs.normal, rhs.uv); 185 | } 186 | }; 187 | 188 | point points[3]; 189 | 190 | bool operator==(const face &rhs) const 191 | { 192 | return std::equal( 193 | std::begin(points), std::end(points), 194 | std::begin(rhs.points), std::end(rhs.points) 195 | ); 196 | } 197 | }; 198 | 199 | std::vector vertices; 200 | std::vector normals; 201 | std::vector uvs; 202 | std::vector faces; 203 | 204 | std::string print(int v_offset, int n_offset, int uv_offset, AxisSystem as) const; 205 | }; 206 | 207 | struct animation 208 | { 209 | // +1 +0.5 -0.5 +1 210 | struct segment 211 | { 212 | struct vertex_diff 213 | { 214 | vertex_normal translation; 215 | float unk[3]; // rotation? 216 | 217 | void load(const buffer &b); 218 | }; 219 | 220 | uint32_t n; 221 | std::vector model_polygons; 222 | 223 | // unk 224 | uint32_t unk0; // time or diff or something 225 | uint32_t unk1; // time or diff or something 226 | std::vector polygon_diffs; 227 | 228 | void loadHeader(const buffer &b); 229 | void loadData(const buffer &b); 230 | }; 231 | 232 | enum animation_type : uint32_t 233 | { 234 | _2grad_1directions_linear, 235 | _2grad_2directions, 236 | _2grad_1directions, 237 | _1grad_1directions, 238 | b, 239 | c,d,e,f,g,h,i,j,k,l 240 | }; 241 | 242 | union 243 | { 244 | animation_type atype; 245 | uint32_t itype; 246 | } type; 247 | std::string name; 248 | segment segments[4]; 249 | 250 | void load(const buffer &b); 251 | }; 252 | 253 | struct damage_model 254 | { 255 | std::string name; 256 | std::vector model_polygons; 257 | uint32_t flags; 258 | model_data data; 259 | 260 | uint8_t unk6; 261 | float unk8[3]; 262 | 263 | virtual void load(const buffer &b); 264 | }; 265 | 266 | struct mat_color 267 | { 268 | float r; 269 | float g; 270 | float b; 271 | float alpha; 272 | 273 | std::string print() const; 274 | }; 275 | 276 | struct material 277 | { 278 | mat_color ambient; 279 | mat_color diffuse; 280 | mat_color specular; 281 | mat_color emissive; 282 | float power; 283 | 284 | void load(const buffer &b); 285 | }; 286 | 287 | struct rotation 288 | { 289 | ModelRotation type; 290 | float speed; 291 | vector3 center_of_rotating_axis; 292 | }; 293 | 294 | struct additional_parameters 295 | { 296 | AdditionalParameter params; 297 | float detalization_koef; 298 | }; 299 | 300 | struct block 301 | { 302 | struct header 303 | { 304 | struct texture 305 | { 306 | std::string name; 307 | uint32_t number; // AimR 308 | 309 | void load(const buffer &b); 310 | }; 311 | 312 | BlockType type; 313 | std::string name; 314 | texture mask; 315 | texture spec; 316 | texture tex3; 317 | texture tex4; 318 | union // LODs 319 | { 320 | struct 321 | { 322 | uint8_t lod1 : 1; 323 | uint8_t lod2 : 1; 324 | uint8_t lod3 : 1; 325 | uint8_t lod4 : 1; 326 | uint8_t : 4; 327 | } LODs; 328 | uint32_t all_lods; 329 | }; 330 | 331 | // stuff 332 | uint32_t size; 333 | 334 | // unk 335 | uint32_t unk2[3]; 336 | uint32_t unk3; 337 | float unk4[10]; 338 | 339 | void load(const buffer &b); 340 | }; 341 | 342 | // for save 343 | struct block_info 344 | { 345 | aim_vector4 min; 346 | aim_vector4 max; 347 | }; 348 | 349 | struct animated_texture 350 | { 351 | uint16_t zeros0; 352 | uint8_t multiplier_of_number_of_gradations; 353 | uint8_t number_of_gradations; 354 | float time_of_full_cycle; 355 | 356 | float get_actual_number_of_gradations() const 357 | { 358 | if (!multiplier_of_number_of_gradations) 359 | return multiplier_of_number_of_gradations * number_of_gradations; 360 | return number_of_gradations * number_of_gradations; 361 | } 362 | 363 | float get_actual_time_of_full_cycle() const 364 | { 365 | return get_actual_number_of_gradations() * time_of_full_cycle; 366 | } 367 | 368 | bool is_present() const { return number_of_gradations; } 369 | }; 370 | 371 | header h; 372 | 373 | // data 374 | material mat; 375 | MaterialType mat_type; 376 | 377 | uint32_t triangles_mult_7; // Tri-mesh. flags? 378 | // 379 | 380 | additional_parameters additional_params; 381 | rotation rot; 382 | uint32_t flags; 383 | model_data md; 384 | 385 | // animations 386 | uint32_t auto_animation; 387 | float animation_cycle; 388 | std::vector animations; 389 | 390 | // 391 | std::vector damage_models; 392 | animated_texture atex; 393 | 394 | // unk 395 | uint32_t unk10; 396 | float unk8; 397 | uint32_t unk11; 398 | uint32_t unk12; 399 | 400 | void load(const buffer &b); 401 | void loadPayload(const buffer &b); 402 | void loadPayloadAndProcess(const buffer &b); 403 | void linkFaces(); 404 | 405 | std::string printMtl() const; 406 | std::string printObj(int v_offset, int n_offset, int uv_offset, AxisSystem as) const; 407 | block_info save(yaml root) const; 408 | 409 | bool canPrint() const; 410 | bool isEngineFx() const; 411 | 412 | // 413 | processed_model_data pmd; 414 | bool printable = false; 415 | }; 416 | 417 | struct model 418 | { 419 | std::vector blocks; 420 | 421 | void load(const buffer &b); 422 | void linkFaces(); 423 | 424 | void print(const std::string &fn, AxisSystem) const; 425 | void printFbx(const std::string &fn, AxisSystem) const; 426 | void save(yaml root) const; 427 | }; 428 | 429 | float scale_mult(); 430 | --------------------------------------------------------------------------------