diff --git a/README.md b/README.md index 980865b7..2d066c53 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![build](https://github.com/xensik/gsc-tool/workflows/Build/badge.svg)](https://github.com/xensik/gsc-tool/actions) [![sonar](https://sonarcloud.io/api/project_badges/measure?project=xensik_gsc-tool&metric=alert_status)](https://sonarcloud.io/summary/overall?id=xensik_gsc-tool) +[![build](https://github.com/xensik/gsc-tool/workflows/Build/badge.svg)](https://github.com/xensik/gsc-tool/actions) [![issues](https://img.shields.io/github/issues/xensik/gsc-tool)](https://github.com/xensik/gsc-tool/issues) [![license](https://img.shields.io/github/license/xensik/gsc-tool.svg)](https://github.com/xensik/gsc-tool/blob/dev/LICENSE) [![releases](https://img.shields.io/github/v/release/xensik/gsc-tool)](https://github.com/xensik/gsc-tool/releases) @@ -23,6 +23,7 @@ A utility to compile & decompile IW engine game scripts. - **T7** *(Call of Duty: Black Ops III)* `PC` *(Decompiler)* - **T8** *(Call of Duty: Black Ops 4)* ***\*WIP\**** - **T9** *(Call of Duty: Black Ops Cold War)* ***\*WIP\**** +- **JUP** *(Call of Duty: Modern Warfare III (2023)* ***\*WIP\**** ## Usage ``gsc-tool [OPTIONS..] `` @@ -33,11 +34,11 @@ A utility to compile & decompile IW engine game scripts. ``-m, --mode `` [REQUIRED] one of: `asm`, `disasm`, `comp`, `decomp`, `parse` - ``-g, --game `` [REQUIRED] one of: `iw5`, `iw6`, `iw7`, `iw8`, `iw9`, `s1`, `s2`, `s4`, `h1`, `h2`, `t6` `t7` `t8` `t9` + ``-g, --game `` [REQUIRED] one of: `iw5`, `iw6`, `iw7`, `iw8`, `iw9`, `s1`, `s2`, `s4`, `h1`, `h2`, `t6` `t7` `t8` `t9` `jup` ``-s, --system `` [REQUIRED] one of: `pc`, `ps3`, `ps4`, `ps5`, `xb2` (*360*), `xb3` (*One*), `xb4` (*Series X|S*), `wiiu` - ``-d, --dev`` Enable developer mode (generate bytecode map). + ``-d, --dev`` Enable developer mode (dev blocks & generate bytecode map). ``-z, --zonetool`` Enable zonetool mode (use .cgsc files). diff --git a/include/xsk/arc/common/types.hpp b/include/xsk/arc/common/types.hpp index 11e55ca4..3233e2bb 100644 --- a/include/xsk/arc/common/types.hpp +++ b/include/xsk/arc/common/types.hpp @@ -64,6 +64,7 @@ enum class engine : u8 t7, t8, t9, + jup }; struct props diff --git a/include/xsk/arc/engine/jup.hpp b/include/xsk/arc/engine/jup.hpp new file mode 100644 index 00000000..07d50f3e --- /dev/null +++ b/include/xsk/arc/engine/jup.hpp @@ -0,0 +1,24 @@ +// Copyright 2024 xensik. All rights reserved. +// +// Use of this source code is governed by a GNU GPLv3 license +// that can be found in the LICENSE file. + +#pragma once + +#include "xsk/stdinc.hpp" +#include "xsk/arc/context.hpp" + +namespace xsk::arc::jup +{ + +constexpr usize code_count = 0; +constexpr usize hash_count = 0; +constexpr u64 header_magic = 0x38000A0D43534780; + +class context : public arc::context +{ +public: + context(); +}; + +} // namespace xsk::arc::jup diff --git a/src/arc/engine/jup.cpp b/src/arc/engine/jup.cpp new file mode 100644 index 00000000..2c485451 --- /dev/null +++ b/src/arc/engine/jup.cpp @@ -0,0 +1,32 @@ +// Copyright 2024 xensik. All rights reserved. +// +// Use of this source code is governed by a GNU GPLv3 license +// that can be found in the LICENSE file. + +#include "xsk/arc/engine/jup.hpp" + +namespace xsk::arc::jup +{ + +extern std::array, code_count> const code_list; +// extern std::array, hash_count> const hash_list; + +context::context() : arc::context(props::v3, engine::jup, endian::little, system::pc, header_magic) +{ + code_map_.reserve(code_list.size()); + code_map_rev_.reserve(code_list.size()); + // hash_map_.reserve(hash_list.size()); + + for (auto const& entry : code_list) + { + code_map_.insert({ entry.first, entry.second }); + code_map_rev_.insert({ entry.second, entry.first }); + } + + // for (auto const& entry : hash_list) + // { + // hash_map_.insert({ entry.first, entry.second }); + // } +} + +} // namespace xsk::arc::jup diff --git a/src/arc/engine/jup_code.cpp b/src/arc/engine/jup_code.cpp new file mode 100644 index 00000000..14967ec2 --- /dev/null +++ b/src/arc/engine/jup_code.cpp @@ -0,0 +1,15 @@ +// Copyright 2024 xensik. All rights reserved. +// +// Use of this source code is governed by a GNU GPLv3 license +// that can be found in the LICENSE file. + +#include "xsk/arc/engine/jup.hpp" + +namespace xsk::arc::jup +{ + +extern std::array, code_count> const code_list +{{ +}}; + +} // namespace xsk::arc::jup diff --git a/src/tool/main.cpp b/src/tool/main.cpp index d30bbdfb..49e1306e 100644 --- a/src/tool/main.cpp +++ b/src/tool/main.cpp @@ -30,6 +30,7 @@ #include "xsk/arc/engine/t7.hpp" #include "xsk/arc/engine/t8.hpp" #include "xsk/arc/engine/t9.hpp" +#include "xsk/arc/engine/jup.hpp" #include "xsk/version.hpp" #include "cxxopts.hpp" @@ -41,7 +42,7 @@ namespace xsk enum class result : i32 { success = 0, failure = 1 }; enum class fenc { _, source, assembly, binary, src_bin }; enum class mode { _, assemble, disassemble, compile, decompile, parse, rename }; -enum class game { _, iw5, iw6, iw7, iw8, iw9, s1, s2, s4, h1, h2, t6, t7, t8, t9 }; +enum class game { _, iw5, iw6, iw7, iw8, iw9, s1, s2, s4, h1, h2, t6, t7, t8, t9, jup }; enum class mach { _, pc, ps3, ps4, ps5, xb2, xb3, xb4, wiiu }; std::unordered_map const gsc_exts = @@ -92,6 +93,7 @@ std::unordered_map const games = { "t7", game::t7 }, { "t8", game::t8 }, { "t9", game::t9 }, + { "jup", game::jup }, }; std::map const games_rev = @@ -110,6 +112,7 @@ std::map const games_rev = { game::t7, "t7" }, { game::t8, "t8" }, { game::t9, "t9" }, + { game::jup, "jup" }, }; std::unordered_map const machs = @@ -763,6 +766,9 @@ auto disassemble_file(game game, mach mach, fs::path const& file, fs::path rel) { try { + if (game > game::t7) + throw std::runtime_error("not implemented"); + rel = fs::path{ games_rev.at(game) } / rel / file.filename().replace_extension((file.extension().string().starts_with(".gsc") ? ".gscasm" : ".cscasm")); auto data = utils::file::read(file.string()); @@ -822,6 +828,9 @@ auto decompile_file(game game, mach mach, fs::path const& file, fs::path rel) -> { try { + if (game > game::t7) + throw std::runtime_error("not implemented"); + rel = fs::path{ games_rev.at(game) } / rel / file.filename(); auto data = utils::file::read(file); @@ -841,10 +850,33 @@ auto decompile_file(game game, mach mach, fs::path const& file, fs::path rel) -> } } -auto parse_file(game, mach, fs::path const&, fs::path) -> result +auto parse_file(game game, mach mach, fs::path file, fs::path rel) -> result { - std::cerr << std::format("not implemented for treyarch\n"); - return result::failure; + try + { + if (game != game::t6) + throw std::runtime_error("not implemented"); + + rel = fs::path{ games_rev.at(game) } / rel / file.filename(); + + auto data = utils::file::read(file); + + if (!std::memcmp(&data[0], "\x80GSC", 4)) + { + std::cerr << std::format("{} at {}\n", "already compiled", file.generic_string()); + return result::success; + } + + auto prog = contexts[game][mach]->source().parse_program(file.string(), data); + utils::file::save(fs::path{ "parsed" } / rel, contexts[game][mach]->source().dump(*prog)); + std::cout << std::format("parsed {}\n", rel.generic_string()); + return result::success; + } + catch (std::exception const& e) + { + std::cerr << std::format("{} at {}\n", e.what(), file.generic_string()); + return result::failure; + } } auto rename_file(game, mach, fs::path const&, fs::path) -> result @@ -910,42 +942,55 @@ auto init_t7(mach mach, bool dev) -> void } } -auto init_t8(mach /*mach*/, bool /*dev*/) -> void +auto init_t8(mach mach, bool dev) -> void { - throw std::runtime_error("not implemented"); + if (contexts[game::t8].contains(mach)) return; - // if (contexts[game::t8].contains(mach)) return; - - // switch (mach) - // { - // case mach::pc: - // { - // contexts[game::t8][mach] = std::make_unique(); - // contexts[game::t8][mach]->init(build::prod, fs_read); - // break; - // } - // default: - // throw std::runtime_error("not implemented"); - // } + switch (mach) + { + case mach::pc: + { + contexts[game::t8][mach] = std::make_unique(); + contexts[game::t8][mach]->init(dev ? build::dev : build::prod, fs_read); + break; + } + default: + throw std::runtime_error("not implemented"); + } } -auto init_t9(mach /*mach*/, bool /*dev*/) -> void +auto init_t9(mach mach, bool dev) -> void { - throw std::runtime_error("not implemented"); + if (contexts[game::t9].contains(mach)) return; - // if (contexts[game::t9].contains(mach)) return; + switch (mach) + { + case mach::pc: + { + contexts[game::t9][mach] = std::make_unique(); + contexts[game::t9][mach]->init(dev ? build::dev : build::prod, fs_read); + break; + } + default: + throw std::runtime_error("not implemented"); + } +} - // switch (mach) - // { - // case mach::pc: - // { - // contexts[game::t9][mach] = std::make_unique(); - // contexts[game::t9][mach]->init(build::prod, fs_read); - // break; - // } - // default: - // throw std::runtime_error("not implemented"); - // } +auto init_jup(mach mach, bool dev) -> void +{ + if (contexts[game::jup].contains(mach)) return; + + switch (mach) + { + case mach::pc: + { + contexts[game::jup][mach] = std::make_unique(); + contexts[game::jup][mach]->init(dev ? build::dev : build::prod, fs_read); + break; + } + default: + throw std::runtime_error("not implemented"); + } } auto init(game game, mach mach, bool dev) -> void @@ -968,6 +1013,7 @@ auto init(game game, mach mach, bool dev) -> void case game::t7: init_t7(mach, dev); break; case game::t8: init_t8(mach, dev); break; case game::t9: init_t9(mach, dev); break; + case game::jup: init_jup(mach, dev); break; default: break; } } @@ -1102,7 +1148,7 @@ auto main(u32 argc, char** argv) -> result options.add_options() ("m,mode","[REQUIRED] one of: asm, disasm, comp, decomp, parse, rename", cxxopts::value(), "") - ("g,game", "[REQUIRED] one of: iw5, iw6, iw7, iw8, iw9, s1, s2, s4, h1, h2, t6, t7, t8, t9", cxxopts::value(), "") + ("g,game", "[REQUIRED] one of: iw5, iw6, iw7, iw8, iw9, s1, s2, s4, h1, h2, t6, t7, t8, t9, jup", cxxopts::value(), "") ("s,system", "[REQUIRED] one of: pc, ps3, ps4, ps5, xb2 (360), xb3 (One), xb4 (Series X|S), wiiu", cxxopts::value(), "") ("p,path", "File or directory to process.", cxxopts::value()) ("d,dev", "Enable developer mode (dev blocks & generate bytecode map).", cxxopts::value()->implicit_value("true"))