diff --git a/iw3xe.vcxproj b/iw3xe.vcxproj index 1f74455..7f8fd45 100644 --- a/iw3xe.vcxproj +++ b/iw3xe.vcxproj @@ -75,6 +75,9 @@ + + + @@ -85,5 +88,9 @@ + + + + diff --git a/mods/253-codjumper/maps/mp/gametypes/cj.gsc b/mods/253-codjumper/maps/mp/gametypes/cj.gsc new file mode 100644 index 0000000..bce0cc5 --- /dev/null +++ b/mods/253-codjumper/maps/mp/gametypes/cj.gsc @@ -0,0 +1,219 @@ +// #include common_scripts\utility; +// #include maps\mp\gametypes\_hud_util; + +init() +{ + setDvar("scr_game_forceuav", 0); // Disable compass + + setDvar("player_sprintUnlimited", 1); // Unlimited sprint + setDvar("jump_slowdownEnable", 0); // Disable jump slowdown + + setDvar("bg_fallDamageMaxHeight", 9999); // Disable fall damage + setDvar("bg_fallDamageMinHeight", 9998); // Disable fall damage + + setDvar("scr_war_timelimit", 0); // Disable time limit + + setDvar("scr_hardpoint_allowuav", 0); // Disable UAV hardpoint and waypoint + + setDvar("sv_cheats", 1); // Enable cheats + + level thread onPlayerConnect(); +} + +onPlayerConnect() +{ + for (;;) + { + level waittill("connecting", player); + + player thread onPlayerSpawned(); + } +} + +onPlayerSpawned() +{ + self endon("disconnect"); + for (;;) + { + self waittill("spawned_player"); + + self thread cj_setup_loadout(); + self thread replenish_ammo(); + self thread watch_buttons(); + self thread set_client_dvars(); + + self Cbuf_ExecuteBuffer("bind BUTTON_LSHLDR toggle pm_fixed_fps 333 250 125"); + self Cbuf_ExecuteBuffer("bind BUTTON_RSHLDR toggle pm_fixed_fps 125 250 333"); + self Cbuf_ExecuteBuffer("bind DPAD_DOWN +frag"); + self Cbuf_ExecuteBuffer("bind DPAD_RIGHT +smoke"); + + self Cbuf_ExecuteBuffer("bind DPAD_RIGHT +smoke"); + } +} + +cj_setup_loadout() +{ + self TakeAllWeapons(); + + wait 0.05; + + // self GiveWeapon("deserteagle_mp"); + self Cbuf_ExecuteBuffer("give deserteagle_mp"); + self GiveWeapon("mp5_mp"); + self GiveWeapon("rpg_mp"); + self SetActionSlot(3, "weapon", "rpg_mp"); + + wait 0.05; + self SwitchToWeapon("deserteagle_mp"); +} + +set_client_dvars() +{ + self endon("disconnect"); + + self setClientDvar("fx_enable", 0); // Disable effects + self setClientDvar("ui_hud_hardcore", 1); // Hardcore HUD + self setClientDvar("cg_drawCrosshair", 0); // Disable crosshair + + // FPS + // self setClientDvar("r_vsync", 0); + // self setClientDvar("cg_drawFPS", 1); + // self setClientDvar("com_maxfps", 333); + + // Fixed FPS + self setClientDvar("pm_fixed_fps_enable", 1); + self setClientDvar("pm_fixed_fps", 250); + + self setClientDvar("cg_fov", 70); + + self setClientDvar("cg_draw2D", 0); // Disable 2D HUD +} + +/** + * Constantly replace the players ammo. + */ +replenish_ammo() +{ + self endon("end_respawn"); + self endon("disconnect"); + + for (;;) + { + currentWeapon = self getCurrentWeapon(); // undefined if the player is mantling or on a ladder + if (isdefined(currentWeapon)) + self giveMaxAmmo(currentWeapon); + wait 1; + } +} + +watch_buttons() +{ + self endon("end_respawn"); + self endon("disconnect"); + + for (;;) + { + + if (self button_pressed("frag")) + { + self toggle_ufo(); + wait 0.25; + } + else if (self button_pressed_twice("melee")) + { + self savePos(0); + wait 0.25; + } + else if (self button_pressed("smoke")) + { + self loadPos(0); + wait 0.25; + } + + wait 0.05; + } +} + +/** + * Check if a button is pressed. + */ +button_pressed(button) +{ + switch (ToLower(button)) + { + case "frag": + return self fragbuttonpressed(); + case "melee": + return self meleebuttonpressed(); + case "smoke": + return self secondaryoffhandbuttonpressed(); + default: + self iprintln("^1Unknown button " + button); + return false; + } +} + +/** + * Check if a button is pressed twice within 500ms. + */ +button_pressed_twice(button) +{ + if (self button_pressed(button)) + { + // Wait for the button to be released after the first press + while (self button_pressed(button)) + { + wait 0.05; + } + + // Now, wait for a second press within 500ms + for (elapsed_time = 0; elapsed_time < 0.5; elapsed_time += 0.05) + { + if (self button_pressed(button)) + { + // Ensure it was released before this second press + return true; + } + + wait 0.05; + } + } + return false; +} + + +savePos(i) +{ + if (!self isOnGround()) + return; + + self.cj["settings"]["rpg_switched"] = false; + self.cj["saves"]["org"][i] = self.origin; + self.cj["saves"]["ang"][i] = self getPlayerAngles(); +} + +loadPos(i) +{ + self freezecontrols(true); + wait 0.05; + + self setPlayerAngles(self.cj["saves"]["ang"][i]); + self setOrigin(self.cj["saves"]["org"][i]); + + wait 0.05; + self freezecontrols(false); +} + +toggle_ufo() +{ + if (self.sessionstate == "playing") + { + self allowSpectateTeam("freelook", true); + self.sessionstate = "spectator"; + } + else + { + self allowSpectateTeam("freelook", false); + self.sessionstate = "playing"; + } +} diff --git a/mods/253-codjumper/maps/mp/gametypes/war.gsc b/mods/253-codjumper/maps/mp/gametypes/war.gsc new file mode 100644 index 0000000..8157c42 --- /dev/null +++ b/mods/253-codjumper/maps/mp/gametypes/war.gsc @@ -0,0 +1,164 @@ +#include maps\mp\_utility; +#include maps\mp\gametypes\_hud_util; +/* + War + Objective: Score points for your team by eliminating players on the opposing team + Map ends: When one team reaches the score limit, or time limit is reached + Respawning: No wait / Near teammates + + Level requirements + ------------------ + Spawnpoints: + classname mp_tdm_spawn + All players spawn from these. The spawnpoint chosen is dependent on the current locations of teammates and enemies + at the time of spawn. Players generally spawn behind their teammates relative to the direction of enemies. + + Spectator Spawnpoints: + classname mp_global_intermission + Spectators spawn from these and intermission is viewed from these positions. + Atleast one is required, any more and they are randomly chosen between. + + Level script requirements + ------------------------- + Team Definitions: + game["allies"] = "marines"; + game["axis"] = "opfor"; + This sets the nationalities of the teams. Allies can be american, british, or russian. Axis can be german. + + If using minefields or exploders: + maps\mp\_load::main(); + + Optional level script settings + ------------------------------ + Soldier Type and Variation: + game["american_soldiertype"] = "normandy"; + game["german_soldiertype"] = "normandy"; + This sets what character models are used for each nationality on a particular map. + + Valid settings: + american_soldiertype normandy + british_soldiertype normandy, africa + russian_soldiertype coats, padded + german_soldiertype normandy, africa, winterlight, winterdark +*/ + +/*QUAKED mp_tdm_spawn (0.0 0.0 1.0) (-16 -16 0) (16 16 72) +Players spawn away from enemies and near their team at one of these positions.*/ + +/*QUAKED mp_tdm_spawn_axis_start (0.5 0.0 1.0) (-16 -16 0) (16 16 72) +Axis players spawn away from enemies and near their team at one of these positions at the start of a round.*/ + +/*QUAKED mp_tdm_spawn_allies_start (0.0 0.5 1.0) (-16 -16 0) (16 16 72) +Allied players spawn away from enemies and near their team at one of these positions at the start of a round.*/ + +main() +{ + if(getdvar("mapname") == "mp_background") + return; + + maps\mp\gametypes\_globallogic::init(); + maps\mp\gametypes\_callbacksetup::SetupCallbacks(); + maps\mp\gametypes\_globallogic::SetupCallbacks(); + + maps\mp\gametypes\_globallogic::registerTimeLimitDvar( "war", 30, 0, 1440 ); + maps\mp\gametypes\_globallogic::registerScoreLimitDvar( "war", 100, 0, 500 ); + maps\mp\gametypes\_globallogic::registerRoundLimitDvar( "war", 1, 0, 10 ); + maps\mp\gametypes\_globallogic::registerNumLivesDvar( "war", 0, 0, 10 ); + + level.teamBased = true; + level.onStartGameType = ::onStartGameType; + level.onSpawnPlayer = ::onSpawnPlayer; + + maps\mp\gametypes\cj::init(); +} + +onStartGameType() +{ + //thread maps\mp\gametypes\_hardpoints::init(); + //thread maps\mp\_helicopter::init(); + + setClientNameMode("auto_change"); + + maps\mp\gametypes\_globallogic::setObjectiveText( "allies", &"OBJECTIVES_WAR" ); + maps\mp\gametypes\_globallogic::setObjectiveText( "axis", &"OBJECTIVES_WAR" ); + + if ( level.splitscreen ) + { + maps\mp\gametypes\_globallogic::setObjectiveScoreText( "allies", &"OBJECTIVES_WAR" ); + maps\mp\gametypes\_globallogic::setObjectiveScoreText( "axis", &"OBJECTIVES_WAR" ); + } + else + { + maps\mp\gametypes\_globallogic::setObjectiveScoreText( "allies", &"OBJECTIVES_WAR_SCORE" ); + maps\mp\gametypes\_globallogic::setObjectiveScoreText( "axis", &"OBJECTIVES_WAR_SCORE" ); + } + maps\mp\gametypes\_globallogic::setObjectiveHintText( "allies", &"OBJECTIVES_WAR_HINT" ); + maps\mp\gametypes\_globallogic::setObjectiveHintText( "axis", &"OBJECTIVES_WAR_HINT" ); + + level.spawnMins = ( 0, 0, 0 ); + level.spawnMaxs = ( 0, 0, 0 ); + maps\mp\gametypes\_spawnlogic::addSpawnPoints( "allies", "mp_tdm_spawn" ); + maps\mp\gametypes\_spawnlogic::addSpawnPoints( "axis", "mp_tdm_spawn" ); + + level.mapCenter = maps\mp\gametypes\_spawnlogic::findBoxCenter( level.spawnMins, level.spawnMaxs ); + setMapCenter( level.mapCenter ); + + level.spawnpoints = getentarray("mp_tdm_spawn", "classname"); + + allowed[0] = "dm"; + allowed[1] = "war"; + allowed[2] = "sd"; + allowed[3] = "dom"; + allowed[4] = "sab"; + allowed[5] = "hq"; + allowed[6] = "bombzone"; + allowed[7] = "blocker"; + allowed[8] = "hardpoint"; + maps\mp\gametypes\_gameobjects::main(allowed); + + thread updateGametypeDvars(); + +} + +onSpawnPlayer() +{ + self.usingObj = undefined; + + if ( level.xboxlive ) + { + spawns = self getstat( 2000 ) + 1; + self setstat( 2000, spawns ); + } + + if ( level.inGracePeriod ) + { + spawnPoints = getentarray("mp_tdm_spawn_" + self.pers["team"] + "_start", "classname"); + + if ( !spawnPoints.size ) + spawnPoints = getentarray("mp_sab_spawn_" + self.pers["team"] + "_start", "classname"); + + if ( !spawnPoints.size ) + { + spawnPoints = maps\mp\gametypes\_spawnlogic::getTeamSpawnPoints( self.pers["team"] ); + spawnPoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_NearTeam( spawnPoints ); + } + else + { + spawnPoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_Random( spawnPoints ); + } + } + else + { + spawnPoints = maps\mp\gametypes\_spawnlogic::getTeamSpawnPoints( self.pers["team"] ); + spawnPoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_NearTeam( spawnPoints ); + } + + self spawn( spawnPoint.origin, spawnPoint.angles ); +} + + +updateGametypeDvars() +{ +} + + diff --git a/resources/xenia/plugins/415607E6/plugins.toml b/resources/xenia/plugins/415607E6/plugins.toml index 1f2ccfe..f783817 100644 --- a/resources/xenia/plugins/415607E6/plugins.toml +++ b/resources/xenia/plugins/415607E6/plugins.toml @@ -17,3 +17,11 @@ name = "iw3xe" file = "iw3xe.xex" hash = "F5F903E4F326EB10" # default_mp.xex is_enabled = true + +# Pre Alpha 253 MP +[[plugin]] +author = "mo" +name = "iw3xe" +file = "iw3xe.xex" +hash = "60FD640B7A28C69D" # default_mp.xex +is_enabled = true diff --git a/src/game/game.cpp b/src/game/game.cpp index 25e53ac..96bdb33 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -4,6 +4,7 @@ #include "game.h" #include "mp_main.h" #include "sp_main.h" +#include "iw3_253/main.h" namespace game { @@ -21,6 +22,11 @@ namespace game xbox::show_notification(L"IW3xe sp"); sp::init(); } + else if (strncmp((char *)0x8203A0A0, "multiplayer", 11) == 0) + { + xbox::show_notification(L"IW3xe 253 MP"); + iw3_253::init(); + } else { xbox::show_notification(L"IW3xe unsupported executable"); diff --git a/src/game/iw3_253/cg_consolecmds.cpp b/src/game/iw3_253/cg_consolecmds.cpp new file mode 100644 index 0000000..c9db25f --- /dev/null +++ b/src/game/iw3_253/cg_consolecmds.cpp @@ -0,0 +1,639 @@ +#pragma warning(push) +#pragma warning(disable : 4480) + +#include +#include +#include + +#include + +#include "..\..\detour.h" +#include "..\..\filesystem.h" +#include "..\..\xboxkrnl.h" + +namespace iw3_253 +{ + struct cmd_function_s + { + cmd_function_s *next; + const char *name; + const char *autoCompleteDir; + const char *autoCompleteExt; + void (*function)(); + }; + + typedef void (*Cmd_AddCommandInternal_t)(const char *cmdName, void (*function)(), cmd_function_s *allocedCmd); + Cmd_AddCommandInternal_t Cmd_AddCommandInternal = reinterpret_cast(0x821843F0); + + enum conChannel_t : __int32 + { + CON_CHANNEL_DONT_FILTER = 0x0, + CON_CHANNEL_ERROR = 0x1, + CON_CHANNEL_GAMENOTIFY = 0x2, + CON_CHANNEL_BOLDGAME = 0x3, + CON_CHANNEL_SUBTITLE = 0x4, + CON_CHANNEL_OBITUARY = 0x5, + CON_CHANNEL_LOGFILEONLY = 0x6, + CON_CHANNEL_CONSOLEONLY = 0x7, + CON_CHANNEL_GFX = 0x8, + CON_CHANNEL_SOUND = 0x9, + CON_CHANNEL_FILES = 0xA, + CON_CHANNEL_DEVGUI = 0xB, + CON_CHANNEL_PROFILE = 0xC, + CON_CHANNEL_UI = 0xD, + CON_CHANNEL_CLIENT = 0xE, + CON_CHANNEL_SERVER = 0xF, + CON_CHANNEL_SYSTEM = 0x10, + CON_CHANNEL_PLAYERWEAP = 0x11, + CON_CHANNEL_AI = 0x12, + CON_CHANNEL_ANIM = 0x13, + CON_CHANNEL_PHYS = 0x14, + CON_CHANNEL_FX = 0x15, + CON_CHANNEL_LEADERBOARDS = 0x16, + CON_CHANNEL_PARSERSCRIPT = 0x17, + CON_CHANNEL_SCRIPT = 0x18, + CON_BUILTIN_CHANNEL_COUNT = 0x19, + }; + + typedef void (*Com_Printf_t)(conChannel_t channel, const char *fmt, ...); + Com_Printf_t Com_Printf = reinterpret_cast(0x82184A78); + + typedef void (*Com_PrintError_t)(conChannel_t channel, const char *fmt, ...); + Com_PrintError_t Com_PrintError = reinterpret_cast(0x82184BE8); + + enum MapType : __int32 + { + MAPTYPE_NONE = 0x0, + MAPTYPE_INVALID1 = 0x1, + MAPTYPE_INVALID2 = 0x2, + MAPTYPE_2D = 0x3, + MAPTYPE_3D = 0x4, + MAPTYPE_CUBE = 0x5, + MAPTYPE_COUNT = 0x6, + }; + + struct CardMemory + { + int platform[1]; + }; + + struct GfxImageLoadDef; // Forward declaration + + union GfxTexture + { + D3DBaseTexture *basemap; + D3DTexture *map; + D3DVolumeTexture *volmap; + D3DCubeTexture *cubemap; + GfxImageLoadDef *loadDef; + }; + + struct GfxImageLoadDef + { + unsigned __int8 levelCount; + unsigned __int8 flags; + __int16 dimensions[3]; + int format; + GfxTexture texture; + }; + + struct GfxImage + { + MapType mapType; + GfxTexture texture; + unsigned __int8 semantic; + CardMemory cardMemory; + unsigned __int16 width; + unsigned __int16 height; + unsigned __int16 depth; + unsigned __int8 category; + unsigned __int8 *pixels; + unsigned int baseSize; + unsigned __int16 streamSlot; + bool streaming; + const char *name; + }; + + struct RawFile + { + const char *name; + int len; + const char *buffer; + }; + + enum XAssetType : __int32 + { + ASSET_TYPE_XMODELPIECES = 0x0, + ASSET_TYPE_PHYSPRESET = 0x1, + ASSET_TYPE_XANIMPARTS = 0x2, + ASSET_TYPE_XMODEL = 0x3, + ASSET_TYPE_MATERIAL = 0x4, + ASSET_TYPE_PIXELSHADER = 0x5, + ASSET_TYPE_TECHNIQUE_SET = 0x6, + ASSET_TYPE_IMAGE = 0x7, + ASSET_TYPE_SOUND = 0x8, + ASSET_TYPE_SOUND_CURVE = 0x9, + ASSET_TYPE_CLIPMAP = 0xA, + ASSET_TYPE_CLIPMAP_PVS = 0xB, + ASSET_TYPE_COMWORLD = 0xC, + ASSET_TYPE_GAMEWORLD_SP = 0xD, + ASSET_TYPE_GAMEWORLD_MP = 0xE, + ASSET_TYPE_MAP_ENTS = 0xF, + ASSET_TYPE_GFXWORLD = 0x10, + ASSET_TYPE_LIGHT_DEF = 0x11, + ASSET_TYPE_UI_MAP = 0x12, + ASSET_TYPE_FONT = 0x13, + ASSET_TYPE_MENULIST = 0x14, + ASSET_TYPE_MENU = 0x15, + ASSET_TYPE_LOCALIZE_ENTRY = 0x16, + ASSET_TYPE_WEAPON = 0x17, + ASSET_TYPE_SNDDRIVER_GLOBALS = 0x18, + ASSET_TYPE_FX = 0x19, + ASSET_TYPE_IMPACT_FX = 0x1A, + ASSET_TYPE_AITYPE = 0x1B, + ASSET_TYPE_MPTYPE = 0x1C, + ASSET_TYPE_CHARACTER = 0x1D, + ASSET_TYPE_XMODELALIAS = 0x1E, + ASSET_TYPE_RAWFILE = 0x1F, + ASSET_TYPE_STRINGTABLE = 0x20, + ASSET_TYPE_COUNT = 0x21, + ASSET_TYPE_STRING = 0x21, + ASSET_TYPE_ASSETLIST = 0x22, + }; + + union XAssetHeader + { + // XModelPieces *xmodelPieces; + // PhysPreset *physPreset; + // XAnimParts *parts; + // XModel *model; + // Material *material; + // MaterialPixelShader *pixelShader; + // MaterialVertexShader *vertexShader; + // MaterialTechniqueSet *techniqueSet; + GfxImage *image; + // snd_alias_list_t *sound; + // SndCurve *sndCurve; + // clipMap_t *clipMap; + // ComWorld *comWorld; + // GameWorldSp *gameWorldSp; + // GameWorldMp *gameWorldMp; + // MapEnts *mapEnts; + // GfxWorld *gfxWorld; + // GfxLightDef *lightDef; + // Font_s *font; + // MenuList *menuList; + // menuDef_t *menu; + // LocalizeEntry *localize; + // WeaponDef *weapon; + // SndDriverGlobals *sndDriverGlobals; + // const FxEffectDef *fx; + // FxImpactTable *impactFx; + RawFile *rawfile; + // StringTable *stringTable; + void *data; + }; + + typedef int (*DB_GetAllXAssetOfType_FastFile_t)(XAssetType type, void *assets, int maxCount); + DB_GetAllXAssetOfType_FastFile_t DB_GetAllXAssetOfType_FastFile = reinterpret_cast(0x82124C30); + + void Cmd_dump_rawfiles_f() + { + const int MAX_RAWFILES = 2048; + XAssetHeader assets[MAX_RAWFILES]; + int count = DB_GetAllXAssetOfType_FastFile(ASSET_TYPE_RAWFILE, assets, MAX_RAWFILES); + // Com_Printf(CON_CHANNEL_CONSOLEONLY, "Dumping %d raw files to 'game::\\_iw3xe\\raw\\' %d\n", count); + for (int i = 0; i < count; i++) + { + auto rawfile = assets[i].rawfile; + std::string asset_name = rawfile->name; + std::replace(asset_name.begin(), asset_name.end(), '/', '\\'); // Replace forward slashes with backslashes + filesystem::write_file_to_disk(("game:\\_iw3xe\\dump\\" + asset_name).c_str(), rawfile->buffer, rawfile->len); + } + Com_Printf(CON_CHANNEL_CONSOLEONLY, "Dumped %d raw files to 'game::\\_iw3xe\\dump\\'\n", count); + } + + struct cplane_s; + struct cStaticModel_s; + struct dmaterial_t; + struct cbrushside_t; + struct cNode_t; + struct cLeaf_t; + struct cLeafBrushNode_s; + struct CollisionBorder; + struct CollisionPartition; + struct CollisionAabbTree; + struct cmodel_t; + struct cbrush_t; + + struct MapEnts + { + const char *name; + char *entityString; + int numEntityChars; + }; + + struct clipMap_t + { + const char *name; + int planeCount; + cplane_s *planes; + unsigned int numStaticModels; + cStaticModel_s *staticModelList; + unsigned int numMaterials; + dmaterial_t *materials; + unsigned int numBrushSides; + cbrushside_t *brushsides; + unsigned int numBrushEdges; + unsigned __int8 *brushEdges; + unsigned int numNodes; + cNode_t *nodes; + unsigned int numLeafs; + cLeaf_t *leafs; + unsigned int leafbrushNodesCount; + cLeafBrushNode_s *leafbrushNodes; + unsigned int numLeafBrushes; + unsigned __int16 *leafbrushes; + unsigned int numLeafSurfaces; + unsigned int *leafsurfaces; + unsigned int vertCount; + float (*verts)[3]; + int triCount; + unsigned __int16 *triIndices; + unsigned __int8 *triEdgeIsWalkable; + int borderCount; + CollisionBorder *borders; + int partitionCount; + CollisionPartition *partitions; + int aabbTreeCount; + CollisionAabbTree *aabbTrees; + unsigned int numSubModels; + cmodel_t *cmodels; + unsigned __int16 numBrushes; + cbrush_t *brushes; + int numClusters; + int clusterBytes; + unsigned __int8 *visibility; + int vised; + MapEnts *mapEnts; + cbrush_t *box_brush; + // cmodel_t box_model; + // unsigned __int16 dynEntCount[4]; + // DynEntityDef *dynEntDefList[2]; + // DynEntityClient *dynEntClientList[2]; + // DynEntityServer *dynEntServerList[2]; + // DynEntityColl *dynEntCollList[4]; + // unsigned int checksum; + }; + + auto cm = reinterpret_cast(0x836CDB08); + + void Cmd_dump_mapents_f() + { + if (!cm->name) + { + Com_PrintError(CON_CHANNEL_CONSOLEONLY, "No map loaded\n"); + return; + } + + // cm.name contains a string like this `maps/mp/mp_backlot.d3dsp` + std::string asset_name = cm->name; + asset_name += ".ents"; + std::replace(asset_name.begin(), asset_name.end(), '/', '\\'); // Replace forward slashes with backslashes + filesystem::write_file_to_disk(("game:\\_iw3xe\\dump\\" + asset_name).c_str(), cm->mapEnts->entityString, cm->mapEnts->numEntityChars); + Com_Printf(CON_CHANNEL_CONSOLEONLY, "Dumped map ents to 'game:\\_iw3xe\\dump\\%s'\n", cm->mapEnts->numEntityChars, asset_name.c_str()); + } + + struct DDSHeader + { + uint32_t magic; + uint32_t size; + uint32_t flags; + uint32_t height; + uint32_t width; + uint32_t pitchOrLinearSize; + uint32_t depth; + uint32_t mipMapCount; + uint32_t reserved1[11]; + struct + { + uint32_t size; + uint32_t flags; + uint32_t fourCC; + uint32_t rgbBitCount; + uint32_t rBitMask; + uint32_t gBitMask; + uint32_t bBitMask; + uint32_t aBitMask; + } pixelFormat; + uint32_t caps; + uint32_t caps2; + uint32_t caps3; + uint32_t caps4; + uint32_t reserved2; + }; + + static_assert(sizeof(DDSHeader) == 128, ""); + + const uint32_t DDS_MAGIC = MAKEFOURCC('D', 'D', 'S', ' '); + const uint32_t DDS_HEADER_SIZE = 124; + const uint32_t DDS_PIXEL_FORMAT_SIZE = 32; + const uint32_t DDSD_CAPS = 0x1; + const uint32_t DDSD_HEIGHT = 0x2; + const uint32_t DDSD_WIDTH = 0x4; + const uint32_t DDSD_PIXELFORMAT = 0x1000; + const uint32_t DDSD_LINEARSIZE = 0x80000; + const uint32_t DDPF_FOURCC = 0x4; + const uint32_t DDPF_RGB = 0x40; + const uint32_t DDPF_ALPHAPIXELS = 0x1; + const uint32_t DDSCAPS_TEXTURE = 0x1000; + const uint32_t DDSCAPS_MIPMAP = 0x400000; + const uint32_t DDPF_LUMINANCE = 0x20000; + + // DDS Pixel Formats (FourCC Codes) + const uint32_t DXT1_FOURCC = MAKEFOURCC('D', 'X', 'T', '1'); + const uint32_t DXT3_FOURCC = MAKEFOURCC('D', 'X', 'T', '3'); + const uint32_t DXT5_FOURCC = MAKEFOURCC('D', 'X', 'T', '5'); + const uint32_t DXN_FOURCC = MAKEFOURCC('A', 'T', 'I', '2'); // (DXN / BC5) + + // Additional DDS Cubemap Flags + const uint32_t DDSCAPS2_CUBEMAP = 0x200; + const uint32_t DDSCAPS2_CUBEMAP_POSITIVEX = 0x400; + const uint32_t DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800; + const uint32_t DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000; + const uint32_t DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000; + const uint32_t DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000; + const uint32_t DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000; + + void GPUEndianSwapTexture(std::vector &pixelData, GPUENDIAN endianType) + { + switch (endianType) + { + case GPUENDIAN_8IN16: + XGEndianSwapMemory(pixelData.data(), pixelData.data(), XGENDIAN_8IN16, 2, pixelData.size() / 2); + break; + case GPUENDIAN_8IN32: + XGEndianSwapMemory(pixelData.data(), pixelData.data(), XGENDIAN_8IN32, 4, pixelData.size() / 4); + break; + case GPUENDIAN_16IN32: + XGEndianSwapMemory(pixelData.data(), pixelData.data(), XGENDIAN_16IN32, 4, pixelData.size() / 4); + break; + } + } + + void Dump_Image(const GfxImage *image) + { + if (!image) + { + Com_PrintError(CON_CHANNEL_ERROR, "Image_Dump: Null GfxImage!\n"); + return; + } + + if (image->mapType != MAPTYPE_2D && image->mapType != MAPTYPE_CUBE) + { + Com_PrintError(CON_CHANNEL_ERROR, "Image_Dump: Unsupported map type %d!\n", image->mapType); + return; + } + + UINT BaseSize; + XGGetTextureLayout(image->texture.basemap, 0, &BaseSize, 0, 0, 0, 0, 0, 0, 0, 0); + + DDSHeader header; + memset(&header, 0, sizeof(DDSHeader)); + + header.magic = _byteswap_ulong(DDS_MAGIC); + header.size = _byteswap_ulong(DDS_HEADER_SIZE); + header.flags = _byteswap_ulong(DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_LINEARSIZE); + header.height = _byteswap_ulong(image->height); + header.width = _byteswap_ulong(image->width); + header.depth = _byteswap_ulong(image->depth); + header.mipMapCount = _byteswap_ulong(image->texture.basemap->GetLevelCount()); + + auto format = image->texture.basemap->Format.DataFormat; + switch (format) + { + case GPUTEXTUREFORMAT_DXT1: + header.pixelFormat.fourCC = _byteswap_ulong(DXT1_FOURCC); + header.pitchOrLinearSize = BaseSize; + break; + case GPUTEXTUREFORMAT_DXT2_3: + header.pixelFormat.fourCC = _byteswap_ulong(DXT3_FOURCC); + header.pitchOrLinearSize = BaseSize; + break; + case GPUTEXTUREFORMAT_DXT4_5: + header.pixelFormat.fourCC = _byteswap_ulong(DXT5_FOURCC); + header.pitchOrLinearSize = BaseSize; + break; + case GPUTEXTUREFORMAT_DXN: + header.pixelFormat.fourCC = _byteswap_ulong(DXN_FOURCC); + header.pitchOrLinearSize = BaseSize; + break; + case GPUTEXTUREFORMAT_8: + header.pixelFormat.flags = _byteswap_ulong(DDPF_LUMINANCE); + header.pixelFormat.rgbBitCount = _byteswap_ulong(8); + header.pixelFormat.rBitMask = _byteswap_ulong(0x000000FF); + header.pixelFormat.gBitMask = 0; + header.pixelFormat.bBitMask = 0; + header.pixelFormat.aBitMask = 0; + header.pitchOrLinearSize = BaseSize; + break; + case GPUTEXTUREFORMAT_8_8: + header.pixelFormat.flags = _byteswap_ulong(DDPF_LUMINANCE | DDPF_ALPHAPIXELS); + header.pixelFormat.rgbBitCount = _byteswap_ulong(16); + header.pixelFormat.rBitMask = _byteswap_ulong(0x000000FF); + header.pixelFormat.gBitMask = _byteswap_ulong(0x0000FF00); + header.pixelFormat.bBitMask = 0; + header.pixelFormat.aBitMask = 0; + header.pitchOrLinearSize = BaseSize; + break; + case GPUTEXTUREFORMAT_8_8_8_8: + header.pixelFormat.flags = _byteswap_ulong(DDPF_RGB | DDPF_ALPHAPIXELS); + header.pixelFormat.rgbBitCount = _byteswap_ulong(32); + header.pixelFormat.rBitMask = _byteswap_ulong(0x00FF0000); + header.pixelFormat.gBitMask = _byteswap_ulong(0x0000FF00); + header.pixelFormat.bBitMask = _byteswap_ulong(0x000000FF); + header.pixelFormat.aBitMask = _byteswap_ulong(0xFF000000); + header.pitchOrLinearSize = BaseSize; + break; + default: + Com_PrintError(CON_CHANNEL_ERROR, "Image_Dump: Unsupported texture format %d!\n", format); + return; + } + + // Set texture capabilities + header.caps = _byteswap_ulong(DDSCAPS_TEXTURE | DDSCAPS_MIPMAP); + + // Handle Cubemaps + if (image->mapType == MAPTYPE_CUBE) + { + header.caps2 = _byteswap_ulong(DDSCAPS2_CUBEMAP | + DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX | + DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_NEGATIVEY | + DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ); + } + + std::string filename = "game:\\_iw3xe\\dump\\images\\"; + std::string sanitized_name = image->name; + + // Remove invalid characters + sanitized_name.erase(std::remove_if(sanitized_name.begin(), sanitized_name.end(), [](char c) + { return c == '*'; }), + sanitized_name.end()); + + filename += sanitized_name + ".dds"; + + std::ofstream file(filename, std::ios::binary); + if (!file) + { + Com_PrintError(CON_CHANNEL_ERROR, "Image_Dump: Failed to open file: %s\n", filename.c_str()); + return; + } + + if (image->mapType == MAPTYPE_CUBE) + { + file.write(reinterpret_cast(&header), sizeof(DDSHeader)); + + unsigned int face_size = 0; + unsigned int rowPitch = 0; + const GPUTEXTUREFORMAT format = static_cast(image->texture.basemap->Format.DataFormat); + + switch (format) + { + case GPUTEXTUREFORMAT_DXT1: + face_size = (image->width / 4) * (image->height / 4) * 8; + rowPitch = (image->width / 4) * 8; // 8 bytes per 4x4 block + break; + case GPUTEXTUREFORMAT_8_8_8_8: + face_size = image->width * image->height * 4; + rowPitch = image->width * 4; // 4 bytes per pixel + break; + default: + Com_PrintError(CON_CHANNEL_ERROR, "Image_Dump: Unsupported cube map format %d!\n", format); + return; + } + + // TODO: handle mip levels per face for cubemaps + for (int i = 0; i < 6; i++) + { + unsigned char *face_pixels = image->pixels + (i * face_size); // Offset for each face + + std::vector swappedFace(face_pixels, face_pixels + face_size); + GPUEndianSwapTexture(swappedFace, static_cast(image->texture.basemap->Format.Endian)); + + // Create buffer for linear texture data + std::vector linearFace(face_size); + + // Convert tiled texture to linear layout using XGUntileTextureLevel + XGUntileTextureLevel( + image->width, // Width + image->height, // Height + 0, // Level (base level) + static_cast(format), // GpuFormat + 0, // Flags (no special flags) + linearFace.data(), // pDestination (linear output) + rowPitch, // RowPitch + nullptr, // pPoint (no offset) + swappedFace.data(), // pSource (tiled input) + nullptr // pRect (entire texture) + ); + + file.write(reinterpret_cast(linearFace.data()), linearFace.size()); + } + + file.close(); + } + else if (image->mapType == MAPTYPE_2D) + { + // TODO: write mip levels + file.write(reinterpret_cast(&header), sizeof(DDSHeader)); + + std::vector pixelData(image->pixels, image->pixels + image->baseSize); + + GPUEndianSwapTexture(pixelData, static_cast(image->texture.basemap->Format.Endian)); + + // Create a linear data buffer to hold the untiled texture + std::vector linearData(image->baseSize); + + // Calculate row pitch based on format + UINT rowPitch; + auto format = image->texture.basemap->Format.DataFormat; + + switch (format) + { + case GPUTEXTUREFORMAT_DXT1: + case GPUTEXTUREFORMAT_DXT2_3: + case GPUTEXTUREFORMAT_DXT4_5: + case GPUTEXTUREFORMAT_DXN: + // Block compressed formats use 4x4 blocks + rowPitch = ((image->width + 3) / 4) * (format == GPUTEXTUREFORMAT_DXT1 ? 8 : 16); + break; + case GPUTEXTUREFORMAT_8: + rowPitch = image->width; + break; + case GPUTEXTUREFORMAT_8_8: + rowPitch = image->width * 2; + break; + case GPUTEXTUREFORMAT_8_8_8_8: + rowPitch = image->width * 4; + break; + default: + Com_PrintError(CON_CHANNEL_ERROR, "Image_Dump: Unsupported texture format %d!\n", format); + return; + } + + xbox::DbgPrint("Image_Dump: rowPitch=%d\n", rowPitch); + + // Call XGUntileTextureLevel to convert the tiled texture to linear format + XGUntileTextureLevel( + image->width, // Width + image->height, // Height + 0, // Level (base level 0) + static_cast(format), // GpuFormat + XGTILE_NONPACKED, // Flags (no special flags) + linearData.data(), // pDestination + rowPitch, // RowPitch (calculated based on format) + nullptr, // pPoint (no offset) + pixelData.data(), // pSource + nullptr // pRect (entire texture) + ); + + file.write(reinterpret_cast(linearData.data()), linearData.size()); + + file.close(); + } + else + { + Com_PrintError(CON_CHANNEL_ERROR, "Image_Dump: Unsupported map type %d!\n", image->mapType); + return; + } + } + + void Cmd_dump_images_f() + { + CreateDirectoryA("game:\\_iw3xe", nullptr); + CreateDirectoryA("game:\\_iw3xe\\dump", nullptr); + CreateDirectoryA("game:\\_iw3xe\\dump\\images", nullptr); + + const int MAX = 2048; + XAssetHeader assets[MAX]; + int count = DB_GetAllXAssetOfType_FastFile(ASSET_TYPE_IMAGE, assets, MAX); + + for (int i = 0; i < count; i++) + { + Dump_Image(assets[i].image); + } + Com_Printf(CON_CHANNEL_CONSOLEONLY, "Dumped %d rimages to 'game:\\_iw3xe\\dump\\images'\n", count); + } + + void init_cg_consolecmds() + { + cmd_function_s *dump_images_VAR = new cmd_function_s; + Cmd_AddCommandInternal("dump_images", Cmd_dump_images_f, dump_images_VAR); + + cmd_function_s *dump_mapents_VAR = new cmd_function_s; + Cmd_AddCommandInternal("dump_mapents", Cmd_dump_mapents_f, dump_mapents_VAR); + + cmd_function_s *dump_rawfiles_VAR = new cmd_function_s; + Cmd_AddCommandInternal("dump_rawfiles", Cmd_dump_rawfiles_f, dump_rawfiles_VAR); + } +} \ No newline at end of file diff --git a/src/game/iw3_253/cg_consolecmds.h b/src/game/iw3_253/cg_consolecmds.h new file mode 100644 index 0000000..5954e24 --- /dev/null +++ b/src/game/iw3_253/cg_consolecmds.h @@ -0,0 +1,10 @@ +#include + +#include "..\..\detour.h" +#include "..\..\filesystem.h" +#include "..\..\xboxkrnl.h" + +namespace iw3_253 +{ + void init_cg_consolecmds(); +} diff --git a/src/game/iw3_253/main.cpp b/src/game/iw3_253/main.cpp new file mode 100644 index 0000000..2d81ab7 --- /dev/null +++ b/src/game/iw3_253/main.cpp @@ -0,0 +1,247 @@ +#include + +#include "cg_consolecmds.h" +#include "scr_parser.h" +#include "structs.h" +#include "..\..\detour.h" + +extern "C" void DbgPrint(const char *format, ...); + +namespace iw3_253 +{ + typedef dvar_s *(*Dvar_FindMalleableVar_t)(const char *dvarName); + Dvar_FindMalleableVar_t Dvar_FindMalleableVar = reinterpret_cast(0x821E0780); + + typedef dvar_s *(*Dvar_RegisterBool_t)(const char *dvarName, bool value, unsigned __int16 flags, const char *description); + Dvar_RegisterBool_t Dvar_RegisterBool = reinterpret_cast(0x821E1718); + + typedef dvar_s *(*Dvar_RegisterInt_t)(const char *dvarName, int value, int min, int max, unsigned __int16 flags, const char *description); + Dvar_RegisterInt_t Dvar_RegisterInt = reinterpret_cast(0x821E1788); + + typedef void (*PM_FoliageSounds_t)(pmove_t *pm); + typedef void (*Pmove_t)(pmove_t *pm); + typedef void (*PmoveSingle_t)(pmove_t *pm); + + PM_FoliageSounds_t PM_FoliageSounds = reinterpret_cast(0x820B27F8); + Pmove_t Pmove = reinterpret_cast(0x820B4F48); + PmoveSingle_t PmoveSingle = reinterpret_cast(0x820B4440); + + typedef void (*CG_DrawActive_t)(int localClientNum); + CG_DrawActive_t CG_DrawActive = reinterpret_cast(0x820CA770); + + typedef int (*R_RegisterFont_t)(const char *name); + R_RegisterFont_t R_RegisterFont = reinterpret_cast(0x8216EC00); + + typedef void (*R_AddCmdDrawText_t)(const char *text, int maxChars, Font_s *font, double x, double y, double xScale, double yScale, double rotation, const float *color, int style); + R_AddCmdDrawText_t R_AddCmdDrawText = (R_AddCmdDrawText_t)0x8228F348; + + typedef bool (*Cmd_ExecFromFastFile_t)(int localClientNum, int controllerIndex, const char *filename); + Cmd_ExecFromFastFile_t Cmd_ExecFromFastFile = reinterpret_cast(0x8223AF40); + + typedef void (*Cbuf_AddText_t)(int localClientNum, const char *text); + Cbuf_AddText_t Cbuf_AddText = reinterpret_cast(0x82183458); + + typedef void (*Cbuf_ExecuteBuffer_t)(int localClientNum, const char *buffer); + Cbuf_ExecuteBuffer_t Cbuf_ExecuteBuffer = reinterpret_cast(0x821838D0); + + struct scr_entref_t + { + unsigned __int16 entnum; + unsigned __int16 classnum; + }; + + typedef void (*BuiltinMethod)(scr_entref_t); + typedef BuiltinMethod (*Scr_GetMethod_t)(const char **pName, int *type); + Scr_GetMethod_t Scr_GetMethod = reinterpret_cast(0x82167998); + + typedef char *(*Scr_GetString_t)(unsigned int index); + Scr_GetString_t Scr_GetString = reinterpret_cast(0x821AC760); + + typedef void (*PM_UpdateSprint_t)(pmove_t *pm, const pml_t *pml); + PM_UpdateSprint_t PM_UpdateSprint = reinterpret_cast(0x820ADD50); + + ScreenPlacement &scrPlaceFullUnsafe = *reinterpret_cast(0x831ACDB8); + + cgMedia_t &cgMedia = *reinterpret_cast(0x82848250); + + Detour PM_UpdateSprint_Detour; + + void PM_UpdateSprint_Hook(pmove_t *pm, const pml_t *pml) + { + DbgPrint("PM_UpdateSprint_Hook\n"); + PM_UpdateSprint_Detour.GetOriginal()(pm, pml); + + // Works but prevents bouncing and mantling + // pm->ps->pm_flags &= ~0x1401Cu; + + // pm->ps->sprintState.lastSprintEnd = 0; + } + + Detour Pmove_Detour; + + // https://github.com/kejjjjj/iw3sptool/blob/17b669233a1ad086deed867469dc9530b84c20e6/iw3sptool/bg/bg_pmove.cpp#L11 + void Pmove_Hook(pmove_t *pm) + { + static dvar_s *pm_fixed_fps_enable = Dvar_FindMalleableVar("pm_fixed_fps_enable"); + static dvar_s *pm_fixed_fps = Dvar_FindMalleableVar("pm_fixed_fps"); + + if (!pm_fixed_fps_enable->current.enabled) + return Pmove_Detour.GetOriginal()(pm); + + int msec = 0; + int cur_msec = 1000 / pm_fixed_fps->current.integer; + + pm->cmd.serverTime = ((pm->cmd.serverTime + (cur_msec < 2 ? 2 : cur_msec) - 1) / cur_msec) * cur_msec; + + int finalTime = pm->cmd.serverTime; + + if (finalTime < pm->ps->commandTime) + { + return; // should not happen + } + + if (finalTime > pm->ps->commandTime + 1000) + pm->ps->commandTime = finalTime - 1000; + pm->numtouch = 0; + + while (pm->ps->commandTime != finalTime) + { + msec = finalTime - pm->ps->commandTime; + + if (msec > cur_msec) + msec = cur_msec; + + pm->cmd.serverTime = msec + pm->ps->commandTime; + PmoveSingle(pm); + memcpy(&pm->oldcmd, &pm->cmd, sizeof(pm->oldcmd)); + } + } + + bool g_pm_foliage_ran_this_frame = false; + + void Sys_SnapVector_RoundToNearestEven(float *v) + { + // Use __frnd for round-to-nearest-even behavior + v[0] = (float)__frnd((double)v[0]); + v[1] = (float)__frnd((double)v[1]); + v[2] = (float)__frnd((double)v[2]); + } + + Detour PmoveSingle_Detour; + + void PmoveSingle_Hook(pmove_t *pm) + { + DbgPrint("PmoveSingle_Hook\n"); + g_pm_foliage_ran_this_frame = false; + PmoveSingle_Detour.GetOriginal()(pm); + if (g_pm_foliage_ran_this_frame) + { + DbgPrint("Sys_SnapVector_RoundToNearestEven\n"); + Sys_SnapVector_RoundToNearestEven(pm->ps->velocity); + } + + g_pm_foliage_ran_this_frame = false; + } + + Detour PM_FoliageSounds_Detour; + + void PM_FoliageSounds_Hook(pmove_t *pm) + { + DbgPrint("PM_FoliageSounds_Hook\n"); + PM_FoliageSounds_Detour.GetOriginal()(pm); + g_pm_foliage_ran_this_frame = true; + } + + void DrawFixedFPS() + { + static dvar_s *pm_fixed_fps = Dvar_FindMalleableVar("pm_fixed_fps"); + + char buff[16]; + sprintf_s(buff, "%d", pm_fixed_fps->current.integer); + + float color[4] = {1, 1, 1, 1}; + float x = 620 * scrPlaceFullUnsafe.scaleVirtualToFull[0]; + float y = 15 * scrPlaceFullUnsafe.scaleVirtualToFull[1]; + R_AddCmdDrawText(buff, 16, cgMedia.bigDevFont, x, y, 1.0, 1.0, 0.0, color, 0); + } + + Detour CG_DrawActive_Detour; + + void CG_DrawActive_Hook(int localClientNum) + { + static dvar_s *pm_fixed_fps_enable = Dvar_FindMalleableVar("pm_fixed_fps_enable"); + + if (pm_fixed_fps_enable->current.enabled) + { + DrawFixedFPS(); + } + + CG_DrawActive_Detour.GetOriginal()(localClientNum); + } + + void GScr_Cbuf_ExecuteBuffer(scr_entref_t entref) + { + auto text = Scr_GetString(0); + Cbuf_ExecuteBuffer(0, text); + } + + Detour Scr_GetMethod_Detour; + + BuiltinMethod Scr_GetMethod_Hook(const char **pName, int *type) + { + if (std::strcmp(*pName, "cbuf_executebuffer") == 0) + return reinterpret_cast(&GScr_Cbuf_ExecuteBuffer); + + return Scr_GetMethod_Detour.GetOriginal()(pName, type); + } + + void init() + { + Pmove_Detour = Detour(Pmove, Pmove_Hook); + Pmove_Detour.Install(); + + Dvar_RegisterBool("pm_fixed_fps_enable", false, 0, "Enable fixed FPS mode"); + Dvar_RegisterInt("pm_fixed_fps", 250, 0, 1000, 0, "Fixed FPS value"); + + init_cg_consolecmds(); + init_scr_parser(); + + PmoveSingle_Detour = Detour(PmoveSingle, PmoveSingle_Hook); + PmoveSingle_Detour.Install(); + + PM_FoliageSounds_Detour = Detour(PM_FoliageSounds, PM_FoliageSounds_Hook); + PM_FoliageSounds_Detour.Install(); + + // Nop the function that calls Sys_SnapVector + uintptr_t start = 0x820B4EB4; + uintptr_t end = 0x820B4F34; + + for (uintptr_t addr = start; addr <= end; addr += 4) + { + *(uint32_t *)addr = 0x60000000; // PowerPC NOP + } + + CG_DrawActive_Detour = Detour(CG_DrawActive, CG_DrawActive_Hook); + CG_DrawActive_Detour.Install(); + + Scr_GetMethod_Detour = Detour(Scr_GetMethod, Scr_GetMethod_Hook); + Scr_GetMethod_Detour.Install(); + + PM_UpdateSprint_Detour = Detour(PM_UpdateSprint, PM_UpdateSprint_Hook); + PM_UpdateSprint_Detour.Install(); + + // Remove read-only protection from DVARs + + // .text:821E04F4 bl _Com_Printf__YAXHPBDZZ # Com_Printf(int,char const *,...) + // .text:821E04F8 lwz r3, 0x4C0+var_40(r1) # cookie + // .text:821E04FC bl __security_check_cookie + // .text:821E0500 addi r1, r1, 0x4C0 + // .text:821E0504 b __restgprlr_27 + + // DVARS + // Patch read-only check to always succeed + *(unsigned int *)0x821E04E0 = 0x48000028; // b loc_821E0508 + // Patch write protection check to always succeed + *(unsigned int *)0x821E0510 = 0x48000028; // b loc_821E0538 + } +} diff --git a/src/game/iw3_253/main.h b/src/game/iw3_253/main.h new file mode 100644 index 0000000..ed7ee46 --- /dev/null +++ b/src/game/iw3_253/main.h @@ -0,0 +1,8 @@ +#pragma once + +#include "structs.h" + +namespace iw3_253 +{ + void init(); +} diff --git a/src/game/iw3_253/scr_parser.cpp b/src/game/iw3_253/scr_parser.cpp new file mode 100644 index 0000000..5a192ca --- /dev/null +++ b/src/game/iw3_253/scr_parser.cpp @@ -0,0 +1,56 @@ +#include + +#include "..\..\detour.h" +#include "..\..\filesystem.h" +#include "..\..\xboxkrnl.h" + +namespace iw3_253 +{ + typedef char *(*Scr_AddSourceBuffer_t)(const char *filename, const char *extFilename, const char *codePos, bool archive); + + Scr_AddSourceBuffer_t Scr_AddSourceBuffer = reinterpret_cast(0x8219DDA8); + + Detour Scr_AddSourceBuffer_Detour; + + char *Scr_AddSourceBuffer_Hook(const char *filename, const char *extFilename, const char *codePos, bool archive) + { + xbox::DbgPrint("Scr_AddSourceBuffer_Hook extFilename=%s \n", extFilename); + + std::string raw_file_path = "game:\\_iw3xe\\raw\\"; + raw_file_path += extFilename; + std::replace(raw_file_path.begin(), raw_file_path.end(), '/', '\\'); // Replace forward slashes with backslashes + if (filesystem::file_exists(raw_file_path)) + { + xbox::DbgPrint("Found raw file: %s\n", raw_file_path.c_str()); + std::string new_contents = filesystem::read_file_to_string(raw_file_path); + if (!new_contents.empty()) + { + + // Allocate new memory and copy the data + size_t new_size = new_contents.size() + 1; // Include null terminator + char *new_memory = static_cast(malloc(new_size)); + + if (new_memory) + { + memcpy(new_memory, new_contents.c_str(), new_size); // Copy with null terminator + + xbox::DbgPrint("Replaced contents from file: %s\n", raw_file_path.c_str()); + return new_memory; + } + else + { + xbox::DbgPrint("Failed to allocate memory for contents replacement.\n"); + } + } + } + + return Scr_AddSourceBuffer_Detour.GetOriginal()(filename, extFilename, codePos, archive); + } + + void init_scr_parser() + { + xbox::DbgPrint("Initializing Scr_AddSourceBuffer detour...\n"); + Scr_AddSourceBuffer_Detour = Detour(Scr_AddSourceBuffer, Scr_AddSourceBuffer_Hook); + Scr_AddSourceBuffer_Detour.Install(); + } +} \ No newline at end of file diff --git a/src/game/iw3_253/scr_parser.h b/src/game/iw3_253/scr_parser.h new file mode 100644 index 0000000..22addef --- /dev/null +++ b/src/game/iw3_253/scr_parser.h @@ -0,0 +1,4 @@ +namespace iw3_253 +{ + void init_scr_parser(); +} diff --git a/src/game/iw3_253/structs.h b/src/game/iw3_253/structs.h new file mode 100644 index 0000000..03c4bdd --- /dev/null +++ b/src/game/iw3_253/structs.h @@ -0,0 +1,425 @@ +#pragma warning(disable : 4480) + +namespace iw3_253 +{ + struct __declspec(align(4)) usercmd_s + { + int serverTime; + int buttons; + unsigned __int8 weapon; + unsigned __int8 offHandIndex; + int angles[3]; + char forwardmove; + char rightmove; + float meleeChargeYaw; + unsigned __int8 meleeChargeDist; + }; + + enum OffhandSecondaryClass : __int32 + { + PLAYER_OFFHAND_SECONDARY_SMOKE = 0x0, + PLAYER_OFFHAND_SECONDARY_FLASH = 0x1, + PLAYER_OFFHAND_SECONDARIES_TOTAL = 0x2, + }; + + enum ViewLockTypes : __int32 + { + PLAYERVIEWLOCK_NONE = 0x0, + PLAYERVIEWLOCK_FULL = 0x1, + PLAYERVIEWLOCK_WEAPONJITTER = 0x2, + PLAYERVIEWLOCKCOUNT = 0x3, + }; + + struct SprintState + { + int sprintButtonUpRequired; + int sprintDelay; + int lastSprintStart; + int lastSprintEnd; + int sprintStartMaxLength; + }; + + struct MantleState + { + float yaw; + int timer; + int transIndex; + int flags; + }; + + enum ActionSlotType : __int32 + { + ACTIONSLOTTYPE_DONOTHING = 0x0, + ACTIONSLOTTYPE_SPECIFYWEAPON = 0x1, + ACTIONSLOTTYPE_ALTWEAPONTOGGLE = 0x2, + ACTIONSLOTTYPE_NIGHTVISION = 0x3, + ACTIONSLOTTYPECOUNT = 0x4, + }; + + struct ActionSlotParam_SpecifyWeapon + { + unsigned int index; + }; + + struct ActionSlotParam + { + ActionSlotParam_SpecifyWeapon specifyWeapon; + }; + + enum objectiveState_t : __int32 + { + OBJST_EMPTY = 0x0, + OBJST_ACTIVE = 0x1, + OBJST_INVISIBLE = 0x2, + OBJST_DONE = 0x3, + OBJST_CURRENT = 0x4, + OBJST_FAILED = 0x5, + OBJST_NUMSTATES = 0x6, + }; + + struct objective_t + { + objectiveState_t state; + float origin[3]; + int entNum; + int teamNum; + int icon; + }; + + struct playerState_s + { + int commandTime; + int pm_type; + int bobCycle; + int pm_flags; + int weapFlags; + int otherFlags; + int pm_time; + float origin[3]; + float velocity[3]; + float oldVelocity[2]; + int weaponTime; + int weaponDelay; + int grenadeTimeLeft; + int throwBackGrenadeOwner; + int throwBackGrenadeTimeLeft; + int weaponRestrictKickTime; + int foliageSoundTime; + int gravity; + float leanf; + int speed; + float delta_angles[3]; + int groundEntityNum; + float vLadderVec[3]; + int jumpTime; + float jumpOriginZ; + int legsTimer; + int legsAnim; + int torsoTimer; + int torsoAnim; + int legsAnimDuration; + int torsoAnimDuration; + int damageTimer; + int damageDuration; + int flinchYawAnim; + int movementDir; + int eFlags; + int eventSequence; + int events[4]; + unsigned int eventParms[4]; + int oldEventSequence; + int clientNum; + int offHandIndex; + OffhandSecondaryClass offhandSecondary; + unsigned int weapon; + int weaponstate; + unsigned int weaponShotCount; + float fWeaponPosFrac; + int adsDelayTime; + int spreadOverride; + int spreadOverrideState; + int viewmodelIndex; + float viewangles[3]; + int viewHeightTarget; + float viewHeightCurrent; + int viewHeightLerpTime; + int viewHeightLerpTarget; + int viewHeightLerpDown; + float viewHeightLerpPosAdj; + float viewAngleClampBase[2]; + float viewAngleClampRange[2]; + int damageEvent; + int damageYaw; + int damagePitch; + int damageCount; + int stats[5]; + int ammo[128]; + int ammoclip[128]; + unsigned int weapons[4]; + unsigned int weaponold[4]; + unsigned int weaponrechamber[4]; + float proneDirection; + float proneDirectionPitch; + float proneTorsoPitch; + ViewLockTypes viewlocked; + int viewlocked_entNum; + int cursorHint; + int cursorHintString; + int cursorHintEntIndex; + int iCompassPlayerInfo; + int radarEnabled; + int locationSelectionInfo; + SprintState sprintState; + float fTorsoPitch; + float fWaistPitch; + float holdBreathScale; + int holdBreathTimer; + MantleState mantleState; + float meleeChargeYaw; + int meleeChargeDist; + ActionSlotType actionSlotType[4]; + ActionSlotParam actionSlotParam[4]; + int entityEventSequence; + int weapAnim; + float aimSpreadScale; + int shellshockIndex; + int shellshockTime; + int shellshockDuration; + float dofNearStart; + float dofNearEnd; + float dofFarStart; + float dofFarEnd; + float dofNearBlur; + float dofFarBlur; + objective_t objective[16]; + int deltaTime; + // playerState_s::hud; + }; + + struct __declspec(align(4)) pmove_t + { + playerState_s *ps; + usercmd_s cmd; + usercmd_s oldcmd; + int tracemask; + int numtouch; + int touchents[32]; + float mins[3]; + float maxs[3]; + float xyspeed; + int proneChange; + float maxSprintTimeMultiplier; + bool mantleStarted; + float mantleEndPos[3]; + int mantleDuration; + unsigned __int8 handler; + }; + + struct DvarValueStringBuf + { + const char *pad; + char string[12]; + }; + + union DvarValue + { + bool enabled; + int integer; + unsigned int unsignedInt; + float value; + float vector[4]; + const char *string; + DvarValueStringBuf stringBuf; + unsigned __int8 color[4]; + }; + + union DvarLimits + { + struct + { + int stringCount; + const char **strings; + } enumeration; + + struct + { + int min; + int max; + } integer; + + struct + { + float min; + float max; + } value; + + struct + { + float min; + float max; + } vector; + }; + + struct dvar_s + { + const char *name; + const char *description; + unsigned __int16 flags; + unsigned __int8 type; + bool modified; + DvarValue current; + DvarValue latched; + DvarValue reset; + DvarLimits domain; + dvar_s *next; + dvar_s *hashNext; + }; + + struct ScreenPlacement + { + float scaleVirtualToReal[2]; + float scaleVirtualToFull[2]; + float scaleRealToVirtual[2]; + float virtualScreenOffsetX; + float virtualViewableMin[2]; + float virtualViewableMax[2]; + float realViewportSize[2]; + float realViewableMin[2]; + float realViewableMax[2]; + float subScreenLeft; + }; + + struct Material; + struct Font_s; + + struct cgMedia_t + { + Material *whiteMaterial; + Material *teamStatusBar; + Material *balloonMaterial; + Material *connectionMaterial; + Material *youInKillCamMaterial; + Material *tracerMaterial; + Material *laserMaterial; + Material *laserLightMaterial; + Material *lagometerMaterial; + Material *hintMaterials[133]; + Material *stanceMaterials[4]; + Material *objectiveMaterials[1]; + Material *friendMaterials[2]; + Material *damageMaterial; + Material *mantleHint; + Font_s *smallDevFont; + Font_s *bigDevFont; + // snd_alias_list_t *landDmgSound; + // snd_alias_list_t *grenadeExplodeSound[29]; + // snd_alias_list_t *rocketExplodeSound[29]; + // snd_alias_list_t *bulletHitSmallSound[29]; + // snd_alias_list_t *bulletHitLargeSound[29]; + // snd_alias_list_t *bulletHitAPSound[29]; + // snd_alias_list_t *shotgunHitSound[29]; + // snd_alias_list_t *stepRunSound[29]; + // snd_alias_list_t *stepRunSoundPlayer[29]; + // snd_alias_list_t *stepWalkSound[29]; + // snd_alias_list_t *stepWalkSoundPlayer[29]; + // snd_alias_list_t *stepProneSound[29]; + // snd_alias_list_t *stepProneSoundPlayer[29]; + // snd_alias_list_t *landSound[29]; + // snd_alias_list_t *landSoundPlayer[29]; + // snd_alias_list_t *runningEquipmentSound; + // snd_alias_list_t *runningEquipmentSoundPlayer; + // snd_alias_list_t *walkingEquipmentSound; + // snd_alias_list_t *walkingEquipmentSoundPlayer; + // snd_alias_list_t *foliageMovement; + // snd_alias_list_t *bulletWhizby; + // snd_alias_list_t *meleeSwingLarge; + // snd_alias_list_t *meleeSwingSmall; + // snd_alias_list_t *meleeHit; + // snd_alias_list_t *meleeHitOther; + // snd_alias_list_t *nightVisionOn; + // snd_alias_list_t *nightVisionOff; + // snd_alias_list_t *playerSprintGasp; + // snd_alias_list_t *playerHeartBeatSound; + // snd_alias_list_t *playerBreathInSound; + // snd_alias_list_t *playerBreathOutSound; + // snd_alias_list_t *playerBreathGaspSound; + // snd_alias_list_t *playerSwapOffhand; + // snd_alias_list_t *physCollisionSound[50][29]; + // Material *compassping_friendlyfiring; + // Material *compassping_friendlyyelling; + // Material *compassping_enemy; + // Material *compassping_enemyfiring; + // Material *compassping_enemyyelling; + // Material *compassping_grenade; + // Material *compassping_explosion; + // Material *compass_radarline; + // Material *compass_helicopter_friendly; + // Material *compass_helicopter_enemy; + // Material *compass_plane_friendly; + // Material *compass_plane_enemy; + // Material *grenadeIconFrag; + // Material *grenadeIconFlash; + // Material *grenadeIconThrowBack; + // Material *grenadePointer; + // FxImpactTable *fx; + // const FxEffectDef *fxNoBloodFleshHit; + // const FxEffectDef *heliDustEffect; + // const FxEffectDef *heliWaterEffect; + // const FxEffectDef *helicopterLightSmoke; + // const FxEffectDef *helicopterHeavySmoke; + // const FxEffectDef *helicopterOnFire; + // const FxEffectDef *jetAfterburner; + // const FxEffectDef *fxVehicleTireDust; + // XModel *nightVisionGoggles; + // Material *nightVisionOverlay; + // Material *hudIconNVG; + // Material *hudDpadArrow; + // Material *ammoCounterBullet; + // Material *ammoCounterBeltBullet; + // Material *ammoCounterRifleBullet; + // Material *ammoCounterRocket; + // Material *ammoCounterShotgunShell; + }; + + enum TraceHitType : __int32 + { + TRACE_HITTYPE_NONE = 0x0, + TRACE_HITTYPE_ENTITY = 0x1, + TRACE_HITTYPE_DYNENT_MODEL = 0x2, + TRACE_HITTYPE_DYNENT_BRUSH = 0x3, + }; + + struct __declspec(align(2)) trace_t + { + float fraction; + float normal[3]; + int surfaceFlags; + int contents; + const char *material; + TraceHitType hitType; + unsigned __int16 hitId; + unsigned __int16 modelIndex; + unsigned __int16 partName; + unsigned __int16 partGroup; + bool allsolid; + bool startsolid; + bool walkable; + }; + + struct pml_t + { + float forward[3]; + float right[3]; + float up[3]; + float frametime; + int msec; + int walking; + int groundPlane; + int almostGroundPlane; + trace_t groundTrace; + float impactSpeed; + float previous_origin[3]; + float previous_velocity[3]; + }; + +} \ No newline at end of file