From e961d5e4e534dfcea960282ad14f7ad2e668355d Mon Sep 17 00:00:00 2001 From: Michael Oliver Date: Wed, 7 Oct 2026 07:33:44 +0100 Subject: [PATCH 1/5] refactor: use per-game `_codxe` data roots --- docs/features.md | 27 ++++---- src/common/config.cpp | 67 ++++++++++---------- src/common/config.h | 17 ++--- src/common/gsc_loader.cpp | 2 +- src/game/iw2/mp/main.cpp | 2 +- src/game/iw2/sp/main.cpp | 2 +- src/game/iw3/mp/components/cmds.cpp | 2 +- src/game/iw3/mp/components/fastfiles.cpp | 12 ++-- src/game/iw3/mp/components/fastfiles.h | 2 +- src/game/iw3/mp/components/image_loader.cpp | 4 +- src/game/iw3/mp/components/mods.cpp | 15 +++-- src/game/iw3/mp/components/mpsp.cpp | 2 +- src/game/iw3/mp/components/stats.cpp | 2 +- src/game/iw3/mp/components/ui_feeder.cpp | 10 +-- src/game/iw3/mp/main.cpp | 1 + src/game/iw3/sp/main.cpp | 6 +- src/game/iw4/mp/components/fastfiles.cpp | 38 ++--------- src/game/iw4/mp/main.cpp | 1 + src/game/iw4/mp_tu6/components/fastfiles.cpp | 38 ++--------- src/game/iw4/mp_tu6/main.cpp | 2 +- src/game/iw4/sp/components/clipmap.cpp | 2 +- src/game/iw4/sp/main.cpp | 2 +- src/game/iw5/mp/main.cpp | 2 +- src/game/qos/mp/main.cpp | 2 +- src/game/qos/sp/main.cpp | 2 +- src/game/t4/mp/components/map.cpp | 2 +- src/game/t4/mp/main.cpp | 2 +- src/game/t4/sp/components/clipmap.cpp | 2 +- src/game/t4/sp/main.cpp | 2 +- src/game/t5/mp/main.cpp | 2 +- src/game/t5/sp/main.cpp | 2 +- 31 files changed, 119 insertions(+), 155 deletions(-) diff --git a/docs/features.md b/docs/features.md index 36e67186..b2ecc27e 100644 --- a/docs/features.md +++ b/docs/features.md @@ -6,7 +6,11 @@ The raw GSC loader enables loading `.gsc` scripts directly from the mod director ### Setup -Create a `_codxe\mods` folder in your game directory, and place a `codxe.json` in `_codxe` to define which mod is active. +Create a `_codxe\\mods` folder in your game directory, and place a `codxe.json` in `_codxe\` to define which mod is active. Use `iw2`, `iw3`, `iw4`, `iw5`, `t4`, `t5`, or `qos` for the game ID. + +CoD Xe selects one data root at startup. If `_codxe\` exists, all config, mod, fastfile, usermap, internal asset, dump, and player stats paths use that root. Missing files never fall back to the legacy `_codxe` root. The old layout remains supported when the nested directory does not exist. Keep all data for a game together when migrating. + +When neither local directory exists, new data uses `_codxe\`. T4 singleplayer retains its USB/HDD lookup and requires an existing data root; see the [T4 guide](t4.md). In `codxe.json`, set `"active_mod"` to the name of your mod folder: @@ -19,7 +23,7 @@ In `codxe.json`, set `"active_mod"` to the name of your mod folder: Once configured, the engine will redirect script lookups to: ```text -game:\_codxe\mods\my_mod\ +game:\_codxe\\mods\my_mod\ ``` Example tree structure: @@ -27,14 +31,15 @@ Example tree structure: ```text game:. `-- _codxe - |-- codxe.json - `-- mods - `-- my_mod - `-- maps - `-- mp - `-- gametypes - |-- _callbacksetup.gsc - `-- custom_logic.gsc + `-- + |-- codxe.json + `-- mods + `-- my_mod + `-- maps + `-- mp + `-- gametypes + |-- _callbacksetup.gsc + `-- custom_logic.gsc ``` The underscore keeps the folder at the top of file listings and separates CoD Xe system files from the game's core content. @@ -46,7 +51,7 @@ The loader operates using a virtual filesystem. All `.gsc` scripts, whether load For example: - A file in `common_mp.ff/maps/mp/gametypes/_callbacksetup.gsc` -- Can be overridden by `game:\_codxe\mods\my_mod\maps\mp\gametypes\_callbacksetup.gsc` +- Can be overridden by `game:\_codxe\\mods\my_mod\maps\mp\gametypes\_callbacksetup.gsc` Your version will take precedence over the copy embedded in the original fastfile. You can also add new scripts and call them from overridden entry points such as `_callbacksetup.gsc`. diff --git a/src/common/config.cpp b/src/common/config.cpp index f780173a..d67077ba 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -9,17 +9,11 @@ #define INVALID_FILE_SIZE ((DWORD) - 1) #endif -const char *CONFIG_PATH = "game:\\_codxe\\codxe.json"; -const char *MOD_DIR = "game:\\_codxe\\mods"; -const char *DUMP_DIR = "game:\\_codxe\\dump"; - // Default values char Config::active_mod[MAX_PATH] = {}; bool Config::dump_rawfile = false; bool Config::dump_map_ents = false; -char Config::mod_base_path[MAX_PATH] = {}; std::string Config::data_root; -bool Config::shared_layout_enabled = false; std::vector Config::mounted_links; namespace @@ -31,9 +25,20 @@ const char *GetGameDirectoryName(Config::GameId gameId) { switch (gameId) { + case Config::GAME_IW2: + return "iw2"; + case Config::GAME_IW3: + return "iw3"; + case Config::GAME_IW4: + return "iw4"; + case Config::GAME_IW5: + return "iw5"; case Config::GAME_T4: return "t4"; - case Config::GAME_NONE: + case Config::GAME_T5: + return "t5"; + case Config::GAME_QOS: + return "qos"; default: return nullptr; } @@ -166,14 +171,12 @@ bool ReadFileToString(const char *path, std::string &outString) return true; } -Config::Config(GameId gameId) +Config::Config(GameId gameId, bool allowSharedStorage) { data_root = "game:\\_codxe"; mounted_links.clear(); const char *gameDirectoryName = GetGameDirectoryName(gameId); - shared_layout_enabled = gameDirectoryName != nullptr; - - std::string config_path = CONFIG_PATH; + std::string config_path = filesystem::JoinPath(data_root.c_str(), "codxe.json"); if (xbox::GetEnvironment() != xbox::ENVIRONMENT_XENIA) { @@ -192,7 +195,7 @@ Config::Config(GameId gameId) if (slash != std::string::npos) { // On hardware Config is constructed before game: is retargeted to the new title. - // Only the config file load needs this early executable-relative path. + // Root selection and config loading need this early executable-relative path. config_path = full_path.substr(0, slash) + "\\_codxe\\codxe.json"; std::string mounted_config_path; @@ -207,7 +210,7 @@ Config::Config(GameId gameId) } } - if (shared_layout_enabled) + if (gameDirectoryName != nullptr) { // Config loads before game: is retargeted on hardware, so inspect the // executable-relative path while choosing the single active root. @@ -223,7 +226,7 @@ Config::Config(GameId gameId) { config_path = filesystem::JoinPath(local_directory.c_str(), "codxe.json"); } - else + else if (allowSharedStorage) { data_root.clear(); if (xbox::GetEnvironment() != xbox::ENVIRONMENT_XENIA) @@ -255,6 +258,12 @@ Config::Config(GameId gameId) } } } + else + { + // A fresh install writes to the new layout. Do not reselect the root per file. + data_root = std::string("game:\\_codxe\\") + gameDirectoryName; + config_path = filesystem::JoinPath(nested_local_directory.c_str(), "codxe.json"); + } } bool loaded = false; @@ -275,11 +284,9 @@ Config::~Config() { // Reset to defaults on cleanup active_mod[0] = '\0'; - mod_base_path[0] = '\0'; dump_rawfile = false; dump_map_ents = false; data_root.clear(); - shared_layout_enabled = false; for (size_t i = 0; i < mounted_links.size(); ++i) UnmountDevice(mounted_links[i].c_str()); mounted_links.clear(); @@ -341,13 +348,6 @@ bool Config::LoadFromJson(const char *jsonBuffer, DWORD bufferSize) DbgPrint(" Dump Raw Scripts: %s\n", dump_rawfile ? "true" : "false"); DbgPrint(" Dump Map Entities: %s\n", dump_map_ents ? "true" : "false"); - if (active_mod[0] != '\0') - { - // Config loads before game: is guaranteed to point at the new executable root on hardware. - // Keep the intended path and let later game-lifecycle file accesses validate it naturally. - _snprintf_s(mod_base_path, ARRAYSIZE(mod_base_path), _TRUNCATE, "%s\\%s", MOD_DIR, active_mod); - } - return true; } @@ -373,16 +373,19 @@ const char *Config::GetActiveMod() std::string Config::ResolveModPath(const char *relativePath) { - if (!relativePath || !*relativePath || !mod_base_path[0]) + if (!relativePath || !*relativePath || !active_mod[0]) return std::string(); - if (shared_layout_enabled) - { - const std::string mod_path = filesystem::JoinPath("mods", active_mod); - return ResolveDataPath(filesystem::JoinPath(mod_path.c_str(), relativePath).c_str()); - } + const std::string mod_path = filesystem::JoinPath("mods", active_mod); + return BuildDataPath(filesystem::JoinPath(mod_path.c_str(), relativePath).c_str()); +} + +std::string Config::BuildDataPath(const char *relativePath) +{ + if (!relativePath || data_root.empty()) + return std::string(); - return filesystem::JoinPath(mod_base_path, relativePath); + return filesystem::JoinPath(data_root.c_str(), relativePath); } std::string Config::ResolveDataPath(const char *relativePath) @@ -390,7 +393,7 @@ std::string Config::ResolveDataPath(const char *relativePath) if (!relativePath || !*relativePath || data_root.empty()) return std::string(); - const std::string path = filesystem::JoinPath(data_root.c_str(), relativePath); + const std::string path = BuildDataPath(relativePath); return filesystem::FileExists(path.c_str()) ? path : std::string(); } @@ -399,7 +402,7 @@ std::string Config::ResolveDataDirectory(const char *relativePath) if (!relativePath || !*relativePath || data_root.empty()) return std::string(); - const std::string path = filesystem::JoinPath(data_root.c_str(), relativePath); + const std::string path = BuildDataPath(relativePath); return filesystem::DirectoryExists(path.c_str()) ? path : std::string(); } diff --git a/src/common/config.h b/src/common/config.h index b94a509a..3722b884 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -2,10 +2,6 @@ #include "pch.h" -extern const char *CONFIG_PATH; -extern const char *MOD_DIR; -extern const char *DUMP_DIR; - bool ReadFileToString(const char *path, std::string &outString); class Config : public Module @@ -13,11 +9,16 @@ class Config : public Module public: enum GameId { - GAME_NONE, + GAME_IW2, + GAME_IW3, + GAME_IW4, + GAME_IW5, GAME_T4, + GAME_T5, + GAME_QOS, }; - explicit Config(GameId gameId = GAME_NONE); + explicit Config(GameId gameId, bool allowSharedStorage = false); ~Config(); static bool dump_rawfile; @@ -25,15 +26,15 @@ class Config : public Module static const char *GetActiveMod(); static std::string ResolveModPath(const char *relativePath); + // Build paths within the root selected at startup, including files that do not exist yet. + static std::string BuildDataPath(const char *relativePath); static std::string ResolveDataPath(const char *relativePath); static std::string ResolveDataDirectory(const char *relativePath); static std::string ResolveDataPathForGameFile(const char *relativePath); private: static char active_mod[MAX_PATH]; - static char mod_base_path[MAX_PATH]; static std::string data_root; - static bool shared_layout_enabled; static std::vector mounted_links; bool LoadFromJson(const char *jsonBuffer, DWORD bufferSize); diff --git a/src/common/gsc_loader.cpp b/src/common/gsc_loader.cpp index 0043cd5e..967f3e91 100644 --- a/src/common/gsc_loader.cpp +++ b/src/common/gsc_loader.cpp @@ -52,7 +52,7 @@ void DumpSource(const char *scriptPath, const char *contents) if (!scriptPath || !contents) return; - const std::string dumpPath = filesystem::JoinPath(DUMP_DIR, scriptPath); + const std::string dumpPath = Config::BuildDataPath(filesystem::JoinPath("dump", scriptPath).c_str()); if (dumpPath.empty()) return; diff --git a/src/game/iw2/mp/main.cpp b/src/game/iw2/mp/main.cpp index 7626f32f..dff3a007 100644 --- a/src/game/iw2/mp/main.cpp +++ b/src/game/iw2/mp/main.cpp @@ -11,7 +11,7 @@ namespace mp IW2_MP_Plugin::IW2_MP_Plugin() { - RegisterModule(new Config()); + RegisterModule(new Config(Config::GAME_IW2)); RegisterModule(new GSC()); RegisterModule(new scr_parser()); RegisterModule(new PlayerMovement()); diff --git a/src/game/iw2/sp/main.cpp b/src/game/iw2/sp/main.cpp index f1f31740..00f4f20c 100644 --- a/src/game/iw2/sp/main.cpp +++ b/src/game/iw2/sp/main.cpp @@ -9,7 +9,7 @@ namespace sp IW2_SP_Plugin::IW2_SP_Plugin() { - RegisterModule(new Config()); + RegisterModule(new Config(Config::GAME_IW2)); RegisterModule(new scr_parser()); } diff --git a/src/game/iw3/mp/components/cmds.cpp b/src/game/iw3/mp/components/cmds.cpp index 797ec078..ac920405 100644 --- a/src/game/iw3/mp/components/cmds.cpp +++ b/src/game/iw3/mp/components/cmds.cpp @@ -16,7 +16,7 @@ void Cmd_Dumpraw_f() for (int i = 0; i < count; i++) { auto rawfile = files[i].rawfile; - const std::string dumpPath = filesystem::JoinPath(DUMP_DIR, rawfile->name); + const std::string dumpPath = Config::BuildDataPath(filesystem::JoinPath("dump", rawfile->name).c_str()); filesystem::WriteFileToDisk(dumpPath.c_str(), rawfile->buffer, rawfile->len); } } diff --git a/src/game/iw3/mp/components/fastfiles.cpp b/src/game/iw3/mp/components/fastfiles.cpp index f7697b90..c64db992 100644 --- a/src/game/iw3/mp/components/fastfiles.cpp +++ b/src/game/iw3/mp/components/fastfiles.cpp @@ -14,8 +14,8 @@ const char *const MOD_ZONE = "mod"; const char *const MOD_FASTFILE = "mod.ff"; const char *const CODXE_COMMON_ZONE = "codxe_common_mp"; const char *const CODXE_UI_ZONE = "codxe_ui_mp"; -const char *const CODXE_ZONE_DIRECTORY = "game:\\_codxe\\zone"; -const char *const USERMAPS_DIRECTORY = "game:\\_codxe\\usermaps"; +const char *const CODXE_ZONE_DIRECTORY = "zone"; +const char *const USERMAPS_DIRECTORY = "usermaps"; const char *const FASTFILE_EXTENSION = ".ff"; bool IsSafeZoneName(const char *name) @@ -39,7 +39,7 @@ std::string GetCodxeZoneFastfilePath(const char *zoneName) return std::string(); const std::string filename = std::string(zoneName) + FASTFILE_EXTENSION; - return filesystem::JoinPath(CODXE_ZONE_DIRECTORY, filename.c_str()); + return Config::BuildDataPath(filesystem::JoinPath(CODXE_ZONE_DIRECTORY, filename.c_str()).c_str()); } bool ContainsZone(const XZoneInfo *zoneInfo, unsigned int zoneCount, const char *name) @@ -109,9 +109,9 @@ std::string FastFiles::GetModFastfilePath() return ModList::ResolvePath(MOD_FASTFILE); } -const char *FastFiles::GetUsermapsDirectory() +std::string FastFiles::GetUsermapsDirectory() { - return USERMAPS_DIRECTORY; + return Config::BuildDataPath(USERMAPS_DIRECTORY); } std::string FastFiles::GetUsermapFastfilePath(const char *zoneName) @@ -128,7 +128,7 @@ std::string FastFiles::GetUsermapFastfilePath(const char *zoneName) directory.erase(directory.length() - 5); const std::string usermapDirectory = filesystem::JoinPath(USERMAPS_DIRECTORY, directory.c_str()); - return filesystem::JoinPath(usermapDirectory.c_str(), (filename + ".ff").c_str()); + return Config::BuildDataPath(filesystem::JoinPath(usermapDirectory.c_str(), (filename + ".ff").c_str()).c_str()); } int FastFiles::DB_BuildOSPath_Hook(const char *zoneName, unsigned int size, char *filename) diff --git a/src/game/iw3/mp/components/fastfiles.h b/src/game/iw3/mp/components/fastfiles.h index 6ef3fc3d..5c44d2bf 100644 --- a/src/game/iw3/mp/components/fastfiles.h +++ b/src/game/iw3/mp/components/fastfiles.h @@ -12,7 +12,7 @@ class FastFiles : public Module FastFiles(); ~FastFiles(); - static const char *GetUsermapsDirectory(); + static std::string GetUsermapsDirectory(); static std::string GetUsermapFastfilePath(const char *zoneName); static void ReloadModZone(); diff --git a/src/game/iw3/mp/components/image_loader.cpp b/src/game/iw3/mp/components/image_loader.cpp index 77203175..9f0a626d 100644 --- a/src/game/iw3/mp/components/image_loader.cpp +++ b/src/game/iw3/mp/components/image_loader.cpp @@ -412,7 +412,7 @@ std::string GetImageDumpPath(const char *imageName) sanitizedName.erase(std::remove_if(sanitizedName.begin(), sanitizedName.end(), [](char c) { return c == '*'; }), sanitizedName.end()); const std::string relativePath = GetImageRelativePath(sanitizedName.c_str()); - return filesystem::JoinPath(DUMP_DIR, relativePath.c_str()); + return Config::BuildDataPath(filesystem::JoinPath("dump", relativePath.c_str()).c_str()); } bool ReadBinaryFile(const std::string &path, std::vector *data) @@ -685,7 +685,7 @@ void Cmd_imagedump() ImageList imageList; R_GetImageList(&imageList); - const std::string imageDumpDirectory = filesystem::JoinPath(DUMP_DIR, "images"); + const std::string imageDumpDirectory = Config::BuildDataPath(filesystem::JoinPath("dump", "images").c_str()); filesystem::CreateDirectories(imageDumpDirectory.c_str()); for (unsigned int i = 0; i < imageList.count; ++i) diff --git a/src/game/iw3/mp/components/mods.cpp b/src/game/iw3/mp/components/mods.cpp index 8c614cf9..d7627eb7 100644 --- a/src/game/iw3/mp/components/mods.cpp +++ b/src/game/iw3/mp/components/mods.cpp @@ -11,7 +11,6 @@ namespace mp namespace { const float MODS_FEEDER_ID = 9.0f; -const char *const MODS_DIRECTORY = "game:\\_codxe\\mods"; const char *const FS_GAME_PREFIX = "mods/"; bool IsSafeModName(const char *name) @@ -85,7 +84,7 @@ std::string ModList::GetActiveName() std::string ModList::GetActivePath() { const std::string name = GetActiveName(); - return name.empty() ? std::string() : filesystem::JoinPath(MODS_DIRECTORY, name.c_str()); + return name.empty() ? std::string() : Config::BuildDataPath(filesystem::JoinPath("mods", name.c_str()).c_str()); } std::string ModList::ResolvePath(const char *relativePath) @@ -102,7 +101,7 @@ bool ModList::RunMod(const char *name) if (!fs_game || !IsSafeModName(name)) return false; - const std::string modPath = filesystem::JoinPath(MODS_DIRECTORY, name); + const std::string modPath = Config::BuildDataPath(filesystem::JoinPath("mods", name).c_str()); if (!filesystem::DirectoryExists(modPath.c_str())) { Com_PrintError(CON_CHANNEL_ERROR, "Mod directory does not exist: %s\n", modPath.c_str()); @@ -139,12 +138,13 @@ void ModList::ScanMods() ModsScanned = true; UIFeeder::SetSelectedIndex(MODS_FEEDER_ID, CurrentMod); - const std::string searchPattern = filesystem::JoinPath(MODS_DIRECTORY, "*"); + const std::string modsDirectory = Config::BuildDataPath("mods"); + const std::string searchPattern = filesystem::JoinPath(modsDirectory.c_str(), "*"); WIN32_FIND_DATAA findData; HANDLE findHandle = FindFirstFileA(searchPattern.c_str(), &findData); if (findHandle == INVALID_HANDLE_VALUE) { - DbgPrint("[codxe][IW3][ModList] Mod directory is unavailable: %s\n", MODS_DIRECTORY); + DbgPrint("[codxe][IW3][ModList] Mod directory is unavailable: %s\n", modsDirectory.c_str()); return; } @@ -156,7 +156,7 @@ void ModList::ScanMods() FeederEntry entry; entry.name = name; - const std::string directory = filesystem::JoinPath(MODS_DIRECTORY, name.c_str()); + const std::string directory = Config::BuildDataPath(filesystem::JoinPath("mods", name.c_str()).c_str()); entry.displayName = ReadDisplayName(filesystem::JoinPath(directory.c_str(), "description.txt"), name); Mods.push_back(entry); } while (FindNextFileA(findHandle, &findData) != 0); @@ -176,7 +176,8 @@ void ModList::ScanMods() UIFeeder::SetSelectedIndex(MODS_FEEDER_ID, CurrentMod); - DbgPrint("[codxe][IW3][ModList] Found %u mod(s) in %s\n", static_cast(Mods.size()), MODS_DIRECTORY); + DbgPrint("[codxe][IW3][ModList] Found %u mod(s) in %s\n", static_cast(Mods.size()), + modsDirectory.c_str()); } void ModList::EnsureModsScanned() diff --git a/src/game/iw3/mp/components/mpsp.cpp b/src/game/iw3/mp/components/mpsp.cpp index 5cdd45db..0a32a47b 100644 --- a/src/game/iw3/mp/components/mpsp.cpp +++ b/src/game/iw3/mp/components/mpsp.cpp @@ -114,7 +114,7 @@ namespace Asset std::string get_load_dir() { - return "game:\\_codxe\\internal"; + return Config::BuildDataPath("internal"); } namespace MapEnts_ { diff --git a/src/game/iw3/mp/components/stats.cpp b/src/game/iw3/mp/components/stats.cpp index 6ff68b63..af285b17 100644 --- a/src/game/iw3/mp/components/stats.cpp +++ b/src/game/iw3/mp/components/stats.cpp @@ -398,7 +398,7 @@ std::string GetStatsPath(unsigned int controllerIndex) _snprintf_s(filename, ARRAYSIZE(filename), _TRUNCATE, "controller_%u.stat", controllerIndex); } - return filesystem::JoinPath("game:\\_codxe\\players", filename); + return Config::BuildDataPath(filesystem::JoinPath("players", filename).c_str()); } bool LoadStats(unsigned int controllerIndex) diff --git a/src/game/iw3/mp/components/ui_feeder.cpp b/src/game/iw3/mp/components/ui_feeder.cpp index 51310abc..021b848f 100644 --- a/src/game/iw3/mp/components/ui_feeder.cpp +++ b/src/game/iw3/mp/components/ui_feeder.cpp @@ -78,14 +78,14 @@ void ScanUsermaps() usermapsScanned = true; UIFeeder::SetSelectedIndex(USERMAPS_FEEDER_ID, selectedUsermap); - const char *usermapsDirectory = FastFiles::GetUsermapsDirectory(); - const std::string searchPattern = filesystem::JoinPath(usermapsDirectory, "*"); + const std::string usermapsDirectory = FastFiles::GetUsermapsDirectory(); + const std::string searchPattern = filesystem::JoinPath(usermapsDirectory.c_str(), "*"); WIN32_FIND_DATAA findData; HANDLE findHandle = FindFirstFileA(searchPattern.c_str(), &findData); if (findHandle == INVALID_HANDLE_VALUE) { UpdateUsermapCounter(); - DbgPrint("[codxe][IW3][UIFeeder] Usermap directory is unavailable: %s\n", usermapsDirectory); + DbgPrint("[codxe][IW3][UIFeeder] Usermap directory is unavailable: %s\n", usermapsDirectory.c_str()); return; } @@ -104,7 +104,7 @@ void ScanUsermaps() FeederEntry entry; entry.name = name; - const std::string directory = filesystem::JoinPath(usermapsDirectory, name.c_str()); + const std::string directory = filesystem::JoinPath(usermapsDirectory.c_str(), name.c_str()); entry.displayName = ReadDisplayName(filesystem::JoinPath(directory.c_str(), "description.txt"), name); usermaps.push_back(entry); } while (FindNextFileA(findHandle, &findData) != 0); @@ -126,7 +126,7 @@ void ScanUsermaps() UpdateUsermapCounter(); DbgPrint("[codxe][IW3][UIFeeder] Found %u usermap(s) in %s\n", static_cast(usermaps.size()), - usermapsDirectory); + usermapsDirectory.c_str()); } void EnsureUsermapsScanned() diff --git a/src/game/iw3/mp/main.cpp b/src/game/iw3/mp/main.cpp index a59008cd..ab4f02be 100644 --- a/src/game/iw3/mp/main.cpp +++ b/src/game/iw3/mp/main.cpp @@ -38,6 +38,7 @@ IW3_MP_Plugin::IW3_MP_Plugin() // Special modules need to be registered first RegisterModule(new Patches()); + RegisterModule(new Config(Config::GAME_IW3)); RegisterModule(new Events()); RegisterModule(new command()); RegisterModule(new UIScript()); diff --git a/src/game/iw3/sp/main.cpp b/src/game/iw3/sp/main.cpp index c90a2da6..d32a2796 100644 --- a/src/game/iw3/sp/main.cpp +++ b/src/game/iw3/sp/main.cpp @@ -84,7 +84,7 @@ void Load_MapEntsPtr_Hook() // Dump stock map ents if enabled (numEntityChars includes the null terminator, which is not written) if (Config::dump_map_ents && mapEnts->numEntityChars > 0) { - const std::string dumpPath = map_ents::BuildPath(DUMP_DIR, mapEnts->name); + const std::string dumpPath = map_ents::BuildPath(Config::BuildDataPath("dump").c_str(), mapEnts->name); if (!dumpPath.empty()) { filesystem::WriteFileToDisk(dumpPath.c_str(), mapEnts->entityString, mapEnts->numEntityChars - 1); @@ -138,7 +138,7 @@ void Cmd_Dumpraw_f() for (int i = 0; i < count; i++) { auto rawfile = files[i].rawfile; - const std::string dumpPath = filesystem::JoinPath(DUMP_DIR, rawfile->name); + const std::string dumpPath = Config::BuildDataPath(filesystem::JoinPath("dump", rawfile->name).c_str()); filesystem::WriteFileToDisk(dumpPath.c_str(), rawfile->buffer, rawfile->len); } } @@ -146,7 +146,7 @@ void Cmd_Dumpraw_f() IW3_SP_Plugin::IW3_SP_Plugin() { DbgPrint("IW3 SP: Plugin loaded\n"); - RegisterModule(new Config()); + RegisterModule(new Config(Config::GAME_IW3)); RegisterModule(new Events()); RegisterModule(new command()); RegisterModule(new scr_parser()); diff --git a/src/game/iw4/mp/components/fastfiles.cpp b/src/game/iw4/mp/components/fastfiles.cpp index 1a9bfe18..455c1f30 100644 --- a/src/game/iw4/mp/components/fastfiles.cpp +++ b/src/game/iw4/mp/components/fastfiles.cpp @@ -8,16 +8,8 @@ namespace mp namespace { const char *const CODXE_PATCH_ZONE = "codxe_patch_mp"; -const char *const CODXE_PATCH_FILENAME = "codxe_patch_mp.ff"; -const char *const CODXE_PATCH_RELATIVE_PATH = "_codxe\\zone\\codxe_patch_mp.ff"; -const char *const CODXE_PATCH_PATH = "game:\\_codxe\\zone\\codxe_patch_mp.ff"; const char *const CODXE_UI_ZONE = "codxe_ui_mp"; -const char *const CODXE_UI_FILENAME = "codxe_ui_mp.ff"; -const char *const CODXE_UI_RELATIVE_PATH = "_codxe\\zone\\codxe_ui_mp.ff"; -const char *const CODXE_UI_PATH = "game:\\_codxe\\zone\\codxe_ui_mp.ff"; -const char *const CODXE_ZONE_RELATIVE_DIRECTORY = "_codxe\\zone\\"; const char *const CODXE_IMAGEFILE_FILENAME = "imagefile5.pak"; -const char *const GAME_DEVICE_PREFIX = "game:\\"; const char *const FASTFILE_EXTENSION = ".ff"; const unsigned int MAX_ZONE_COUNT = 32; @@ -79,8 +71,8 @@ bool IsSafeFastfileName(const char *filename) void DB_LoadXAssets_Hook(XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) { auto original = DB_LoadXAssets_Detour.GetOriginal(); - const bool patchFileExists = filesystem::FileExists(CODXE_PATCH_PATH); - const bool uiFileExists = filesystem::FileExists(CODXE_UI_PATH); + const bool patchFileExists = !Config::ResolveDataPath("zone\\codxe_patch_mp.ff").empty(); + const bool uiFileExists = !Config::ResolveDataPath("zone\\codxe_ui_mp.ff").empty(); const XZoneInfo *stockPatchZone = FindZone(zoneInfo, zoneCount, "patch_mp"); const XZoneInfo *stockUiZone = FindZone(zoneInfo, zoneCount, "ui_mp"); const bool injectPatch = patchFileExists && stockPatchZone; @@ -126,28 +118,12 @@ int Sys_CreateFile_Hook(const char *dir, const char *filename) { auto original = Sys_CreateFile_Detour.GetOriginal(); - if (filename && std::strcmp(filename, CODXE_PATCH_FILENAME) == 0) - return original(dir, CODXE_PATCH_RELATIVE_PATH); - - if (filename && std::strcmp(filename, CODXE_UI_FILENAME) == 0) - return original(dir, CODXE_UI_RELATIVE_PATH); - - if (filename && std::strcmp(filename, CODXE_IMAGEFILE_FILENAME) == 0) + if (filename && (std::strcmp(filename, CODXE_IMAGEFILE_FILENAME) == 0 || IsSafeFastfileName(filename))) { - const std::string relativePath = std::string(CODXE_ZONE_RELATIVE_DIRECTORY) + filename; - const std::string devicePath = std::string(GAME_DEVICE_PREFIX) + relativePath; - - if (filesystem::FileExists(devicePath.c_str())) - return original(dir, relativePath.c_str()); - } - - if (IsSafeFastfileName(filename)) - { - const std::string relativePath = std::string(CODXE_ZONE_RELATIVE_DIRECTORY) + filename; - const std::string devicePath = std::string(GAME_DEVICE_PREFIX) + relativePath; - - if (filesystem::FileExists(devicePath.c_str())) - return original(dir, relativePath.c_str()); + const std::string path = Config::ResolveDataPath(filesystem::JoinPath("zone", filename).c_str()); + // Sys_CreateFile prepends the title directory, so pass a title-relative filename. + if (path.compare(0, 6, "game:\\") == 0) + return original(dir, path.substr(6).c_str()); } return original(dir, filename); diff --git a/src/game/iw4/mp/main.cpp b/src/game/iw4/mp/main.cpp index d5844ee3..7aebbe52 100644 --- a/src/game/iw4/mp/main.cpp +++ b/src/game/iw4/mp/main.cpp @@ -13,6 +13,7 @@ namespace mp IW4_MP_Plugin::IW4_MP_Plugin() { + RegisterModule(new Config(Config::GAME_IW4)); RegisterModule(new Events()); RegisterModule(new fastfiles()); RegisterModule(new Branding()); diff --git a/src/game/iw4/mp_tu6/components/fastfiles.cpp b/src/game/iw4/mp_tu6/components/fastfiles.cpp index 24c1c816..9ecd6e95 100644 --- a/src/game/iw4/mp_tu6/components/fastfiles.cpp +++ b/src/game/iw4/mp_tu6/components/fastfiles.cpp @@ -8,16 +8,8 @@ namespace mp_tu6 namespace { const char *const CODXE_PATCH_ZONE = "codxe_patch_mp"; -const char *const CODXE_PATCH_FILENAME = "codxe_patch_mp.ff"; -const char *const CODXE_PATCH_RELATIVE_PATH = "_codxe\\zone\\codxe_patch_mp.ff"; -const char *const CODXE_PATCH_PATH = "game:\\_codxe\\zone\\codxe_patch_mp.ff"; const char *const CODXE_UI_ZONE = "codxe_ui_mp"; -const char *const CODXE_UI_FILENAME = "codxe_ui_mp.ff"; -const char *const CODXE_UI_RELATIVE_PATH = "_codxe\\zone\\codxe_ui_mp.ff"; -const char *const CODXE_UI_PATH = "game:\\_codxe\\zone\\codxe_ui_mp.ff"; -const char *const CODXE_ZONE_RELATIVE_DIRECTORY = "_codxe\\zone\\"; const char *const CODXE_IMAGEFILE_FILENAME = "imagefile5.pak"; -const char *const GAME_DEVICE_PREFIX = "game:\\"; const char *const FASTFILE_EXTENSION = ".ff"; const unsigned int MAX_ZONE_COUNT = 32; @@ -86,8 +78,8 @@ bool IsSafeFastfileName(const char *filename) void DB_LoadXAssets_Hook(XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) { auto original = DB_LoadXAssets_Detour.GetOriginal(); - const bool patchFileExists = filesystem::FileExists(CODXE_PATCH_PATH); - const bool uiFileExists = filesystem::FileExists(CODXE_UI_PATH); + const bool patchFileExists = !Config::ResolveDataPath("zone\\codxe_patch_mp.ff").empty(); + const bool uiFileExists = !Config::ResolveDataPath("zone\\codxe_ui_mp.ff").empty(); const XZoneInfo *stockPatchZone = FindZone(zoneInfo, zoneCount, "patch_mp"); const XZoneInfo *stockUiZone = FindZone(zoneInfo, zoneCount, "ui_mp"); const bool injectPatch = patchFileExists && stockPatchZone; @@ -133,28 +125,12 @@ SysFile Sys_CreateFile_Hook(const char *dir, const char *filename) { auto original = Sys_CreateFile_Detour.GetOriginal(); - if (filename && std::strcmp(filename, CODXE_PATCH_FILENAME) == 0) - return original(dir, CODXE_PATCH_RELATIVE_PATH); - - if (filename && std::strcmp(filename, CODXE_UI_FILENAME) == 0) - return original(dir, CODXE_UI_RELATIVE_PATH); - - if (filename && std::strcmp(filename, CODXE_IMAGEFILE_FILENAME) == 0) + if (filename && (std::strcmp(filename, CODXE_IMAGEFILE_FILENAME) == 0 || IsSafeFastfileName(filename))) { - const std::string relativePath = std::string(CODXE_ZONE_RELATIVE_DIRECTORY) + filename; - const std::string devicePath = std::string(GAME_DEVICE_PREFIX) + relativePath; - - if (filesystem::FileExists(devicePath.c_str())) - return original(dir, relativePath.c_str()); - } - - if (IsSafeFastfileName(filename)) - { - const std::string relativePath = std::string(CODXE_ZONE_RELATIVE_DIRECTORY) + filename; - const std::string devicePath = std::string(GAME_DEVICE_PREFIX) + relativePath; - - if (filesystem::FileExists(devicePath.c_str())) - return original(dir, relativePath.c_str()); + const std::string path = Config::ResolveDataPath(filesystem::JoinPath("zone", filename).c_str()); + // Sys_CreateFile prepends the title directory, so pass a title-relative filename. + if (path.compare(0, 6, "game:\\") == 0) + return original(dir, path.substr(6).c_str()); } return original(dir, filename); diff --git a/src/game/iw4/mp_tu6/main.cpp b/src/game/iw4/mp_tu6/main.cpp index 28585c60..6e75cafa 100644 --- a/src/game/iw4/mp_tu6/main.cpp +++ b/src/game/iw4/mp_tu6/main.cpp @@ -22,7 +22,7 @@ namespace mp_tu6 IW4_MP_TU6_Plugin::IW4_MP_TU6_Plugin() { - RegisterModule(new Config()); + RegisterModule(new Config(Config::GAME_IW4)); RegisterModule(new Events()); // Must be registered first to ensure hooks are in place RegisterModule(new fastfiles()); RegisterModule(new cg()); diff --git a/src/game/iw4/sp/components/clipmap.cpp b/src/game/iw4/sp/components/clipmap.cpp index 4e295b3f..ee025d96 100644 --- a/src/game/iw4/sp/components/clipmap.cpp +++ b/src/game/iw4/sp/components/clipmap.cpp @@ -20,7 +20,7 @@ void Load_clipMap_t_Hook(bool atStreamStart) // Dump map entities if enabled if (Config::dump_map_ents) { - const std::string dumpPath = map_ents::BuildPath(DUMP_DIR, mapEnts->name); + const std::string dumpPath = map_ents::BuildPath(Config::BuildDataPath("dump").c_str(), mapEnts->name); if (!dumpPath.empty()) { filesystem::WriteFileToDisk(dumpPath.c_str(), mapEnts->entityString, mapEnts->numEntityChars - 1); diff --git a/src/game/iw4/sp/main.cpp b/src/game/iw4/sp/main.cpp index a5aaebaa..96a75725 100644 --- a/src/game/iw4/sp/main.cpp +++ b/src/game/iw4/sp/main.cpp @@ -33,7 +33,7 @@ IW4_SP_Plugin::IW4_SP_Plugin() RemoveIdleGunSway(); - RegisterModule(new Config()); + RegisterModule(new Config(Config::GAME_IW4)); RegisterModule(new cg()); RegisterModule(new clipmap()); RegisterModule(new GSCFields()); diff --git a/src/game/iw5/mp/main.cpp b/src/game/iw5/mp/main.cpp index ccf809da..2a47dc79 100644 --- a/src/game/iw5/mp/main.cpp +++ b/src/game/iw5/mp/main.cpp @@ -219,7 +219,7 @@ bool DB_IsXAssetDefault_Hook(XAssetType type, const char *name) IW5_MP_Plugin::IW5_MP_Plugin() { - RegisterModule(new Config()); + RegisterModule(new Config(Config::GAME_IW5)); RegisterModule(new Events()); RegisterModule(new Branding()); RegisterModule(new patches()); diff --git a/src/game/qos/mp/main.cpp b/src/game/qos/mp/main.cpp index c9f14f9c..32ccd8c9 100644 --- a/src/game/qos/mp/main.cpp +++ b/src/game/qos/mp/main.cpp @@ -14,7 +14,7 @@ namespace mp QOS_MP_Plugin::QOS_MP_Plugin() { DbgPrint("QOS SP: Registering modules\n"); - RegisterModule(new Config()); + RegisterModule(new Config(Config::GAME_QOS)); RegisterModule(new Events()); RegisterModule(new clipmap()); RegisterModule(new GSC()); diff --git a/src/game/qos/sp/main.cpp b/src/game/qos/sp/main.cpp index 844b9e8a..b3e77381 100644 --- a/src/game/qos/sp/main.cpp +++ b/src/game/qos/sp/main.cpp @@ -9,7 +9,7 @@ namespace sp QOS_SP_Plugin::QOS_SP_Plugin() { DbgPrint("QOS SP: Registering modules\n"); - RegisterModule(new Config()); + RegisterModule(new Config(Config::GAME_QOS)); RegisterModule(new scr_parser()); } diff --git a/src/game/t4/mp/components/map.cpp b/src/game/t4/mp/components/map.cpp index ed120e0c..273832f4 100644 --- a/src/game/t4/mp/components/map.cpp +++ b/src/game/t4/mp/components/map.cpp @@ -68,7 +68,7 @@ void Load_clipMap_t_Hook(bool atStreamStart) // Dump map entities if enabled if (Config::dump_map_ents) { - const std::string dumpPath = map_ents::BuildPath(DUMP_DIR, mapEnts->name); + const std::string dumpPath = map_ents::BuildPath(Config::BuildDataPath("dump").c_str(), mapEnts->name); if (!dumpPath.empty()) { filesystem::WriteFileToDisk(dumpPath.c_str(), mapEnts->entityString, mapEnts->numEntityChars - 1); diff --git a/src/game/t4/mp/main.cpp b/src/game/t4/mp/main.cpp index 80db9204..bbe3aaa6 100644 --- a/src/game/t4/mp/main.cpp +++ b/src/game/t4/mp/main.cpp @@ -27,7 +27,7 @@ T4_MP_Plugin::T4_MP_Plugin() RegisterModule(new Debug()); #endif - RegisterModule(new Config()); + RegisterModule(new Config(Config::GAME_T4)); RegisterModule(new Events()); // Must be registered before modules that subscribe to engine events. RegisterModule(new Branding()); RegisterModule(new BrushCollision()); diff --git a/src/game/t4/sp/components/clipmap.cpp b/src/game/t4/sp/components/clipmap.cpp index c07da257..23d6cd17 100644 --- a/src/game/t4/sp/components/clipmap.cpp +++ b/src/game/t4/sp/components/clipmap.cpp @@ -68,7 +68,7 @@ void Load_clipMap_t_Hook(bool atStreamStart) // Dump map entities if enabled if (Config::dump_map_ents) { - const std::string dumpPath = map_ents::BuildPath(DUMP_DIR, mapEnts->name); + const std::string dumpPath = map_ents::BuildPath(Config::BuildDataPath("dump").c_str(), mapEnts->name); if (!dumpPath.empty()) { filesystem::WriteFileToDisk(dumpPath.c_str(), mapEnts->entityString, mapEnts->numEntityChars - 1); diff --git a/src/game/t4/sp/main.cpp b/src/game/t4/sp/main.cpp index 375607b6..a7bb36f0 100644 --- a/src/game/t4/sp/main.cpp +++ b/src/game/t4/sp/main.cpp @@ -61,7 +61,7 @@ T4_SP_Plugin::T4_SP_Plugin() // Default ui_autoContinue on so level loads do not wait for input in solo or split-screen. *(volatile uint8_t *)0x82279B07 = 0x01; - RegisterModule(new Config(Config::GAME_T4)); + RegisterModule(new Config(Config::GAME_T4, true)); RegisterModule(new Events()); // Must be registered before modules that subscribe to engine events. RegisterModule(new FastFiles()); RegisterModule(new clipmap()); diff --git a/src/game/t5/mp/main.cpp b/src/game/t5/mp/main.cpp index bfb0dec3..f62dc88f 100644 --- a/src/game/t5/mp/main.cpp +++ b/src/game/t5/mp/main.cpp @@ -10,7 +10,7 @@ namespace mp T5_MP_Plugin::T5_MP_Plugin() { DbgPrint("T5 MP: Registering modules\n"); - RegisterModule(new Config()); + RegisterModule(new Config(Config::GAME_T5)); RegisterModule(new scr_parser()); } diff --git a/src/game/t5/sp/main.cpp b/src/game/t5/sp/main.cpp index dcb73bd3..e292c366 100644 --- a/src/game/t5/sp/main.cpp +++ b/src/game/t5/sp/main.cpp @@ -10,7 +10,7 @@ namespace sp T5_SP_Plugin::T5_SP_Plugin() { DbgPrint("T5 SP: Registering modules\n"); - RegisterModule(new Config()); + RegisterModule(new Config(Config::GAME_T5)); RegisterModule(new scr_parser()); } From 9ad9bbb3ccb497541fa00f71add5da508583c523 Mon Sep 17 00:00:00 2001 From: Michael Oliver Date: Wed, 7 Oct 2026 07:38:54 +0100 Subject: [PATCH 2/5] refactor: move game IDs into shared GameId namespace --- codxe.vcxproj | 2 ++ src/common/config.cpp | 27 ++------------------------- src/common/config.h | 14 ++------------ src/common/game_id.cpp | 28 ++++++++++++++++++++++++++++ src/common/game_id.h | 17 +++++++++++++++++ src/game/iw2/mp/main.cpp | 2 +- src/game/iw2/sp/main.cpp | 2 +- src/game/iw3/mp/main.cpp | 2 +- src/game/iw3/sp/main.cpp | 2 +- src/game/iw4/mp/main.cpp | 2 +- src/game/iw4/mp_tu6/main.cpp | 2 +- src/game/iw4/sp/main.cpp | 2 +- src/game/iw5/mp/main.cpp | 2 +- src/game/qos/mp/main.cpp | 2 +- src/game/qos/sp/main.cpp | 2 +- src/game/t4/mp/main.cpp | 2 +- src/game/t4/sp/main.cpp | 2 +- src/game/t5/mp/main.cpp | 2 +- src/game/t5/sp/main.cpp | 2 +- 19 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 src/common/game_id.cpp create mode 100644 src/common/game_id.h diff --git a/codxe.vcxproj b/codxe.vcxproj index 902aba34..a2a1bfa4 100644 --- a/codxe.vcxproj +++ b/codxe.vcxproj @@ -85,6 +85,7 @@ + @@ -235,6 +236,7 @@ + diff --git a/src/common/config.cpp b/src/common/config.cpp index d67077ba..8dd08efc 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -21,29 +21,6 @@ namespace const char *CONFIG_DEVICE_LINK_NAME = "codxe:"; const char *CONFIG_DEVICE_LINK_PATH = "codxe:\\"; -const char *GetGameDirectoryName(Config::GameId gameId) -{ - switch (gameId) - { - case Config::GAME_IW2: - return "iw2"; - case Config::GAME_IW3: - return "iw3"; - case Config::GAME_IW4: - return "iw4"; - case Config::GAME_IW5: - return "iw5"; - case Config::GAME_T4: - return "t4"; - case Config::GAME_T5: - return "t5"; - case Config::GAME_QOS: - return "qos"; - default: - return nullptr; - } -} - bool StartsWith(const std::string &value, const char *prefix) { return strncmp(value.c_str(), prefix, strlen(prefix)) == 0; @@ -171,11 +148,11 @@ bool ReadFileToString(const char *path, std::string &outString) return true; } -Config::Config(GameId gameId, bool allowSharedStorage) +Config::Config(GameId::Type gameId, bool allowSharedStorage) { data_root = "game:\\_codxe"; mounted_links.clear(); - const char *gameDirectoryName = GetGameDirectoryName(gameId); + const char *gameDirectoryName = GameId::ToString(gameId); std::string config_path = filesystem::JoinPath(data_root.c_str(), "codxe.json"); if (xbox::GetEnvironment() != xbox::ENVIRONMENT_XENIA) diff --git a/src/common/config.h b/src/common/config.h index 3722b884..2989b99e 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -1,24 +1,14 @@ #pragma once #include "pch.h" +#include "game_id.h" bool ReadFileToString(const char *path, std::string &outString); class Config : public Module { public: - enum GameId - { - GAME_IW2, - GAME_IW3, - GAME_IW4, - GAME_IW5, - GAME_T4, - GAME_T5, - GAME_QOS, - }; - - explicit Config(GameId gameId, bool allowSharedStorage = false); + explicit Config(GameId::Type gameId, bool allowSharedStorage = false); ~Config(); static bool dump_rawfile; diff --git a/src/common/game_id.cpp b/src/common/game_id.cpp new file mode 100644 index 00000000..357aad11 --- /dev/null +++ b/src/common/game_id.cpp @@ -0,0 +1,28 @@ +#include "pch.h" +#include "game_id.h" + +namespace GameId +{ +const char *ToString(Type gameId) +{ + switch (gameId) + { + case IW2: + return "iw2"; + case IW3: + return "iw3"; + case IW4: + return "iw4"; + case IW5: + return "iw5"; + case T4: + return "t4"; + case T5: + return "t5"; + case QOS: + return "qos"; + default: + return nullptr; + } +} +} // namespace GameId diff --git a/src/common/game_id.h b/src/common/game_id.h new file mode 100644 index 00000000..c2064aae --- /dev/null +++ b/src/common/game_id.h @@ -0,0 +1,17 @@ +#pragma once + +namespace GameId +{ +enum Type +{ + IW2, + IW3, + IW4, + IW5, + T4, + T5, + QOS, +}; + +const char *ToString(Type gameId); +} // namespace GameId diff --git a/src/game/iw2/mp/main.cpp b/src/game/iw2/mp/main.cpp index dff3a007..211c0ee9 100644 --- a/src/game/iw2/mp/main.cpp +++ b/src/game/iw2/mp/main.cpp @@ -11,7 +11,7 @@ namespace mp IW2_MP_Plugin::IW2_MP_Plugin() { - RegisterModule(new Config(Config::GAME_IW2)); + RegisterModule(new Config(GameId::IW2)); RegisterModule(new GSC()); RegisterModule(new scr_parser()); RegisterModule(new PlayerMovement()); diff --git a/src/game/iw2/sp/main.cpp b/src/game/iw2/sp/main.cpp index 00f4f20c..2463753f 100644 --- a/src/game/iw2/sp/main.cpp +++ b/src/game/iw2/sp/main.cpp @@ -9,7 +9,7 @@ namespace sp IW2_SP_Plugin::IW2_SP_Plugin() { - RegisterModule(new Config(Config::GAME_IW2)); + RegisterModule(new Config(GameId::IW2)); RegisterModule(new scr_parser()); } diff --git a/src/game/iw3/mp/main.cpp b/src/game/iw3/mp/main.cpp index ab4f02be..0b8132d3 100644 --- a/src/game/iw3/mp/main.cpp +++ b/src/game/iw3/mp/main.cpp @@ -38,7 +38,7 @@ IW3_MP_Plugin::IW3_MP_Plugin() // Special modules need to be registered first RegisterModule(new Patches()); - RegisterModule(new Config(Config::GAME_IW3)); + RegisterModule(new Config(GameId::IW3)); RegisterModule(new Events()); RegisterModule(new command()); RegisterModule(new UIScript()); diff --git a/src/game/iw3/sp/main.cpp b/src/game/iw3/sp/main.cpp index d32a2796..7fbd73e8 100644 --- a/src/game/iw3/sp/main.cpp +++ b/src/game/iw3/sp/main.cpp @@ -146,7 +146,7 @@ void Cmd_Dumpraw_f() IW3_SP_Plugin::IW3_SP_Plugin() { DbgPrint("IW3 SP: Plugin loaded\n"); - RegisterModule(new Config(Config::GAME_IW3)); + RegisterModule(new Config(GameId::IW3)); RegisterModule(new Events()); RegisterModule(new command()); RegisterModule(new scr_parser()); diff --git a/src/game/iw4/mp/main.cpp b/src/game/iw4/mp/main.cpp index 7aebbe52..5198da6b 100644 --- a/src/game/iw4/mp/main.cpp +++ b/src/game/iw4/mp/main.cpp @@ -13,7 +13,7 @@ namespace mp IW4_MP_Plugin::IW4_MP_Plugin() { - RegisterModule(new Config(Config::GAME_IW4)); + RegisterModule(new Config(GameId::IW4)); RegisterModule(new Events()); RegisterModule(new fastfiles()); RegisterModule(new Branding()); diff --git a/src/game/iw4/mp_tu6/main.cpp b/src/game/iw4/mp_tu6/main.cpp index 6e75cafa..873088fd 100644 --- a/src/game/iw4/mp_tu6/main.cpp +++ b/src/game/iw4/mp_tu6/main.cpp @@ -22,7 +22,7 @@ namespace mp_tu6 IW4_MP_TU6_Plugin::IW4_MP_TU6_Plugin() { - RegisterModule(new Config(Config::GAME_IW4)); + RegisterModule(new Config(GameId::IW4)); RegisterModule(new Events()); // Must be registered first to ensure hooks are in place RegisterModule(new fastfiles()); RegisterModule(new cg()); diff --git a/src/game/iw4/sp/main.cpp b/src/game/iw4/sp/main.cpp index 96a75725..48e472aa 100644 --- a/src/game/iw4/sp/main.cpp +++ b/src/game/iw4/sp/main.cpp @@ -33,7 +33,7 @@ IW4_SP_Plugin::IW4_SP_Plugin() RemoveIdleGunSway(); - RegisterModule(new Config(Config::GAME_IW4)); + RegisterModule(new Config(GameId::IW4)); RegisterModule(new cg()); RegisterModule(new clipmap()); RegisterModule(new GSCFields()); diff --git a/src/game/iw5/mp/main.cpp b/src/game/iw5/mp/main.cpp index 2a47dc79..f5bf38c7 100644 --- a/src/game/iw5/mp/main.cpp +++ b/src/game/iw5/mp/main.cpp @@ -219,7 +219,7 @@ bool DB_IsXAssetDefault_Hook(XAssetType type, const char *name) IW5_MP_Plugin::IW5_MP_Plugin() { - RegisterModule(new Config(Config::GAME_IW5)); + RegisterModule(new Config(GameId::IW5)); RegisterModule(new Events()); RegisterModule(new Branding()); RegisterModule(new patches()); diff --git a/src/game/qos/mp/main.cpp b/src/game/qos/mp/main.cpp index 32ccd8c9..98705e67 100644 --- a/src/game/qos/mp/main.cpp +++ b/src/game/qos/mp/main.cpp @@ -14,7 +14,7 @@ namespace mp QOS_MP_Plugin::QOS_MP_Plugin() { DbgPrint("QOS SP: Registering modules\n"); - RegisterModule(new Config(Config::GAME_QOS)); + RegisterModule(new Config(GameId::QOS)); RegisterModule(new Events()); RegisterModule(new clipmap()); RegisterModule(new GSC()); diff --git a/src/game/qos/sp/main.cpp b/src/game/qos/sp/main.cpp index b3e77381..761495e8 100644 --- a/src/game/qos/sp/main.cpp +++ b/src/game/qos/sp/main.cpp @@ -9,7 +9,7 @@ namespace sp QOS_SP_Plugin::QOS_SP_Plugin() { DbgPrint("QOS SP: Registering modules\n"); - RegisterModule(new Config(Config::GAME_QOS)); + RegisterModule(new Config(GameId::QOS)); RegisterModule(new scr_parser()); } diff --git a/src/game/t4/mp/main.cpp b/src/game/t4/mp/main.cpp index bbe3aaa6..67b9863b 100644 --- a/src/game/t4/mp/main.cpp +++ b/src/game/t4/mp/main.cpp @@ -27,7 +27,7 @@ T4_MP_Plugin::T4_MP_Plugin() RegisterModule(new Debug()); #endif - RegisterModule(new Config(Config::GAME_T4)); + RegisterModule(new Config(GameId::T4)); RegisterModule(new Events()); // Must be registered before modules that subscribe to engine events. RegisterModule(new Branding()); RegisterModule(new BrushCollision()); diff --git a/src/game/t4/sp/main.cpp b/src/game/t4/sp/main.cpp index a7bb36f0..261978c0 100644 --- a/src/game/t4/sp/main.cpp +++ b/src/game/t4/sp/main.cpp @@ -61,7 +61,7 @@ T4_SP_Plugin::T4_SP_Plugin() // Default ui_autoContinue on so level loads do not wait for input in solo or split-screen. *(volatile uint8_t *)0x82279B07 = 0x01; - RegisterModule(new Config(Config::GAME_T4, true)); + RegisterModule(new Config(GameId::T4, true)); RegisterModule(new Events()); // Must be registered before modules that subscribe to engine events. RegisterModule(new FastFiles()); RegisterModule(new clipmap()); diff --git a/src/game/t5/mp/main.cpp b/src/game/t5/mp/main.cpp index f62dc88f..0f604ec4 100644 --- a/src/game/t5/mp/main.cpp +++ b/src/game/t5/mp/main.cpp @@ -10,7 +10,7 @@ namespace mp T5_MP_Plugin::T5_MP_Plugin() { DbgPrint("T5 MP: Registering modules\n"); - RegisterModule(new Config(Config::GAME_T5)); + RegisterModule(new Config(GameId::T5)); RegisterModule(new scr_parser()); } diff --git a/src/game/t5/sp/main.cpp b/src/game/t5/sp/main.cpp index e292c366..27a53ac2 100644 --- a/src/game/t5/sp/main.cpp +++ b/src/game/t5/sp/main.cpp @@ -10,7 +10,7 @@ namespace sp T5_SP_Plugin::T5_SP_Plugin() { DbgPrint("T5 SP: Registering modules\n"); - RegisterModule(new Config(Config::GAME_T5)); + RegisterModule(new Config(GameId::T5)); RegisterModule(new scr_parser()); } From d03c82bf67230bf1d9ae14d221415ae2560a0d01 Mon Sep 17 00:00:00 2001 From: Michael Oliver Date: Wed, 7 Oct 2026 08:00:40 +0100 Subject: [PATCH 3/5] refactor: consolidate game data under _codxe --- {resources/iw2/_codxe => _codxe/iw2}/codxe.json | 0 .../iw2}/mods/example/maps/_minefields.gsc | 0 .../iw2}/mods/example/maps/custom.gsc | 0 .../mods/example/maps/mp/gametypes/_clientids.gsc | 0 .../mods/example/maps/mp/gametypes/custom_mp.gsc | 0 .../iw3}/internal/maps/ac130.d3dbsp.ents | 0 .../_codxe => _codxe/iw3}/internal/maps/ac130.gsc | 0 .../iw3}/internal/maps/aftermath.d3dbsp.ents | 0 .../iw3}/internal/maps/aftermath.gsc | 0 .../iw3}/internal/maps/airlift.d3dbsp.ents | 0 .../_codxe => _codxe/iw3}/internal/maps/airlift.gsc | 0 .../iw3}/internal/maps/airplane.d3dbsp.ents | 0 .../iw3}/internal/maps/airplane.gsc | 0 .../iw3}/internal/maps/ambush.d3dbsp.ents | 0 .../_codxe => _codxe/iw3}/internal/maps/ambush.gsc | 0 .../iw3}/internal/maps/armada.d3dbsp.ents | 0 .../_codxe => _codxe/iw3}/internal/maps/armada.gsc | 0 .../iw3}/internal/maps/blackout.d3dbsp.ents | 0 .../iw3}/internal/maps/blackout.gsc | 0 .../iw3}/internal/maps/bog_a.d3dbsp.ents | 0 .../_codxe => _codxe/iw3}/internal/maps/bog_a.gsc | 0 .../iw3}/internal/maps/bog_b.d3dbsp.ents | 0 .../_codxe => _codxe/iw3}/internal/maps/bog_b.gsc | 0 .../iw3}/internal/maps/cargoship.d3dbsp.ents | 0 .../iw3}/internal/maps/cargoship.gsc | 0 .../iw3}/internal/maps/coup.d3dbsp.ents | 0 .../_codxe => _codxe/iw3}/internal/maps/coup.gsc | 0 .../iw3}/internal/maps/hunted.d3dbsp.ents | 0 .../_codxe => _codxe/iw3}/internal/maps/hunted.gsc | 0 .../iw3}/internal/maps/icbm.d3dbsp.ents | 0 .../_codxe => _codxe/iw3}/internal/maps/icbm.gsc | 0 .../iw3}/internal/maps/jeepride.d3dbsp.ents | 0 .../iw3}/internal/maps/jeepride.gsc | 0 .../iw3}/internal/maps/killhouse.d3dbsp.ents | 0 .../iw3}/internal/maps/killhouse.gsc | 0 .../iw3}/internal/maps/launchfacility_a.d3dbsp.ents | 0 .../iw3}/internal/maps/launchfacility_a.gsc | 0 .../iw3}/internal/maps/launchfacility_b.d3dbsp.ents | 0 .../iw3}/internal/maps/launchfacility_b.gsc | 0 .../iw3}/internal/maps/scoutsniper.d3dbsp.ents | 0 .../iw3}/internal/maps/scoutsniper.gsc | 0 .../iw3}/internal/maps/sniperescape.d3dbsp.ents | 0 .../iw3}/internal/maps/sniperescape.gsc | 0 .../iw3}/internal/maps/village_assault.d3dbsp.ents | 0 .../iw3}/internal/maps/village_assault.gsc | 0 .../iw3}/internal/maps/village_defend.d3dbsp.ents | 0 .../iw3}/internal/maps/village_defend.gsc | 0 .../_codxe => _codxe/iw3}/mods/codjumper/README.md | 0 .../iw3}/mods/codjumper/buttons_default.cfg | 0 .../iw3}/mods/codjumper/fixed_fps.cfg | 0 .../~weapon_rpg7_spec-rgb&weapon_~06163c5b.dds | Bin .../codjumper/maps/mp/gametypes/_globallogic.gsc | 0 .../iw3}/mods/codjumper/maps/mp/gametypes/_rank.gsc | 0 .../iw3}/mods/codjumper/maps/mp/gametypes/cj.gsc | 0 .../iw3}/mods/codjumper/maps/mp/gametypes/dm.gsc | 0 .../iw3}/mods/codjumper/maps/mp/gametypes/war.gsc | 0 .../_codxe => _codxe/iw3}/mods/codjumper/pc_fps.cfg | 0 .../iw3}/mods/example/maps/_cagedchickens.gsc | 0 .../iw3}/mods/example/maps/example.gsc | 0 {resources/iw4/_codxe => _codxe/iw4}/codxe.json | 0 .../iw4}/mods/codjumper/maps/_names.gsc | 0 .../iw4}/mods/codjumper/maps/cj.gsc | 0 .../iw4}/mods/codjumper/maps/codxe_utility.gsc | 0 .../codjumper/maps/mp/gametypes/_codxe_utility.gsc | 0 .../mods/codjumper/maps/mp/gametypes/cj_hud.gsc | 0 .../iw4}/mods/codjumper/maps/mp/gametypes/cj_mp.gsc | 0 .../iw4}/mods/codjumper/maps/mp/gametypes/menu.gsc | 0 .../iw4}/mods/codjumper/maps/mp/gametypes/war.gsc | 0 .../iw4}/mods/example/maps/_cagedchickens.gsc | 0 .../iw4}/mods/example/maps/codxe_utility.gsc | 0 .../iw4}/mods/example/maps/example_sp.gsc | 0 {resources/iw5/_codxe => _codxe/iw5}/codxe.json | 0 .../iw5}/mods/codjumper/maps/mp/gametypes/cj.gsc | 0 .../iw5}/mods/codjumper/maps/mp/gametypes/cj.gscbin | Bin .../iw5}/mods/codjumper/maps/mp/gametypes/war.gsc | 0 .../mods/codjumper/maps/mp/gametypes/war.gscbin | Bin {resources/qos/_codxe => _codxe/qos}/codxe.json | 0 .../mods/example/maps/mp/gametypes/_clientids.gsc | 0 .../mods/example/maps/mp/gametypes/example_mp.gsc | 0 {resources/t4/_codxe => _codxe/t4}/codxe.json | 0 .../t4}/mods/codjumper/maps/mp/gametypes/cj.gsc | 0 .../t4}/mods/codjumper/maps/mp/gametypes/menu.gsc | 0 .../t4}/mods/codjumper/maps/mp/gametypes/tdm.gsc | 0 .../t4}/mods/example/maps/_music.gsc | 0 .../t4}/mods/example/maps/custom.gsc | 0 .../mods/example/maps/mp/gametypes/_clientids.gsc | 0 .../t4}/mods/example/maps/mp/gametypes/custom.gsc | 0 {resources/t5/_codxe => _codxe/t5}/codxe.json | 0 .../mods/example/maps/mp/gametypes/_clientids.gsc | 0 .../mods/example/maps/mp/gametypes/example_mp.gsc | 0 build.py | 8 ++++++++ docs/features.md | 2 ++ docs/t4.md | 2 +- 93 files changed, 11 insertions(+), 1 deletion(-) rename {resources/iw2/_codxe => _codxe/iw2}/codxe.json (100%) rename {resources/iw2/_codxe => _codxe/iw2}/mods/example/maps/_minefields.gsc (100%) rename {resources/iw2/_codxe => _codxe/iw2}/mods/example/maps/custom.gsc (100%) rename {resources/iw2/_codxe => _codxe/iw2}/mods/example/maps/mp/gametypes/_clientids.gsc (100%) rename {resources/iw2/_codxe => _codxe/iw2}/mods/example/maps/mp/gametypes/custom_mp.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/ac130.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/ac130.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/aftermath.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/aftermath.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/airlift.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/airlift.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/airplane.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/airplane.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/ambush.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/ambush.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/armada.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/armada.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/blackout.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/blackout.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/bog_a.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/bog_a.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/bog_b.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/bog_b.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/cargoship.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/cargoship.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/coup.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/coup.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/hunted.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/hunted.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/icbm.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/icbm.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/jeepride.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/jeepride.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/killhouse.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/killhouse.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/launchfacility_a.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/launchfacility_a.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/launchfacility_b.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/launchfacility_b.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/scoutsniper.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/scoutsniper.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/sniperescape.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/sniperescape.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/village_assault.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/village_assault.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/village_defend.d3dbsp.ents (100%) rename {resources/iw3/_codxe => _codxe/iw3}/internal/maps/village_defend.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/codjumper/README.md (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/codjumper/buttons_default.cfg (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/codjumper/fixed_fps.cfg (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/codjumper/images/~weapon_rpg7_spec-rgb&weapon_~06163c5b.dds (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/codjumper/maps/mp/gametypes/_globallogic.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/codjumper/maps/mp/gametypes/_rank.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/codjumper/maps/mp/gametypes/cj.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/codjumper/maps/mp/gametypes/dm.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/codjumper/maps/mp/gametypes/war.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/codjumper/pc_fps.cfg (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/example/maps/_cagedchickens.gsc (100%) rename {resources/iw3/_codxe => _codxe/iw3}/mods/example/maps/example.gsc (100%) rename {resources/iw4/_codxe => _codxe/iw4}/codxe.json (100%) rename {resources/iw4/_codxe => _codxe/iw4}/mods/codjumper/maps/_names.gsc (100%) rename {resources/iw4/_codxe => _codxe/iw4}/mods/codjumper/maps/cj.gsc (100%) rename {resources/iw4/_codxe => _codxe/iw4}/mods/codjumper/maps/codxe_utility.gsc (100%) rename {resources/iw4/_codxe => _codxe/iw4}/mods/codjumper/maps/mp/gametypes/_codxe_utility.gsc (100%) rename {resources/iw4/_codxe => _codxe/iw4}/mods/codjumper/maps/mp/gametypes/cj_hud.gsc (100%) rename {resources/iw4/_codxe => _codxe/iw4}/mods/codjumper/maps/mp/gametypes/cj_mp.gsc (100%) rename {resources/iw4/_codxe => _codxe/iw4}/mods/codjumper/maps/mp/gametypes/menu.gsc (100%) rename {resources/iw4/_codxe => _codxe/iw4}/mods/codjumper/maps/mp/gametypes/war.gsc (100%) rename {resources/iw4/_codxe => _codxe/iw4}/mods/example/maps/_cagedchickens.gsc (100%) rename {resources/iw4/_codxe => _codxe/iw4}/mods/example/maps/codxe_utility.gsc (100%) rename {resources/iw4/_codxe => _codxe/iw4}/mods/example/maps/example_sp.gsc (100%) rename {resources/iw5/_codxe => _codxe/iw5}/codxe.json (100%) rename {resources/iw5/_codxe => _codxe/iw5}/mods/codjumper/maps/mp/gametypes/cj.gsc (100%) rename {resources/iw5/_codxe => _codxe/iw5}/mods/codjumper/maps/mp/gametypes/cj.gscbin (100%) rename {resources/iw5/_codxe => _codxe/iw5}/mods/codjumper/maps/mp/gametypes/war.gsc (100%) rename {resources/iw5/_codxe => _codxe/iw5}/mods/codjumper/maps/mp/gametypes/war.gscbin (100%) rename {resources/qos/_codxe => _codxe/qos}/codxe.json (100%) rename {resources/qos/_codxe => _codxe/qos}/mods/example/maps/mp/gametypes/_clientids.gsc (100%) rename {resources/qos/_codxe => _codxe/qos}/mods/example/maps/mp/gametypes/example_mp.gsc (100%) rename {resources/t4/_codxe => _codxe/t4}/codxe.json (100%) rename {resources/t4/_codxe => _codxe/t4}/mods/codjumper/maps/mp/gametypes/cj.gsc (100%) rename {resources/t4/_codxe => _codxe/t4}/mods/codjumper/maps/mp/gametypes/menu.gsc (100%) rename {resources/t4/_codxe => _codxe/t4}/mods/codjumper/maps/mp/gametypes/tdm.gsc (100%) rename {resources/t4/_codxe => _codxe/t4}/mods/example/maps/_music.gsc (100%) rename {resources/t4/_codxe => _codxe/t4}/mods/example/maps/custom.gsc (100%) rename {resources/t4/_codxe => _codxe/t4}/mods/example/maps/mp/gametypes/_clientids.gsc (100%) rename {resources/t4/_codxe => _codxe/t4}/mods/example/maps/mp/gametypes/custom.gsc (100%) rename {resources/t5/_codxe => _codxe/t5}/codxe.json (100%) rename {resources/t5/_codxe => _codxe/t5}/mods/example/maps/mp/gametypes/_clientids.gsc (100%) rename {resources/t5/_codxe => _codxe/t5}/mods/example/maps/mp/gametypes/example_mp.gsc (100%) diff --git a/resources/iw2/_codxe/codxe.json b/_codxe/iw2/codxe.json similarity index 100% rename from resources/iw2/_codxe/codxe.json rename to _codxe/iw2/codxe.json diff --git a/resources/iw2/_codxe/mods/example/maps/_minefields.gsc b/_codxe/iw2/mods/example/maps/_minefields.gsc similarity index 100% rename from resources/iw2/_codxe/mods/example/maps/_minefields.gsc rename to _codxe/iw2/mods/example/maps/_minefields.gsc diff --git a/resources/iw2/_codxe/mods/example/maps/custom.gsc b/_codxe/iw2/mods/example/maps/custom.gsc similarity index 100% rename from resources/iw2/_codxe/mods/example/maps/custom.gsc rename to _codxe/iw2/mods/example/maps/custom.gsc diff --git a/resources/iw2/_codxe/mods/example/maps/mp/gametypes/_clientids.gsc b/_codxe/iw2/mods/example/maps/mp/gametypes/_clientids.gsc similarity index 100% rename from resources/iw2/_codxe/mods/example/maps/mp/gametypes/_clientids.gsc rename to _codxe/iw2/mods/example/maps/mp/gametypes/_clientids.gsc diff --git a/resources/iw2/_codxe/mods/example/maps/mp/gametypes/custom_mp.gsc b/_codxe/iw2/mods/example/maps/mp/gametypes/custom_mp.gsc similarity index 100% rename from resources/iw2/_codxe/mods/example/maps/mp/gametypes/custom_mp.gsc rename to _codxe/iw2/mods/example/maps/mp/gametypes/custom_mp.gsc diff --git a/resources/iw3/_codxe/internal/maps/ac130.d3dbsp.ents b/_codxe/iw3/internal/maps/ac130.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/ac130.d3dbsp.ents rename to _codxe/iw3/internal/maps/ac130.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/ac130.gsc b/_codxe/iw3/internal/maps/ac130.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/ac130.gsc rename to _codxe/iw3/internal/maps/ac130.gsc diff --git a/resources/iw3/_codxe/internal/maps/aftermath.d3dbsp.ents b/_codxe/iw3/internal/maps/aftermath.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/aftermath.d3dbsp.ents rename to _codxe/iw3/internal/maps/aftermath.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/aftermath.gsc b/_codxe/iw3/internal/maps/aftermath.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/aftermath.gsc rename to _codxe/iw3/internal/maps/aftermath.gsc diff --git a/resources/iw3/_codxe/internal/maps/airlift.d3dbsp.ents b/_codxe/iw3/internal/maps/airlift.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/airlift.d3dbsp.ents rename to _codxe/iw3/internal/maps/airlift.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/airlift.gsc b/_codxe/iw3/internal/maps/airlift.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/airlift.gsc rename to _codxe/iw3/internal/maps/airlift.gsc diff --git a/resources/iw3/_codxe/internal/maps/airplane.d3dbsp.ents b/_codxe/iw3/internal/maps/airplane.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/airplane.d3dbsp.ents rename to _codxe/iw3/internal/maps/airplane.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/airplane.gsc b/_codxe/iw3/internal/maps/airplane.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/airplane.gsc rename to _codxe/iw3/internal/maps/airplane.gsc diff --git a/resources/iw3/_codxe/internal/maps/ambush.d3dbsp.ents b/_codxe/iw3/internal/maps/ambush.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/ambush.d3dbsp.ents rename to _codxe/iw3/internal/maps/ambush.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/ambush.gsc b/_codxe/iw3/internal/maps/ambush.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/ambush.gsc rename to _codxe/iw3/internal/maps/ambush.gsc diff --git a/resources/iw3/_codxe/internal/maps/armada.d3dbsp.ents b/_codxe/iw3/internal/maps/armada.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/armada.d3dbsp.ents rename to _codxe/iw3/internal/maps/armada.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/armada.gsc b/_codxe/iw3/internal/maps/armada.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/armada.gsc rename to _codxe/iw3/internal/maps/armada.gsc diff --git a/resources/iw3/_codxe/internal/maps/blackout.d3dbsp.ents b/_codxe/iw3/internal/maps/blackout.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/blackout.d3dbsp.ents rename to _codxe/iw3/internal/maps/blackout.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/blackout.gsc b/_codxe/iw3/internal/maps/blackout.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/blackout.gsc rename to _codxe/iw3/internal/maps/blackout.gsc diff --git a/resources/iw3/_codxe/internal/maps/bog_a.d3dbsp.ents b/_codxe/iw3/internal/maps/bog_a.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/bog_a.d3dbsp.ents rename to _codxe/iw3/internal/maps/bog_a.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/bog_a.gsc b/_codxe/iw3/internal/maps/bog_a.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/bog_a.gsc rename to _codxe/iw3/internal/maps/bog_a.gsc diff --git a/resources/iw3/_codxe/internal/maps/bog_b.d3dbsp.ents b/_codxe/iw3/internal/maps/bog_b.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/bog_b.d3dbsp.ents rename to _codxe/iw3/internal/maps/bog_b.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/bog_b.gsc b/_codxe/iw3/internal/maps/bog_b.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/bog_b.gsc rename to _codxe/iw3/internal/maps/bog_b.gsc diff --git a/resources/iw3/_codxe/internal/maps/cargoship.d3dbsp.ents b/_codxe/iw3/internal/maps/cargoship.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/cargoship.d3dbsp.ents rename to _codxe/iw3/internal/maps/cargoship.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/cargoship.gsc b/_codxe/iw3/internal/maps/cargoship.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/cargoship.gsc rename to _codxe/iw3/internal/maps/cargoship.gsc diff --git a/resources/iw3/_codxe/internal/maps/coup.d3dbsp.ents b/_codxe/iw3/internal/maps/coup.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/coup.d3dbsp.ents rename to _codxe/iw3/internal/maps/coup.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/coup.gsc b/_codxe/iw3/internal/maps/coup.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/coup.gsc rename to _codxe/iw3/internal/maps/coup.gsc diff --git a/resources/iw3/_codxe/internal/maps/hunted.d3dbsp.ents b/_codxe/iw3/internal/maps/hunted.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/hunted.d3dbsp.ents rename to _codxe/iw3/internal/maps/hunted.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/hunted.gsc b/_codxe/iw3/internal/maps/hunted.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/hunted.gsc rename to _codxe/iw3/internal/maps/hunted.gsc diff --git a/resources/iw3/_codxe/internal/maps/icbm.d3dbsp.ents b/_codxe/iw3/internal/maps/icbm.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/icbm.d3dbsp.ents rename to _codxe/iw3/internal/maps/icbm.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/icbm.gsc b/_codxe/iw3/internal/maps/icbm.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/icbm.gsc rename to _codxe/iw3/internal/maps/icbm.gsc diff --git a/resources/iw3/_codxe/internal/maps/jeepride.d3dbsp.ents b/_codxe/iw3/internal/maps/jeepride.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/jeepride.d3dbsp.ents rename to _codxe/iw3/internal/maps/jeepride.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/jeepride.gsc b/_codxe/iw3/internal/maps/jeepride.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/jeepride.gsc rename to _codxe/iw3/internal/maps/jeepride.gsc diff --git a/resources/iw3/_codxe/internal/maps/killhouse.d3dbsp.ents b/_codxe/iw3/internal/maps/killhouse.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/killhouse.d3dbsp.ents rename to _codxe/iw3/internal/maps/killhouse.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/killhouse.gsc b/_codxe/iw3/internal/maps/killhouse.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/killhouse.gsc rename to _codxe/iw3/internal/maps/killhouse.gsc diff --git a/resources/iw3/_codxe/internal/maps/launchfacility_a.d3dbsp.ents b/_codxe/iw3/internal/maps/launchfacility_a.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/launchfacility_a.d3dbsp.ents rename to _codxe/iw3/internal/maps/launchfacility_a.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/launchfacility_a.gsc b/_codxe/iw3/internal/maps/launchfacility_a.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/launchfacility_a.gsc rename to _codxe/iw3/internal/maps/launchfacility_a.gsc diff --git a/resources/iw3/_codxe/internal/maps/launchfacility_b.d3dbsp.ents b/_codxe/iw3/internal/maps/launchfacility_b.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/launchfacility_b.d3dbsp.ents rename to _codxe/iw3/internal/maps/launchfacility_b.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/launchfacility_b.gsc b/_codxe/iw3/internal/maps/launchfacility_b.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/launchfacility_b.gsc rename to _codxe/iw3/internal/maps/launchfacility_b.gsc diff --git a/resources/iw3/_codxe/internal/maps/scoutsniper.d3dbsp.ents b/_codxe/iw3/internal/maps/scoutsniper.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/scoutsniper.d3dbsp.ents rename to _codxe/iw3/internal/maps/scoutsniper.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/scoutsniper.gsc b/_codxe/iw3/internal/maps/scoutsniper.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/scoutsniper.gsc rename to _codxe/iw3/internal/maps/scoutsniper.gsc diff --git a/resources/iw3/_codxe/internal/maps/sniperescape.d3dbsp.ents b/_codxe/iw3/internal/maps/sniperescape.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/sniperescape.d3dbsp.ents rename to _codxe/iw3/internal/maps/sniperescape.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/sniperescape.gsc b/_codxe/iw3/internal/maps/sniperescape.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/sniperescape.gsc rename to _codxe/iw3/internal/maps/sniperescape.gsc diff --git a/resources/iw3/_codxe/internal/maps/village_assault.d3dbsp.ents b/_codxe/iw3/internal/maps/village_assault.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/village_assault.d3dbsp.ents rename to _codxe/iw3/internal/maps/village_assault.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/village_assault.gsc b/_codxe/iw3/internal/maps/village_assault.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/village_assault.gsc rename to _codxe/iw3/internal/maps/village_assault.gsc diff --git a/resources/iw3/_codxe/internal/maps/village_defend.d3dbsp.ents b/_codxe/iw3/internal/maps/village_defend.d3dbsp.ents similarity index 100% rename from resources/iw3/_codxe/internal/maps/village_defend.d3dbsp.ents rename to _codxe/iw3/internal/maps/village_defend.d3dbsp.ents diff --git a/resources/iw3/_codxe/internal/maps/village_defend.gsc b/_codxe/iw3/internal/maps/village_defend.gsc similarity index 100% rename from resources/iw3/_codxe/internal/maps/village_defend.gsc rename to _codxe/iw3/internal/maps/village_defend.gsc diff --git a/resources/iw3/_codxe/mods/codjumper/README.md b/_codxe/iw3/mods/codjumper/README.md similarity index 100% rename from resources/iw3/_codxe/mods/codjumper/README.md rename to _codxe/iw3/mods/codjumper/README.md diff --git a/resources/iw3/_codxe/mods/codjumper/buttons_default.cfg b/_codxe/iw3/mods/codjumper/buttons_default.cfg similarity index 100% rename from resources/iw3/_codxe/mods/codjumper/buttons_default.cfg rename to _codxe/iw3/mods/codjumper/buttons_default.cfg diff --git a/resources/iw3/_codxe/mods/codjumper/fixed_fps.cfg b/_codxe/iw3/mods/codjumper/fixed_fps.cfg similarity index 100% rename from resources/iw3/_codxe/mods/codjumper/fixed_fps.cfg rename to _codxe/iw3/mods/codjumper/fixed_fps.cfg diff --git a/resources/iw3/_codxe/mods/codjumper/images/~weapon_rpg7_spec-rgb&weapon_~06163c5b.dds b/_codxe/iw3/mods/codjumper/images/~weapon_rpg7_spec-rgb&weapon_~06163c5b.dds similarity index 100% rename from resources/iw3/_codxe/mods/codjumper/images/~weapon_rpg7_spec-rgb&weapon_~06163c5b.dds rename to _codxe/iw3/mods/codjumper/images/~weapon_rpg7_spec-rgb&weapon_~06163c5b.dds diff --git a/resources/iw3/_codxe/mods/codjumper/maps/mp/gametypes/_globallogic.gsc b/_codxe/iw3/mods/codjumper/maps/mp/gametypes/_globallogic.gsc similarity index 100% rename from resources/iw3/_codxe/mods/codjumper/maps/mp/gametypes/_globallogic.gsc rename to _codxe/iw3/mods/codjumper/maps/mp/gametypes/_globallogic.gsc diff --git a/resources/iw3/_codxe/mods/codjumper/maps/mp/gametypes/_rank.gsc b/_codxe/iw3/mods/codjumper/maps/mp/gametypes/_rank.gsc similarity index 100% rename from resources/iw3/_codxe/mods/codjumper/maps/mp/gametypes/_rank.gsc rename to _codxe/iw3/mods/codjumper/maps/mp/gametypes/_rank.gsc diff --git a/resources/iw3/_codxe/mods/codjumper/maps/mp/gametypes/cj.gsc b/_codxe/iw3/mods/codjumper/maps/mp/gametypes/cj.gsc similarity index 100% rename from resources/iw3/_codxe/mods/codjumper/maps/mp/gametypes/cj.gsc rename to _codxe/iw3/mods/codjumper/maps/mp/gametypes/cj.gsc diff --git a/resources/iw3/_codxe/mods/codjumper/maps/mp/gametypes/dm.gsc b/_codxe/iw3/mods/codjumper/maps/mp/gametypes/dm.gsc similarity index 100% rename from resources/iw3/_codxe/mods/codjumper/maps/mp/gametypes/dm.gsc rename to _codxe/iw3/mods/codjumper/maps/mp/gametypes/dm.gsc diff --git a/resources/iw3/_codxe/mods/codjumper/maps/mp/gametypes/war.gsc b/_codxe/iw3/mods/codjumper/maps/mp/gametypes/war.gsc similarity index 100% rename from resources/iw3/_codxe/mods/codjumper/maps/mp/gametypes/war.gsc rename to _codxe/iw3/mods/codjumper/maps/mp/gametypes/war.gsc diff --git a/resources/iw3/_codxe/mods/codjumper/pc_fps.cfg b/_codxe/iw3/mods/codjumper/pc_fps.cfg similarity index 100% rename from resources/iw3/_codxe/mods/codjumper/pc_fps.cfg rename to _codxe/iw3/mods/codjumper/pc_fps.cfg diff --git a/resources/iw3/_codxe/mods/example/maps/_cagedchickens.gsc b/_codxe/iw3/mods/example/maps/_cagedchickens.gsc similarity index 100% rename from resources/iw3/_codxe/mods/example/maps/_cagedchickens.gsc rename to _codxe/iw3/mods/example/maps/_cagedchickens.gsc diff --git a/resources/iw3/_codxe/mods/example/maps/example.gsc b/_codxe/iw3/mods/example/maps/example.gsc similarity index 100% rename from resources/iw3/_codxe/mods/example/maps/example.gsc rename to _codxe/iw3/mods/example/maps/example.gsc diff --git a/resources/iw4/_codxe/codxe.json b/_codxe/iw4/codxe.json similarity index 100% rename from resources/iw4/_codxe/codxe.json rename to _codxe/iw4/codxe.json diff --git a/resources/iw4/_codxe/mods/codjumper/maps/_names.gsc b/_codxe/iw4/mods/codjumper/maps/_names.gsc similarity index 100% rename from resources/iw4/_codxe/mods/codjumper/maps/_names.gsc rename to _codxe/iw4/mods/codjumper/maps/_names.gsc diff --git a/resources/iw4/_codxe/mods/codjumper/maps/cj.gsc b/_codxe/iw4/mods/codjumper/maps/cj.gsc similarity index 100% rename from resources/iw4/_codxe/mods/codjumper/maps/cj.gsc rename to _codxe/iw4/mods/codjumper/maps/cj.gsc diff --git a/resources/iw4/_codxe/mods/codjumper/maps/codxe_utility.gsc b/_codxe/iw4/mods/codjumper/maps/codxe_utility.gsc similarity index 100% rename from resources/iw4/_codxe/mods/codjumper/maps/codxe_utility.gsc rename to _codxe/iw4/mods/codjumper/maps/codxe_utility.gsc diff --git a/resources/iw4/_codxe/mods/codjumper/maps/mp/gametypes/_codxe_utility.gsc b/_codxe/iw4/mods/codjumper/maps/mp/gametypes/_codxe_utility.gsc similarity index 100% rename from resources/iw4/_codxe/mods/codjumper/maps/mp/gametypes/_codxe_utility.gsc rename to _codxe/iw4/mods/codjumper/maps/mp/gametypes/_codxe_utility.gsc diff --git a/resources/iw4/_codxe/mods/codjumper/maps/mp/gametypes/cj_hud.gsc b/_codxe/iw4/mods/codjumper/maps/mp/gametypes/cj_hud.gsc similarity index 100% rename from resources/iw4/_codxe/mods/codjumper/maps/mp/gametypes/cj_hud.gsc rename to _codxe/iw4/mods/codjumper/maps/mp/gametypes/cj_hud.gsc diff --git a/resources/iw4/_codxe/mods/codjumper/maps/mp/gametypes/cj_mp.gsc b/_codxe/iw4/mods/codjumper/maps/mp/gametypes/cj_mp.gsc similarity index 100% rename from resources/iw4/_codxe/mods/codjumper/maps/mp/gametypes/cj_mp.gsc rename to _codxe/iw4/mods/codjumper/maps/mp/gametypes/cj_mp.gsc diff --git a/resources/iw4/_codxe/mods/codjumper/maps/mp/gametypes/menu.gsc b/_codxe/iw4/mods/codjumper/maps/mp/gametypes/menu.gsc similarity index 100% rename from resources/iw4/_codxe/mods/codjumper/maps/mp/gametypes/menu.gsc rename to _codxe/iw4/mods/codjumper/maps/mp/gametypes/menu.gsc diff --git a/resources/iw4/_codxe/mods/codjumper/maps/mp/gametypes/war.gsc b/_codxe/iw4/mods/codjumper/maps/mp/gametypes/war.gsc similarity index 100% rename from resources/iw4/_codxe/mods/codjumper/maps/mp/gametypes/war.gsc rename to _codxe/iw4/mods/codjumper/maps/mp/gametypes/war.gsc diff --git a/resources/iw4/_codxe/mods/example/maps/_cagedchickens.gsc b/_codxe/iw4/mods/example/maps/_cagedchickens.gsc similarity index 100% rename from resources/iw4/_codxe/mods/example/maps/_cagedchickens.gsc rename to _codxe/iw4/mods/example/maps/_cagedchickens.gsc diff --git a/resources/iw4/_codxe/mods/example/maps/codxe_utility.gsc b/_codxe/iw4/mods/example/maps/codxe_utility.gsc similarity index 100% rename from resources/iw4/_codxe/mods/example/maps/codxe_utility.gsc rename to _codxe/iw4/mods/example/maps/codxe_utility.gsc diff --git a/resources/iw4/_codxe/mods/example/maps/example_sp.gsc b/_codxe/iw4/mods/example/maps/example_sp.gsc similarity index 100% rename from resources/iw4/_codxe/mods/example/maps/example_sp.gsc rename to _codxe/iw4/mods/example/maps/example_sp.gsc diff --git a/resources/iw5/_codxe/codxe.json b/_codxe/iw5/codxe.json similarity index 100% rename from resources/iw5/_codxe/codxe.json rename to _codxe/iw5/codxe.json diff --git a/resources/iw5/_codxe/mods/codjumper/maps/mp/gametypes/cj.gsc b/_codxe/iw5/mods/codjumper/maps/mp/gametypes/cj.gsc similarity index 100% rename from resources/iw5/_codxe/mods/codjumper/maps/mp/gametypes/cj.gsc rename to _codxe/iw5/mods/codjumper/maps/mp/gametypes/cj.gsc diff --git a/resources/iw5/_codxe/mods/codjumper/maps/mp/gametypes/cj.gscbin b/_codxe/iw5/mods/codjumper/maps/mp/gametypes/cj.gscbin similarity index 100% rename from resources/iw5/_codxe/mods/codjumper/maps/mp/gametypes/cj.gscbin rename to _codxe/iw5/mods/codjumper/maps/mp/gametypes/cj.gscbin diff --git a/resources/iw5/_codxe/mods/codjumper/maps/mp/gametypes/war.gsc b/_codxe/iw5/mods/codjumper/maps/mp/gametypes/war.gsc similarity index 100% rename from resources/iw5/_codxe/mods/codjumper/maps/mp/gametypes/war.gsc rename to _codxe/iw5/mods/codjumper/maps/mp/gametypes/war.gsc diff --git a/resources/iw5/_codxe/mods/codjumper/maps/mp/gametypes/war.gscbin b/_codxe/iw5/mods/codjumper/maps/mp/gametypes/war.gscbin similarity index 100% rename from resources/iw5/_codxe/mods/codjumper/maps/mp/gametypes/war.gscbin rename to _codxe/iw5/mods/codjumper/maps/mp/gametypes/war.gscbin diff --git a/resources/qos/_codxe/codxe.json b/_codxe/qos/codxe.json similarity index 100% rename from resources/qos/_codxe/codxe.json rename to _codxe/qos/codxe.json diff --git a/resources/qos/_codxe/mods/example/maps/mp/gametypes/_clientids.gsc b/_codxe/qos/mods/example/maps/mp/gametypes/_clientids.gsc similarity index 100% rename from resources/qos/_codxe/mods/example/maps/mp/gametypes/_clientids.gsc rename to _codxe/qos/mods/example/maps/mp/gametypes/_clientids.gsc diff --git a/resources/qos/_codxe/mods/example/maps/mp/gametypes/example_mp.gsc b/_codxe/qos/mods/example/maps/mp/gametypes/example_mp.gsc similarity index 100% rename from resources/qos/_codxe/mods/example/maps/mp/gametypes/example_mp.gsc rename to _codxe/qos/mods/example/maps/mp/gametypes/example_mp.gsc diff --git a/resources/t4/_codxe/codxe.json b/_codxe/t4/codxe.json similarity index 100% rename from resources/t4/_codxe/codxe.json rename to _codxe/t4/codxe.json diff --git a/resources/t4/_codxe/mods/codjumper/maps/mp/gametypes/cj.gsc b/_codxe/t4/mods/codjumper/maps/mp/gametypes/cj.gsc similarity index 100% rename from resources/t4/_codxe/mods/codjumper/maps/mp/gametypes/cj.gsc rename to _codxe/t4/mods/codjumper/maps/mp/gametypes/cj.gsc diff --git a/resources/t4/_codxe/mods/codjumper/maps/mp/gametypes/menu.gsc b/_codxe/t4/mods/codjumper/maps/mp/gametypes/menu.gsc similarity index 100% rename from resources/t4/_codxe/mods/codjumper/maps/mp/gametypes/menu.gsc rename to _codxe/t4/mods/codjumper/maps/mp/gametypes/menu.gsc diff --git a/resources/t4/_codxe/mods/codjumper/maps/mp/gametypes/tdm.gsc b/_codxe/t4/mods/codjumper/maps/mp/gametypes/tdm.gsc similarity index 100% rename from resources/t4/_codxe/mods/codjumper/maps/mp/gametypes/tdm.gsc rename to _codxe/t4/mods/codjumper/maps/mp/gametypes/tdm.gsc diff --git a/resources/t4/_codxe/mods/example/maps/_music.gsc b/_codxe/t4/mods/example/maps/_music.gsc similarity index 100% rename from resources/t4/_codxe/mods/example/maps/_music.gsc rename to _codxe/t4/mods/example/maps/_music.gsc diff --git a/resources/t4/_codxe/mods/example/maps/custom.gsc b/_codxe/t4/mods/example/maps/custom.gsc similarity index 100% rename from resources/t4/_codxe/mods/example/maps/custom.gsc rename to _codxe/t4/mods/example/maps/custom.gsc diff --git a/resources/t4/_codxe/mods/example/maps/mp/gametypes/_clientids.gsc b/_codxe/t4/mods/example/maps/mp/gametypes/_clientids.gsc similarity index 100% rename from resources/t4/_codxe/mods/example/maps/mp/gametypes/_clientids.gsc rename to _codxe/t4/mods/example/maps/mp/gametypes/_clientids.gsc diff --git a/resources/t4/_codxe/mods/example/maps/mp/gametypes/custom.gsc b/_codxe/t4/mods/example/maps/mp/gametypes/custom.gsc similarity index 100% rename from resources/t4/_codxe/mods/example/maps/mp/gametypes/custom.gsc rename to _codxe/t4/mods/example/maps/mp/gametypes/custom.gsc diff --git a/resources/t5/_codxe/codxe.json b/_codxe/t5/codxe.json similarity index 100% rename from resources/t5/_codxe/codxe.json rename to _codxe/t5/codxe.json diff --git a/resources/t5/_codxe/mods/example/maps/mp/gametypes/_clientids.gsc b/_codxe/t5/mods/example/maps/mp/gametypes/_clientids.gsc similarity index 100% rename from resources/t5/_codxe/mods/example/maps/mp/gametypes/_clientids.gsc rename to _codxe/t5/mods/example/maps/mp/gametypes/_clientids.gsc diff --git a/resources/t5/_codxe/mods/example/maps/mp/gametypes/example_mp.gsc b/_codxe/t5/mods/example/maps/mp/gametypes/example_mp.gsc similarity index 100% rename from resources/t5/_codxe/mods/example/maps/mp/gametypes/example_mp.gsc rename to _codxe/t5/mods/example/maps/mp/gametypes/example_mp.gsc diff --git a/build.py b/build.py index 4fefd013..722c560f 100644 --- a/build.py +++ b/build.py @@ -9,6 +9,7 @@ BINARY_PATH = r"build\Release\bin\codxe.xex" STAGING_DIR = r"build\staging" RESOURCES_PATH = r"resources" +DATA_PATH = r"_codxe" VERSION_HEADER_PATH = r"build\Release\obj\git_version.h" @@ -56,6 +57,13 @@ def read_git_version(): print(f"Resources directory not found at {RESOURCES_PATH}") exit(1) +if os.path.isdir(DATA_PATH): + shutil.copytree(DATA_PATH, os.path.join(STAGING_DIR, "_codxe"), dirs_exist_ok=True) + print("CoD Xe data directory copied successfully") +else: + print(f"CoD Xe data directory not found at {DATA_PATH}") + exit(1) + print("Copying binary to all title ID directories...") plugins_dir = os.path.join(STAGING_DIR, "xenia", "plugins") if os.path.exists(plugins_dir): diff --git a/docs/features.md b/docs/features.md index b2ecc27e..844dff5c 100644 --- a/docs/features.md +++ b/docs/features.md @@ -8,6 +8,8 @@ The raw GSC loader enables loading `.gsc` scripts directly from the mod director Create a `_codxe\\mods` folder in your game directory, and place a `codxe.json` in `_codxe\` to define which mod is active. Use `iw2`, `iw3`, `iw4`, `iw5`, `t4`, `t5`, or `qos` for the game ID. +The repository and release archive contain one top-level [`_codxe`](/_codxe) folder with a subdirectory for each game. Copy that folder into your game directory, preserving `_codxe\\...`. T4 singleplayer also supports copying the same folder to a USB or HDD root. Title updates and Xenia files remain separate from the game data. + CoD Xe selects one data root at startup. If `_codxe\` exists, all config, mod, fastfile, usermap, internal asset, dump, and player stats paths use that root. Missing files never fall back to the legacy `_codxe` root. The old layout remains supported when the nested directory does not exist. Keep all data for a game together when migrating. When neither local directory exists, new data uses `_codxe\`. T4 singleplayer retains its USB/HDD lookup and requires an existing data root; see the [T4 guide](t4.md). diff --git a/docs/t4.md b/docs/t4.md index c817d595..834bdbb8 100644 --- a/docs/t4.md +++ b/docs/t4.md @@ -220,7 +220,7 @@ self.god = false; ## Example Mods -Example T4 mods are available under [`resources/t4/_codxe/mods`](/resources/t4/_codxe/mods): +Example T4 mods are available under [`_codxe/t4/mods`](/_codxe/t4/mods): - `example` - Basic script examples for singleplayer and multiplayer. - `codjumper` - Codjumper-oriented scripts and menu examples. From 06a93910695450a8c6b28558f805c8447a070e6b Mon Sep 17 00:00:00 2001 From: Michael Oliver Date: Wed, 7 Oct 2026 08:15:10 +0100 Subject: [PATCH 4/5] feat: enable shared storage for all games --- docs/features.md | 4 ++-- src/common/config.cpp | 14 +++++--------- src/common/config.h | 2 +- src/filesystem.cpp | 17 +++++++---------- src/game/iw4/mp/components/fastfiles.cpp | 6 ++++++ src/game/iw4/mp_tu6/components/fastfiles.cpp | 9 +++++++++ src/game/t4/sp/main.cpp | 2 +- 7 files changed, 31 insertions(+), 23 deletions(-) diff --git a/docs/features.md b/docs/features.md index 844dff5c..5f262317 100644 --- a/docs/features.md +++ b/docs/features.md @@ -8,11 +8,11 @@ The raw GSC loader enables loading `.gsc` scripts directly from the mod director Create a `_codxe\\mods` folder in your game directory, and place a `codxe.json` in `_codxe\` to define which mod is active. Use `iw2`, `iw3`, `iw4`, `iw5`, `t4`, `t5`, or `qos` for the game ID. -The repository and release archive contain one top-level [`_codxe`](/_codxe) folder with a subdirectory for each game. Copy that folder into your game directory, preserving `_codxe\\...`. T4 singleplayer also supports copying the same folder to a USB or HDD root. Title updates and Xenia files remain separate from the game data. +The repository and release archive contain one top-level [`_codxe`](/_codxe) folder with a subdirectory for each game. Copy that folder into your game directory or a USB/HDD root, preserving `_codxe\\...`. All supported games that use CoD Xe data share this layout. Title updates and Xenia files remain separate from the game data. CoD Xe selects one data root at startup. If `_codxe\` exists, all config, mod, fastfile, usermap, internal asset, dump, and player stats paths use that root. Missing files never fall back to the legacy `_codxe` root. The old layout remains supported when the nested directory does not exist. Keep all data for a game together when migrating. -When neither local directory exists, new data uses `_codxe\`. T4 singleplayer retains its USB/HDD lookup and requires an existing data root; see the [T4 guide](t4.md). +On Xbox hardware, when neither local directory exists, CoD Xe searches `Usb0:\_codxe\` through `Usb4:\_codxe\`, then `Hdd1:\_codxe\`. The first existing directory becomes the only active root for reads and writes. USB/HDD roots only accept the nested layout. If no existing root is found, new files use the local `_codxe\` directory. Xenia uses the local directories. In `codxe.json`, set `"active_mod"` to the name of your mod folder: diff --git a/src/common/config.cpp b/src/common/config.cpp index 8dd08efc..2fc400d2 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -148,7 +148,7 @@ bool ReadFileToString(const char *path, std::string &outString) return true; } -Config::Config(GameId::Type gameId, bool allowSharedStorage) +Config::Config(GameId::Type gameId) { data_root = "game:\\_codxe"; mounted_links.clear(); @@ -203,9 +203,11 @@ Config::Config(GameId::Type gameId, bool allowSharedStorage) { config_path = filesystem::JoinPath(local_directory.c_str(), "codxe.json"); } - else if (allowSharedStorage) + else { - data_root.clear(); + // If no existing root is found, new files use the local nested layout. + data_root = std::string("game:\\_codxe\\") + gameDirectoryName; + config_path = filesystem::JoinPath(nested_local_directory.c_str(), "codxe.json"); if (xbox::GetEnvironment() != xbox::ENVIRONMENT_XENIA) { // Only the game directory accepts the legacy layout. @@ -235,12 +237,6 @@ Config::Config(GameId::Type gameId, bool allowSharedStorage) } } } - else - { - // A fresh install writes to the new layout. Do not reselect the root per file. - data_root = std::string("game:\\_codxe\\") + gameDirectoryName; - config_path = filesystem::JoinPath(nested_local_directory.c_str(), "codxe.json"); - } } bool loaded = false; diff --git a/src/common/config.h b/src/common/config.h index 2989b99e..9a0fcbd2 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -8,7 +8,7 @@ bool ReadFileToString(const char *path, std::string &outString); class Config : public Module { public: - explicit Config(GameId::Type gameId, bool allowSharedStorage = false); + explicit Config(GameId::Type gameId); ~Config(); static bool dump_rawfile; diff --git a/src/filesystem.cpp b/src/filesystem.cpp index 8d1c6732..890844f4 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -43,12 +43,10 @@ void CreateDirectories(const char *path) NormalizePathSeparators(&directory[0]); char *p = &directory[0]; - const bool hasDrivePrefix = directory.size() >= 3 && p[1] == ':' && p[2] == '\\'; - const bool hasGamePrefix = directory.size() >= 6 && strncmp(p, "game:\\", 6) == 0; - - // Skip leading drive letter (e.g., "C:\") or "game:\" prefix - if (hasDrivePrefix || hasGamePrefix) - p += (hasDrivePrefix ? 3 : 6); // Move past "C:\" or "game:\" + const size_t rootSeparator = directory.find('\\'); + // Skip the device root, including multi-character links such as game: and codxeusb0:. + if (rootSeparator != std::string::npos && rootSeparator > 0 && directory[rootSeparator - 1] == ':') + p += rootSeparator + 1; for (; *p; p++) { @@ -75,10 +73,9 @@ void CreateParentDirectories(const char *path) if (lastSeparator == std::string::npos) return; - const bool isDriveRoot = lastSeparator == 2 && directory.size() > 1 && directory[1] == ':'; - const bool isGameRoot = - lastSeparator == 5 && directory.size() >= 6 && strncmp(directory.c_str(), "game:\\", 6) == 0; - if (isDriveRoot || isGameRoot) + const bool isDeviceRoot = + lastSeparator > 0 && directory[lastSeparator - 1] == ':' && directory.find('\\') == lastSeparator; + if (isDeviceRoot) { return; } diff --git a/src/game/iw4/mp/components/fastfiles.cpp b/src/game/iw4/mp/components/fastfiles.cpp index 455c1f30..fe4c02d9 100644 --- a/src/game/iw4/mp/components/fastfiles.cpp +++ b/src/game/iw4/mp/components/fastfiles.cpp @@ -124,6 +124,12 @@ int Sys_CreateFile_Hook(const char *dir, const char *filename) // Sys_CreateFile prepends the title directory, so pass a title-relative filename. if (path.compare(0, 6, "game:\\") == 0) return original(dir, path.substr(6).c_str()); + + // External roots use a device link directly, with the stock async/unbuffered read flags. + if (!path.empty()) + return reinterpret_cast(CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, + nullptr)); } return original(dir, filename); diff --git a/src/game/iw4/mp_tu6/components/fastfiles.cpp b/src/game/iw4/mp_tu6/components/fastfiles.cpp index 9ecd6e95..29c6b41f 100644 --- a/src/game/iw4/mp_tu6/components/fastfiles.cpp +++ b/src/game/iw4/mp_tu6/components/fastfiles.cpp @@ -131,6 +131,15 @@ SysFile Sys_CreateFile_Hook(const char *dir, const char *filename) // Sys_CreateFile prepends the title directory, so pass a title-relative filename. if (path.compare(0, 6, "game:\\") == 0) return original(dir, path.substr(6).c_str()); + + // External roots use a device link directly, with the stock async/unbuffered read flags. + if (!path.empty()) + { + SysFile file = {CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr), + 0}; + return file; + } } return original(dir, filename); diff --git a/src/game/t4/sp/main.cpp b/src/game/t4/sp/main.cpp index 261978c0..84ed9200 100644 --- a/src/game/t4/sp/main.cpp +++ b/src/game/t4/sp/main.cpp @@ -61,7 +61,7 @@ T4_SP_Plugin::T4_SP_Plugin() // Default ui_autoContinue on so level loads do not wait for input in solo or split-screen. *(volatile uint8_t *)0x82279B07 = 0x01; - RegisterModule(new Config(GameId::T4, true)); + RegisterModule(new Config(GameId::T4)); RegisterModule(new Events()); // Must be registered before modules that subscribe to engine events. RegisterModule(new FastFiles()); RegisterModule(new clipmap()); From 46a86ad00c82bda0c980372c40fd8de6bd84f8f4 Mon Sep 17 00:00:00 2001 From: Michael Oliver Date: Thu, 8 Oct 2026 09:51:45 +0100 Subject: [PATCH 5/5] fix: mount shared storage in system and title namespaces --- src/common/config.cpp | 48 +++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 2fc400d2..a30f30f2 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -20,6 +20,7 @@ namespace { const char *CONFIG_DEVICE_LINK_NAME = "codxe:"; const char *CONFIG_DEVICE_LINK_PATH = "codxe:\\"; +const char *const DEVICE_LINK_NAMESPACES[] = {"\\System??\\", "\\??\\"}; bool StartsWith(const std::string &value, const char *prefix) { @@ -47,30 +48,41 @@ bool TryGetDeviceRoot(const std::string &path, std::string &device_root) return true; } -bool MountDevice(const char *link_name, const char *device_path) +void UnmountDevice(const char *link_name) { - const DWORD process_type = KeGetCurrentProcessType(); - const std::string link_path = std::string(process_type == PROC_TYPE_SYSTEM ? "\\System??\\" : "\\??\\") + link_name; + for (size_t i = 0; i < ARRAYSIZE(DEVICE_LINK_NAMESPACES); ++i) + { + const std::string link_path = std::string(DEVICE_LINK_NAMESPACES[i]) + link_name; + STRING link = {}; + RtlInitAnsiString(&link, link_path.c_str()); + ObDeleteSymbolicLink(&link); + } +} - STRING link = {}; +bool MountDevice(const char *link_name, const char *device_path) +{ STRING device = {}; - RtlInitAnsiString(&link, link_path.c_str()); RtlInitAnsiString(&device, device_path); - const NTSTATUS delete_status = ObDeleteSymbolicLink(&link); - const NTSTATUS create_status = ObCreateSymbolicLink(&link, &device); - UNREFERENCED_PARAMETER(delete_status); - - return NT_SUCCESS(create_status); -} + // Plugin setup runs in the system process; game reads and writes run in the title process. + // DOS device links are namespace-specific, so both must point to the same selected device. + for (size_t i = 0; i < ARRAYSIZE(DEVICE_LINK_NAMESPACES); ++i) + { + const std::string link_path = std::string(DEVICE_LINK_NAMESPACES[i]) + link_name; + STRING link = {}; + RtlInitAnsiString(&link, link_path.c_str()); + ObDeleteSymbolicLink(&link); + const NTSTATUS status = ObCreateSymbolicLink(&link, &device); + if (!NT_SUCCESS(status)) + { + DbgPrint("[codxe][Config] Failed to mount %s -> %s: status=0x%08X\n", link_path.c_str(), device_path, + status); + UnmountDevice(link_name); + return false; + } + } -void UnmountDevice(const char *link_name) -{ - const DWORD process_type = KeGetCurrentProcessType(); - const std::string link_path = std::string(process_type == PROC_TYPE_SYSTEM ? "\\System??\\" : "\\??\\") + link_name; - STRING link = {}; - RtlInitAnsiString(&link, link_path.c_str()); - ObDeleteSymbolicLink(&link); + return true; } bool BuildMountedConfigPath(const std::string &device_config_path, std::string &mounted_config_path)