diff --git a/src/aim1_mod_maker/aim1_mod_maker.h b/src/aim1_mod_maker/aim1_mod_maker.h index eeed217..2797916 100644 --- a/src/aim1_mod_maker/aim1_mod_maker.h +++ b/src/aim1_mod_maker/aim1_mod_maker.h @@ -8,6 +8,47 @@ #include #include +constexpr auto aim_exe = "aim.exe"sv; + +using byte_array = std::vector; + +struct patcher { +}; + +auto operator""_bin(const char *ptr, uint64_t len) { + byte_array ret; + auto lines = split_lines(ptr); + for (auto &&line : lines) { + auto d = line.substr(0, line.find(';')); + auto bytes = split_string(d, " \r\n"); + for (auto &&v : bytes) { + if (v.size() != 2) { + throw std::runtime_error{"bad input string"}; + } + auto hex2int1 = [](auto c) { + if (isdigit(c)) { + return c - '0'; + } else if (isupper(c)) { + return c - 'A' + 10; + } else { + return c - 'a' + 10; + } + }; + auto hex2int = [&](auto c) { + auto v = hex2int1(c); + if (v < 0 || v > 15) { + throw std::runtime_error{"bad input char"}; + } + return v; + }; + auto d1 = hex2int(v[0]); + auto d2 = hex2int(v[1]); + ret.push_back((d1 << 4) | d2); + } + } + return ret; +} + struct mod_maker { enum class file_type { unknown, @@ -30,10 +71,12 @@ struct mod_maker { mod_maker(const std::string &name) : name{name} { detect_game_dir(fs::current_path()); detect_tools(); + prepare_injections(); } mod_maker(const std::string &name, const path &dir) : name{name} { detect_game_dir(dir); detect_tools(); + prepare_injections(); } void replace(const path &fn, const std::string &from, const std::string &to) { @@ -57,7 +100,7 @@ struct mod_maker { void apply() { std::vector files; for (auto &&p : files_to_mod) { - if (p.filename() == "aim.exe") { + if (p.filename() == aim_exe) { continue; } files.push_back(p.string()); @@ -88,6 +131,73 @@ struct mod_maker { #undef ENABLE_DISABLE_FUNC private: + static void memcpy(auto &ptr, const byte_array &data) { + ::memcpy(ptr, data.data(), data.size()); + ptr += data.size(); + } + static auto memmem(auto ptr, auto sz, const byte_array &bytes) { + sz -= bytes.size(); + for (int i = 0; i < sz; ++i) { + if (memcmp(ptr + i, bytes.data(), bytes.size()) == 0) { + return ptr + i; + } + } + throw std::runtime_error{"not found"}; + } + static auto memreplace(auto base, auto sz, const byte_array &from, const byte_array &to) { + if (from.size() != to.size()) { + throw std::runtime_error{"size mismatch"}; + } + auto ptr = memmem(base, sz, from); + byte_array old; + old.resize(from.size()); + ::memcpy(old.data(), ptr, old.size()); + ::memcpy(ptr, to.data(), to.size()); + return std::tuple{ptr, old}; + } + static auto make_insn_with_address(auto &&insn, uint32_t addr) { + byte_array arr(insn.size() + sizeof(addr)); + ::memcpy(arr.data(), insn.data(), insn.size()); + *(uint32_t *)(&arr[insn.size()]) = addr; + return arr; + } + void prepare_injections() { + create_backup_exe_file(); + primitives::templates2::mmap_file f{find_real_filename(aim_exe), primitives::templates2::mmap_file::rw{}}; + constexpr uint32_t trampoline_base = 0x00025100; + constexpr uint32_t trampoline_target = 0x001207f0; + constexpr uint32_t code_base = 0x00401000; + constexpr uint32_t data_base = 0x00540000; + constexpr uint32_t free_data_base = 0x006929C0; + //constexpr uint32_t our_data = 0x00550FD0; + constexpr uint32_t our_data = 0x005207F0; + //constexpr uint32_t free_data_base_real = 0x140000 + our_data - 0x00540000; + + auto ptr = f.p + trampoline_target; + //strcpy((char *)f.p + free_data_base_real, "aim_fixes-0.0.1.dll"); + strcpy((char *)ptr, "aim_fixes-0.0.1.dll"); + ptr += 0x20; + const auto jumppad = "68 30 B8 51 00"_bin; // push offset SEH_425100 + uint32_t jump_offset = ptr - f.p - trampoline_base - jumppad.size() * 2; + auto [oldaddr, oldcode] = memreplace(f.p, f.sz, jumppad, make_insn_with_address("e9"_bin, jump_offset)); + memcpy(ptr, jumppad); // put our removed insn + memcpy(ptr, R"( +60 ; pusha +)"_bin); + auto push_dll_name = make_insn_with_address("68"_bin, our_data); + memcpy(ptr, push_dll_name); // + memcpy(ptr, R"( +8B 3D D8 10 52 00 ; mov edi, ds:LoadLibraryA - not working ; but do not remove, it does not work without it +bf 30 0f 91 75 ; mov edi, 0x75910f30 - load direct adress +; edi has wrong address after prev. insn, so we fix it manually +81 EF 00 BD 00 00 ; sub edi, 0BD00h +)"_bin); + memcpy(ptr, R"( +FF D7 ; call edi +61 ; popa +)"_bin); + memcpy(ptr, make_insn_with_address("e9"_bin, -(ptr - f.p - trampoline_base - jumppad.size()))); + } path find_real_filename(path fn) const { auto s = fn.wstring(); boost::to_lower(s); @@ -95,7 +205,7 @@ private: if (fs::exists(fn)) { return fn; } - if (fn == "aim.exe") { + if (fn == aim_exe) { return game_dir / fn; } @@ -148,10 +258,19 @@ private: } // from https://github.com/Solant/aim-patches void free_camera(uint8_t val) { - patch("aim.exe", 0x1F805, val); + create_backup_exe_file(); + patch(aim_exe, 0x1F805, val); } void win_key(uint8_t val) { - patch("aim.exe", 0x4A40D, val); + create_backup_exe_file(); + patch(aim_exe, 0x4A40D, val); + } + void create_backup_exe_file() { + auto fn = find_real_filename(aim_exe); + auto backup = path{fn} += ".orig"; + if (!fs::exists(backup)) { + fs::copy_file(fn, backup); + } } template void patch_raw(path fn, uint32_t offset, T val) const { @@ -199,7 +318,7 @@ private: return p; } void unpak(const path &p) const { - if (fs::exists(make_unpak_dir(p.filename()))) { + if (fs::exists(make_unpak_dir(p))) { return; } run_p4_tool("unpaker", p); @@ -223,7 +342,7 @@ private: c.execute(); } void detect_game_dir(const path &dir) { - const auto aim1_exe = "aim.exe"sv; + const auto aim1_exe = aim_exe; if (fs::exists(dir / aim1_exe)) { game_dir = dir; } else if (fs::exists(dir.parent_path() / aim1_exe)) {