diff --git a/Build/PCTomb5.idb b/Build/PCTomb5.idb index a7664ab77..906e0ce68 100644 Binary files a/Build/PCTomb5.idb and b/Build/PCTomb5.idb differ diff --git a/TR5Main/Game/control.cpp b/TR5Main/Game/control.cpp index f6b62a0f3..f60e1cb6a 100644 --- a/TR5Main/Game/control.cpp +++ b/TR5Main/Game/control.cpp @@ -224,14 +224,6 @@ GAME_STATUS __cdecl ControlPhase(__int32 numFrames, __int32 demoMode) return GAME_STATUS::GAME_STATUS_NONE; } -void __cdecl j_AnimateItem(ITEM_INFO* item) -{ - if (item != LaraItem) - printf("AnimateItem\n"); - AnimateItem(item); - -} - unsigned __stdcall GameMain(void*) { DB_Log(2, "GameMain - DLL"); @@ -875,5 +867,5 @@ void __cdecl TestTriggers(__int16* data, __int32 heavy, __int32 HeavyFlags) void Inject_Control() { - INJECT(0x0040261C, j_AnimateItem); + } \ No newline at end of file diff --git a/TR5Main/Game/control.h b/TR5Main/Game/control.h index e422c463a..0167b4703 100644 --- a/TR5Main/Game/control.h +++ b/TR5Main/Game/control.h @@ -2,24 +2,12 @@ #include "..\Global\global.h" -typedef enum GAME_STATUS { - GAME_STATUS_NONE, - GAME_STATUS_NEW_GAME, - GAME_STATUS_LOAD_GAME, - GAME_STATUS_SAVE_GAME, - GAME_STATUS_EXIT_TO_TITLE, - GAME_STATUS_EXIT_GAME, - GAME_STATUS_LARA_DEAD, - GAME_STATUS_LEVEL_COMPLETED -}; - #define GetFloor ((FLOOR_INFO* (__cdecl*)(int, int, int, short*)) 0x00415B20) #define GetCeiling ((int (__cdecl*)(FLOOR_INFO*, int, int, int)) 0x00417640) #define GetFloorHeight ((int (__cdecl*)(FLOOR_INFO*, int, int, int)) 0x00415FB0) #define GetRandomControl ((int (__cdecl*)()) 0x004A7C10) #define AnimateItem ((void (__cdecl*)(ITEM_INFO*)) 0x00415300) #define GetWaterHeight ((__int32 (__cdecl*)(__int32, __int32, __int32, __int16)) 0x00415DA0) -//#define TestTriggers ((void (__cdecl*)(__int16*, __int32, __int32)) 0x00416760) #define TriggerActive ((__int32 (__cdecl*)(ITEM_INFO*)) 0x004175B0) #define GetChange ((__int32 (__cdecl*)(ITEM_INFO*, ANIM_STRUCT*)) 0x00415890) #define KillMoveItems ((void (__cdecl*)()) 0x00414620) @@ -55,7 +43,6 @@ GAME_STATUS __cdecl DoLevel(__int32 index, __int32 ambient, bool loadFromSavegam GAME_STATUS __cdecl ControlPhase(__int32 numFrames, __int32 demoMode); unsigned __stdcall GameMain(void*); -void __cdecl j_AnimateItem(ITEM_INFO* item); void __cdecl TestTriggers(__int16* data, __int32 heavy, __int32 HeavyFlags); void Inject_Control(); \ No newline at end of file diff --git a/TR5Main/Game/savegame.cpp b/TR5Main/Game/savegame.cpp index e69de29bb..333bd6c4b 100644 --- a/TR5Main/Game/savegame.cpp +++ b/TR5Main/Game/savegame.cpp @@ -0,0 +1,117 @@ +#include "savegame.h" +#include "..\Global\global.h" + +FileStream* SaveGame::m_stream; +ChunkReader* SaveGame::m_reader; +ChunkWriter* SaveGame::m_writer; + +ChunkId* SaveGame::m_chunkHeader; +ChunkId* SaveGame::m_chunkGameStatus; +ChunkId* SaveGame::m_chunkItems; +ChunkId* SaveGame::m_chunkItem; +ChunkId* SaveGame::m_chunkLara; +ChunkId* SaveGame::m_chunkLuaVariables; +ChunkId* SaveGame::m_chunkLuaLocal; +ChunkId* SaveGame::m_chunkLuaGlobal; + +void SaveGame::SaveHeader(__int32 param) +{ + m_stream->WriteString(g_GameFlow->GetString(g_GameFlow->GetLevel(CurrentLevel)->Name)); + LEB128::Write(m_stream, (Savegame.Game.Timer / 30) / 86400); + LEB128::Write(m_stream, ((Savegame.Game.Timer / 30) % 86400) / 3600); + LEB128::Write(m_stream, ((Savegame.Game.Timer / 30) / 60) % 60); + LEB128::Write(m_stream, (Savegame.Game.Timer / 30) % 60); + LEB128::Write(m_stream, CurrentLevel); +} + +void SaveGame::SaveGameStatus(__int32 param) +{ + +} + +void SaveGame::SaveItems(__int32 param) +{ + for (__int32 i = 0; i < NumItems; i++) + { + m_writer->WriteChunk(m_chunkItem, &SaveItem, i); + } +} + +void SaveGame::SaveItem(__int32 itemNumber) +{ + ITEM_INFO* item = &Items[itemNumber]; + OBJECT_INFO* obj = &Objects[item->objectNumber]; + + LEB128::Write(m_stream, item->flags); + LEB128::Write(m_stream, item->pos.xPos); + LEB128::Write(m_stream, item->pos.yPos); + LEB128::Write(m_stream, item->pos.zPos); + LEB128::Write(m_stream, item->pos.xRot); + LEB128::Write(m_stream, item->pos.yRot); + LEB128::Write(m_stream, item->pos.zRot); + LEB128::Write(m_stream, item->roomNumber); + LEB128::Write(m_stream, item->itemFlags[0]); + LEB128::Write(m_stream, item->itemFlags[1]); + LEB128::Write(m_stream, item->itemFlags[2]); + LEB128::Write(m_stream, item->itemFlags[3]); + LEB128::Write(m_stream, item->triggerFlags); + LEB128::Write(m_stream, item->timer); + LEB128::Write(m_stream, item->speed); + LEB128::Write(m_stream, item->fallspeed); + LEB128::Write(m_stream, item->currentAnimState); + LEB128::Write(m_stream, item->goalAnimState); + LEB128::Write(m_stream, item->requiredAnimState); + LEB128::Write(m_stream, item->animNumber); + LEB128::Write(m_stream, item->frameNumber); + LEB128::Write(m_stream, item->objectNumber); + LEB128::Write(m_stream, item->hitPoints); +} + +void SaveGame::SaveLara(__int32 param) +{ + LARA_INFO lara; + memcpy(&lara, &Lara, sizeof(Lara)); + + for (__int32 i = 0; i < 15; i++) + lara.meshPtrs[i] = (__int16*)(lara.meshPtrs[i] - MeshBase); + + lara.leftArm.frameBase = (__int16*)(lara.leftArm.frameBase - Objects[ID_PISTOLS_ANIM].frameBase); + lara.rightArm.frameBase = (__int16*)(lara.rightArm.frameBase - Objects[ID_PISTOLS_ANIM].frameBase); + + m_stream->Write(reinterpret_cast(&lara), sizeof(Lara)); +} + +bool SaveGame::Save(char* fileName) +{ + m_chunkHeader = ChunkId::FromString("TR5MSgHeader"); + m_chunkGameStatus = ChunkId::FromString("TR5MSgGameStatus"); + m_chunkItems = ChunkId::FromString("TR5MSgItems"); + m_chunkItem = ChunkId::FromString("TR5MSgItem"); + m_chunkLara = ChunkId::FromString("TR5MSgLara"); + m_chunkLuaVariables = ChunkId::FromString("TR5MSgLuaVars"); + m_chunkLuaLocal = ChunkId::FromString("TR5MSgLuaL"); + m_chunkLuaGlobal = ChunkId::FromString("TR5MSgLuaG"); + + m_stream = new FileStream(fileName); + m_writer = new ChunkWriter(0x4D355254, m_stream); + + m_writer->WriteChunk(m_chunkHeader, &SaveHeader, 0); + m_writer->WriteChunk(m_chunkLara, &SaveLara, 0); + m_writer->WriteChunkWithChildren(m_chunkItems, &SaveItems, 0); + + m_stream->Close(); + + delete m_writer; + delete m_stream; + + delete m_chunkHeader; + delete m_chunkGameStatus; + delete m_chunkItems; + delete m_chunkItem; + delete m_chunkLara; + delete m_chunkLuaVariables; + delete m_chunkLuaLocal; + delete m_chunkLuaGlobal; + + return true; +} \ No newline at end of file diff --git a/TR5Main/Game/savegame.h b/TR5Main/Game/savegame.h index 09786bca5..cca13e000 100644 --- a/TR5Main/Game/savegame.h +++ b/TR5Main/Game/savegame.h @@ -1,8 +1,45 @@ #pragma once +#include "..\Global\global.h" +#include "..\Specific\IO\ChunkId.h" +#include "..\Specific\IO\ChunkReader.h" +#include "..\Specific\IO\ChunkWriter.h" +#include "..\Specific\IO\LEB128.h" +#include "..\Specific\IO\Streams.h" +#include "..\Scripting\GameFlowScript.h" +#include "..\Scripting\GameLogicScript.h" + #define RestoreGame ((__int32 (__cdecl*)()) 0x00472060) #define ReadSavegame ((__int32 (__cdecl*)(__int32)) 0x004A8E10) #define CreateSavegame ((void (__cdecl*)()) 0x00470FA0) #define WriteSavegame ((__int32 (__cdecl*)(__int32)) 0x004A8BC0) +#define SAVEGAME_BUFFER_SIZE 1048576 +extern GameFlow* g_GameFlow; +extern GameScript* g_GameScript; + +class SaveGame { +private: + static FileStream* m_stream; + static ChunkReader* m_reader; + static ChunkWriter* m_writer; + + static ChunkId* m_chunkHeader; + static ChunkId* m_chunkGameStatus; + static ChunkId* m_chunkItems; + static ChunkId* m_chunkItem; + static ChunkId* m_chunkLara; + static ChunkId* m_chunkLuaVariables; + static ChunkId* m_chunkLuaLocal; + static ChunkId* m_chunkLuaGlobal; + + static void SaveHeader(__int32 param); + static void SaveGameStatus(__int32 param); + static void SaveLara(__int32 param); + static void SaveItem(__int32 param); + static void SaveItems(__int32 param); + +public: + static bool Save(char* fileName); +}; \ No newline at end of file diff --git a/TR5Main/Global/constants.h b/TR5Main/Global/constants.h index 2ac9b8f6b..ce549e2f8 100644 --- a/TR5Main/Global/constants.h +++ b/TR5Main/Global/constants.h @@ -17,7 +17,7 @@ #define BOX_BLOCKED (1 << 14) // unpassable for other enemies, always set for movable blocks & closed doors #define BOX_LAST (1 << 15) // unpassable by large enemies (T-Rex, Centaur, etc), always set behind doors -#define NUM_OBJECTS 488 +#define NUM_OBJECTS 518 #define UNIT_SHADOW 256 #define DEFAULT_RADIUS 10 diff --git a/TR5Main/Global/enums.h b/TR5Main/Global/enums.h index d4f5bd7f7..f01e45ad6 100644 --- a/TR5Main/Global/enums.h +++ b/TR5Main/Global/enums.h @@ -896,4 +896,15 @@ enum WEATHER_TYPES WEATHER_NORMAL = 0, WEATHER_RAIN = 1, WEATHER_SNOW = 2 +}; + +enum GAME_STATUS { + GAME_STATUS_NONE, + GAME_STATUS_NEW_GAME, + GAME_STATUS_LOAD_GAME, + GAME_STATUS_SAVE_GAME, + GAME_STATUS_EXIT_TO_TITLE, + GAME_STATUS_EXIT_GAME, + GAME_STATUS_LARA_DEAD, + GAME_STATUS_LEVEL_COMPLETED }; \ No newline at end of file diff --git a/TR5Main/Global/math.h b/TR5Main/Global/math.h index a934c4963..5d771c096 100644 --- a/TR5Main/Global/math.h +++ b/TR5Main/Global/math.h @@ -11,6 +11,7 @@ #define ONE_DEGREE 182 #define ANGLE(x) ((x) * 65536.0 / 360.0) +#define TR_ANGLE_TO_DEGREES(x) ((x) / 65536.0 * 360.0) #define TR_ANGLE_TO_RAD(x) ((x) / 65536.0 * 360.0 * RADIAN) #define SQRT_ASM ((int (__cdecl*)(int)) 0x0048F980) diff --git a/TR5Main/Global/objectslist.h b/TR5Main/Global/objectslist.h index e8162bba2..9d6c09474 100644 --- a/TR5Main/Global/objectslist.h +++ b/TR5Main/Global/objectslist.h @@ -489,5 +489,35 @@ typedef enum object_types { ID_NEW_SLOT_485, ID_NEW_SLOT_486, ID_NEW_SLOT_487, - ID_NEW_SLOT_488 + ID_NEW_SLOT_488, + ID_NEW_SLOT_489, + ID_NEW_SLOT_490, + ID_NEW_SLOT_491, + ID_NEW_SLOT_492, + ID_NEW_SLOT_493, + ID_NEW_SLOT_494, + ID_NEW_SLOT_495, + ID_NEW_SLOT_496, + ID_NEW_SLOT_497, + ID_NEW_SLOT_498, + ID_NEW_SLOT_499, + ID_NEW_SLOT_500, + ID_NEW_SLOT_501, + ID_NEW_SLOT_502, + ID_NEW_SLOT_503, + ID_NEW_SLOT_504, + ID_NEW_SLOT_505, + ID_NEW_SLOT_506, + ID_NEW_SLOT_507, + ID_NEW_SLOT_508, + ID_NEW_SLOT_509, + ID_NEW_SLOT_510, + ID_NEW_SLOT_511, + ID_NEW_SLOT_512, + ID_NEW_SLOT_513, + ID_NEW_SLOT_514, + ID_NEW_SLOT_515, + ID_NEW_SLOT_516, + ID_NEW_SLOT_517, + ID_NEW_SLOT_518 } GAME_OBJECT_ID; diff --git a/TR5Main/Global/vars.h b/TR5Main/Global/vars.h index 12b287737..5cfe066b5 100644 --- a/TR5Main/Global/vars.h +++ b/TR5Main/Global/vars.h @@ -52,11 +52,14 @@ #define FloorData VAR_U_(0x00875168, __int16*) #define ObjectTextures VAR_U_(0x008751B0, OBJECT_TEXTURE*) #define RoomLightsCount VAR_U_(0x0087B0EC, __int32) -#define LevelDataPtr VAR_U_(0x00874964, char*) +//#define LevelDataPtr VAR_U_(0x00874964, char*) #define NumberRooms VAR_U_(0x0087514C, __int16) #define nAnimUVRanges VAR_U_(0x0087495C, __int32) #define LevelFilePtr VAR_U_(0x00875164, FILE*) //#define StaticObjects ARRAY_(0x00874988, STATIC_INFO, [70]) +#define NumberCameras VAR_U_(0x00EEFAC0, __int32) +#define Cameras VAR_U_(0x00EEF9A2, OBJECT_VECTOR*) +#define NumberSpotcams VAR_U_(0x00E4F428, __int32) // Lara #define LaraItem VAR_U_(0x00E5BF08, ITEM_INFO*) @@ -122,8 +125,8 @@ #define AllStringsOffsets VAR_U_(0x00E5C2B8, __int16*) #define RenderLoadBar VAR_U_(0x008FBDC0, __int32) -#define IsLevelLoading VAR_U_(0x00874968, __int32) -#define ThreadId VAR_U_(0x00874978, unsigned int) +//#define IsLevelLoading VAR_U_(0x00874968, __int32) +//#define ThreadId VAR_U_(0x00874978, unsigned int) #define WeatherType VAR_U_(0x00EEF4A0, byte) #define CreditsDone VAR_U_(0x00E6D838, byte) #define CanLoad VAR_U_(0x0051CE54, byte) diff --git a/TR5Main/Scripting/GameLogicScript.cpp b/TR5Main/Scripting/GameLogicScript.cpp index cf8fc4ff2..4b3532140 100644 --- a/TR5Main/Scripting/GameLogicScript.cpp +++ b/TR5Main/Scripting/GameLogicScript.cpp @@ -10,14 +10,37 @@ GameScript::GameScript(sol::state* lua) { m_lua = lua; + // Add constants (*m_lua)["HIT_POINTS"] = ITEM_PARAM_HIT_POINTS; (*m_lua)["CURRENT_ANIM_STATE"] = ITEM_PARAM_CURRENT_ANIM_STATE; (*m_lua)["ANIM_NUMBER"] = ITEM_PARAM_ANIM_NUMBER; - + (*m_lua)["GOAL_ANIM_STATE"] = ITEM_PARAM_GOAL_ANIM_STATE; + (*m_lua)["REQUIRED_ANIM_STATE"] = ITEM_PARAM_REQUIRED_ANIM_STATE; + (*m_lua)["FRAME_NUMBER"] = ITEM_PARAM_FRAME_NUMBER; + (*m_lua)["HIT_POINTS"] = ITEM_PARAM_HIT_POINTS; + (*m_lua)["HIT_STATUS"] = ITEM_PARAM_HIT_STATUS; + (*m_lua)["GRAVITY_STATUS"] = ITEM_PARAM_GRAVITY_STATUS; + (*m_lua)["COLLIDABLE"] = ITEM_PARAM_COLLIDABLE; + (*m_lua)["POISONED"] = ITEM_PARAM_POISONED; + (*m_lua)["ROOM_NUMBER"] = ITEM_PARAM_ROOM_NUMBER; + + // Add the item type + m_lua->new_usertype("ItemPosition", + "x", &GameScriptItemPosition::x, + "y", &GameScriptItemPosition::y, + "z", &GameScriptItemPosition::z, + "xRot", &GameScriptItemPosition::xRot, + "yRot", &GameScriptItemPosition::yRot, + "zRot", &GameScriptItemPosition::zRot, + "room", &GameScriptItemPosition::room + ); + m_lua->new_usertype("Item", - sol::constructors(), "Get", &GameScriptItem::Get, - "Set", &GameScriptItem::Set + "Set", &GameScriptItem::Set, + "GetPosition", &GameScriptItem::GetItemPosition, + "SetPosition", &GameScriptItem::SetItemPosition, + "SetRotation", &GameScriptItem::SetItemRotation ); // GameScript type @@ -31,22 +54,16 @@ GameScript::GameScript(sol::state* lua) "SetSecretsCount", &GameScript::SetSecretsCount, "AddOneSecret", &GameScript::AddOneSecret, "JumpToLevel", &GameScript::JumpToLevel, - "GetItem", &GameScript::GetItem + "GetItem", &GameScript::GetItem, + "PlaySoundEffect", &GameScript::PlaySoundEffect, + "PlaySoundEffectAtPosition", &GameScript::PlaySoundEffectAtPosition ); + // Add global variables and namespaces (*m_lua)["TR"] = this; - // DEBUG: just for testing - LuaFunction* function = new LuaFunction(); - function->Name = "Trigger_0"; - function->Code = "function Trigger_0() \n TR:EnableItem(2); \n TR:PlayAudioTrack(15); \n item = TR:GetItem(2); \n item:Set(HIT_POINTS, -16384); \n return true; \n end"; - m_lua->script(function->Code); - Triggers.push_back(function); - - m_itemsMap.insert(pair<__int16, __int16>(0, 0)); - m_itemsMap.insert(pair<__int16, __int16>(9, 9)); - m_itemsMap.insert(pair<__int16, __int16>(10, 10)); - m_itemsMap.insert(pair<__int16, __int16>(2, 8)); + m_locals = (*m_lua).create_table("Locals"); + m_globals = (*m_lua).create_table("Globals"); } GameScript::~GameScript() @@ -56,22 +73,32 @@ GameScript::~GameScript() void GameScript::AddTrigger(LuaFunction* function) { - Triggers.push_back(function); + m_triggers.push_back(function); (*m_lua).script(function->Code); } +void GameScript::AddLuaId(int luaId, int itemId) +{ + m_itemsMap.insert(pair<__int32, __int32>(luaId, itemId)); +} + void GameScript::FreeLevelScripts() { // Delete all triggers - for (__int32 i = 0; i < Triggers.size(); i++) + for (__int32 i = 0; i < m_triggers.size(); i++) { - LuaFunction* trigger = Triggers[i]; + LuaFunction* trigger = m_triggers[i]; char* name = (char*)trigger->Name.c_str(); (*m_lua)[name] = NULL; - delete Triggers[i]; + delete m_triggers[i]; } + m_triggers.clear(); - Triggers.clear(); + // Clear the items mapping + m_itemsMap.clear(); + + (*m_lua)["Lara"] = NULL; + //delete m_Lara; } string GameScript::loadScriptFromFile(char* luaFilename) @@ -98,10 +125,10 @@ bool GameScript::ExecuteScript(char* luaFilename) bool GameScript::ExecuteTrigger(__int16 index) { // Is this a valid trigger? - if (index >= Triggers.size()) + if (index >= m_triggers.size()) return true; - LuaFunction* trigger = Triggers[index]; + LuaFunction* trigger = m_triggers[index]; // We want to execute a trigger just one time // TODO: implement in the future continoous trigger? @@ -115,7 +142,16 @@ bool GameScript::ExecuteTrigger(__int16 index) bool result = (*m_lua)[name](); // Trigger was executed, don't execute it anymore - Triggers[index]->Executed = result; + trigger->Executed = result; + + m_locals.for_each([&](sol::object const& key, sol::object const& value) { + if (value.is()) + std::cout << key.as() << " " << value.as() << std::endl; + else if (value.is()) + std::cout << key.as() << " " << value.as() << std::endl; + else + std::cout << key.as() << " " << value.as() << std::endl; + }); return result; } @@ -259,13 +295,52 @@ void GameScript::MakeItemInvisible(__int16 id) GameScriptItem GameScript::GetItem(__int16 id) { if (m_itemsMap.find(id) == m_itemsMap.end()) - return NULL; + throw "Item not found"; __int16 itemNum = m_itemsMap[id]; - ITEM_INFO* item = &Items[itemNum]; - GameScriptItem scriptItem = GameScriptItem(item); + return m_items[itemNum]; +} - return scriptItem; +void GameScript::PlaySoundEffectAtPosition(__int16 id, __int32 x, __int32 y, __int32 z, __int32 flags) +{ + PHD_3DPOS pos; + + pos.xPos = x; + pos.yPos = y; + pos.zPos = z; + pos.xRot = 0; + pos.yRot = 0; + pos.zRot = 0; + + SoundEffect(id, &pos, flags); +} + +void GameScript::PlaySoundEffect(__int16 id, __int32 flags) +{ + SoundEffect(id, NULL, flags); +} + +void GameScript::AssignVariables() +{ + for (__int32 i = 0; i < NUM_ITEMS; i++) + m_items[i].NativeItem = NULL; + + for (__int32 i = 0; i < NumItems; i++) + m_items[i].NativeItem = &Items[i]; + + (*m_lua)["Lara"] = m_items[Lara.itemNumber]; +} + +void GameScript::ResetVariables() +{ + (*m_lua)["Lara"] = NULL; +} + +void GameScript::SetItem(__int32 index, ITEM_INFO* item) +{ + if (index >= NUM_ITEMS) + return; + m_items[index].NativeItem = item; } GameScript* g_GameScript; \ No newline at end of file diff --git a/TR5Main/Scripting/GameLogicScript.h b/TR5Main/Scripting/GameLogicScript.h index 6696bf1a3..63ae0d1f1 100644 --- a/TR5Main/Scripting/GameLogicScript.h +++ b/TR5Main/Scripting/GameLogicScript.h @@ -20,6 +20,7 @@ using namespace std; #define ITEM_PARAM_GRAVITY_STATUS 7 #define ITEM_PARAM_COLLIDABLE 8 #define ITEM_PARAM_POISONED 9 +#define ITEM_PARAM_ROOM_NUMBER 10 typedef struct LuaFunction { string Name; @@ -27,40 +28,48 @@ typedef struct LuaFunction { bool Executed; }; -typedef struct GameScriptItem { -private: - ITEM_INFO* m_item; +typedef struct GameScriptItemPosition { + __int32 x; + __int32 y; + __int32 z; + __int16 xRot; + __int16 yRot; + __int16 zRot; + __int16 room; +}; -public: - GameScriptItem(ITEM_INFO* item) - { - m_item = item; - } +typedef struct GameScriptItem { + ITEM_INFO* NativeItem; __int16 Get(__int32 param) { + if (NativeItem == NULL) + return 0; + switch (param) { case ITEM_PARAM_CURRENT_ANIM_STATE: - return m_item->currentAnimState; + return NativeItem->currentAnimState; case ITEM_PARAM_REQUIRED_ANIM_STATE: - return m_item->requiredAnimState; + return NativeItem->requiredAnimState; case ITEM_PARAM_GOAL_ANIM_STATE: - return m_item->goalAnimState; + return NativeItem->goalAnimState; case ITEM_PARAM_ANIM_NUMBER: - return m_item->animNumber; + return NativeItem->animNumber; case ITEM_PARAM_FRAME_NUMBER: - return m_item->frameNumber; + return NativeItem->frameNumber; case ITEM_PARAM_HIT_POINTS: - return m_item->hitPoints; + return NativeItem->hitPoints; case ITEM_PARAM_HIT_STATUS: - return m_item->hitStatus; + return NativeItem->hitStatus; case ITEM_PARAM_GRAVITY_STATUS: - return m_item->gravityStatus; + return NativeItem->gravityStatus; case ITEM_PARAM_COLLIDABLE: - return m_item->collidable; + return NativeItem->collidable; case ITEM_PARAM_POISONED: - return m_item->poisoned; + return NativeItem->poisoned; + case ITEM_PARAM_ROOM_NUMBER: + return NativeItem->roomNumber; default: return 0; } @@ -68,53 +77,102 @@ public: void Set(__int32 param, __int16 value) { + if (NativeItem == NULL) + return; + switch (param) { case ITEM_PARAM_CURRENT_ANIM_STATE: - m_item->currentAnimState = value; break; + NativeItem->currentAnimState = value; break; case ITEM_PARAM_REQUIRED_ANIM_STATE: - m_item->requiredAnimState = value; break; + NativeItem->requiredAnimState = value; break; case ITEM_PARAM_GOAL_ANIM_STATE: - m_item->goalAnimState = value; break; + NativeItem->goalAnimState = value; break; case ITEM_PARAM_ANIM_NUMBER: - m_item->animNumber = value; - m_item->frameNumber = Anims[m_item->animNumber].frameBase; + NativeItem->animNumber = value; + NativeItem->frameNumber = Anims[NativeItem->animNumber].frameBase; break; case ITEM_PARAM_FRAME_NUMBER: - m_item->frameNumber = value; break; + NativeItem->frameNumber = value; break; case ITEM_PARAM_HIT_POINTS: - m_item->hitPoints = value; break; + NativeItem->hitPoints = value; break; case ITEM_PARAM_HIT_STATUS: - m_item->hitStatus = value; break; + NativeItem->hitStatus = value; break; case ITEM_PARAM_GRAVITY_STATUS: - m_item->gravityStatus = value; break; + NativeItem->gravityStatus = value; break; case ITEM_PARAM_COLLIDABLE: - m_item->collidable = value; break; + NativeItem->collidable = value; break; case ITEM_PARAM_POISONED: - m_item->poisoned = value; break; + NativeItem->poisoned = value; break; + case ITEM_PARAM_ROOM_NUMBER: + NativeItem->roomNumber = value; break; default: break; } } + + GameScriptItemPosition GetItemPosition() + { + GameScriptItemPosition pos; + + //if (m_item == NULL) + // return pos; + + pos.x = NativeItem->pos.xPos; + pos.y = NativeItem->pos.yPos; + pos.z = NativeItem->pos.zPos; + pos.xRot = TR_ANGLE_TO_DEGREES(NativeItem->pos.xRot); + pos.yRot = TR_ANGLE_TO_DEGREES(NativeItem->pos.yRot); + pos.zRot = TR_ANGLE_TO_DEGREES(NativeItem->pos.zRot); + pos.room = NativeItem->roomNumber; + + return pos; + } + + void SetItemPosition(__int32 x, __int32 y, __int32 z) + { + if (NativeItem == NULL) + return; + + NativeItem->pos.xPos = x; + NativeItem->pos.yPos = y; + NativeItem->pos.zPos = z; + } + + void SetItemRotation(__int32 x, __int32 y, __int32 z) + { + if (NativeItem == NULL) + return; + + NativeItem->pos.xRot = ANGLE(x); + NativeItem->pos.yRot = ANGLE(y); + NativeItem->pos.zRot = ANGLE(z); + } }; class GameScript { private: sol::state* m_lua; + sol::table m_globals; + sol::table m_locals; + map<__int32, __int32> m_itemsMap; + vector m_triggers; + GameScriptItem m_items[NUM_ITEMS]; string loadScriptFromFile(char* luaFilename); - map<__int16, __int16> m_itemsMap; - -public: - vector Triggers; +public: GameScript(sol::state* lua); ~GameScript(); bool ExecuteScript(char* luaFilename); void FreeLevelScripts(); void AddTrigger(LuaFunction* function); + void AddLuaId(int luaId, int itemId); + void SetItem(__int32 index, ITEM_INFO* item); + void AssignVariables(); + void ResetVariables(); void EnableItem(__int16 id); void DisableItem(__int16 id); @@ -127,4 +185,6 @@ public: void AddOneSecret(); void MakeItemInvisible(__int16 id); GameScriptItem GetItem(__int16 id); + void PlaySoundEffectAtPosition(__int16 id, __int32 x, __int32 y, __int32 z, __int32 flags); + void PlaySoundEffect(__int16 id, __int32 flags); }; \ No newline at end of file diff --git a/TR5Main/Specific/IO/ChunkId.h b/TR5Main/Specific/IO/ChunkId.h new file mode 100644 index 000000000..4d283dceb --- /dev/null +++ b/TR5Main/Specific/IO/ChunkId.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include + +#include "LEB128.h" +#include "Streams.h" + +using namespace std; + +typedef struct ChunkId +{ +private: + byte* m_chunkBytes; + __int32 m_length; + +public: + ChunkId(char* bytes, __int32 length) + { + if (length == 0) + { + m_chunkBytes = NULL; + m_length = 0; + } + else + { + m_chunkBytes = (byte*)malloc(length); + memcpy(m_chunkBytes, bytes, length); + m_length = length; + } + } + + ~ChunkId() + { + if (m_chunkBytes != NULL) + delete m_chunkBytes; + } + + static ChunkId* FromString(char* str) + { + return new ChunkId(str, strlen(str)); + } + + static ChunkId* FromString(string* str) + { + return new ChunkId((char*)str->c_str(), str->length()); + } + + static ChunkId* FromStream(BaseStream* stream) + { + __int32 idLength = LEB128::ReadInt32(stream); + char* buffer = (char*)malloc(idLength); + stream->Read(buffer, idLength); + ChunkId* chunk = new ChunkId(buffer, idLength); + free(buffer); + return chunk; + } + + void ToStream(BaseStream* stream) + { + LEB128::Write(stream, m_length); + stream->WriteBytes(m_chunkBytes, m_length); + } + + byte* GetBytes() + { + return m_chunkBytes; + } + + __int32 GetLength() + { + return m_length; + } + + bool EqualsTo(ChunkId* other) + { + if (m_length != other->GetLength()) + return false; + return (strncmp((const char*)m_chunkBytes, (const char*)other->GetBytes(), m_length) == 0); + } +}; + diff --git a/TR5Main/Specific/IO/ChunkReader.h b/TR5Main/Specific/IO/ChunkReader.h new file mode 100644 index 000000000..995e1589c --- /dev/null +++ b/TR5Main/Specific/IO/ChunkReader.h @@ -0,0 +1,144 @@ +#pragma once + +#include +#include + +#include "ChunkId.h" +#include "LEB128.h" +#include "Streams.h" + +class ChunkReader +{ +private: + bool m_isValid; + ChunkId* m_emptyChunk; + BaseStream* m_stream; + + __int32 readInt32() + { + __int32 value = 0; + m_stream->Read(reinterpret_cast(&value), 4); + return value; + } + + __int16 readInt16() + { + __int16 value = 0; + m_stream->Read(reinterpret_cast(&value), 2); + return value; + } + +public: + ChunkReader(__int32 expectedMagicNumber, BaseStream* stream) + { + m_isValid = false; + + if (stream == NULL) + return; + + m_stream = stream; + + // Check the magic number + __int32 magicNumber = readInt32(); + if (magicNumber != expectedMagicNumber) + return; + + // TODO: future use for compression + m_stream->Seek(4, SEEK_ORIGIN::CURRENT); + + m_emptyChunk = new ChunkId(NULL, 0); + + m_isValid = true; + } + + ~ChunkReader() + { + delete m_emptyChunk; + } + + bool ChunkReader::IsValid() + { + return m_isValid; + } + + bool ChunkReader::ReadChunks(bool(*func)(ChunkId* parentChunkId, __int32 maxSize)) + { + do + { + ChunkId* chunkId = ChunkId::FromStream(m_stream); + if (chunkId->EqualsTo(m_emptyChunk)) // End reached + break; + + // Read up to a 64 bit number for the chunk size + __int64 chunkSize = LEB128::ReadLong(m_stream); + + // Try loading chunk content + bool chunkRecognized = false; + __int32 startPos = m_stream->GetCurrentPosition(); + + chunkRecognized = func(chunkId, chunkSize); + __int32 readDataCount = m_stream->GetCurrentPosition() - startPos; + + // Adjust _stream position if necessary + if (readDataCount != chunkSize) + m_stream->Seek(chunkSize, SEEK_ORIGIN::CURRENT); + } while (true); + + return true; + } + + char* ReadChunkArrayOfBytes(__int64 length) + { + char* value = (char*)malloc(length); + m_stream->Read(value, length); + return value; + } + + bool ReadChunkBool(__int64 length) + { + return (LEB128::ReadByte(m_stream) != 0); + } + + __int64 ReadChunkLong(__int64 length) + { + return LEB128::ReadLong(m_stream); + } + + __int32 ReadChunkInt32(__int64 length) + { + return LEB128::ReadInt32(m_stream); + } + + unsigned __int32 ReadChunkUInt32(__int64 length) + { + return LEB128::ReadUInt32(m_stream); + } + + __int16 ReadChunkInt16(__int64 length) + { + return LEB128::ReadInt16(m_stream); + } + + unsigned __int16 ReadChunkUInt16(__int64 length) + { + return LEB128::ReadUInt16(m_stream); + } + + byte ReadChunkByte(__int64 length) + { + return LEB128::ReadByte(m_stream); + } + + char* ReadChunkString(long length) + { + char* value = (char*)malloc(length); + memcpy(value, LevelDataPtr, length); + return value; + } + + BaseStream* GetRawStream() + { + return m_stream; + } +}; + diff --git a/TR5Main/Specific/IO/ChunkWriter.h b/TR5Main/Specific/IO/ChunkWriter.h new file mode 100644 index 000000000..12caefcbf --- /dev/null +++ b/TR5Main/Specific/IO/ChunkWriter.h @@ -0,0 +1,109 @@ +#pragma once +#include "Streams.h" +#include "LEB128.h" +#include "ChunkId.h" + +typedef struct ChunkWritingState +{ +private: + BaseStream* m_stream; + __int64 m_chunkSizePosition; + __int64 m_previousPosition; + __int64 m_maximumSize; + +public: + ChunkWritingState(BaseStream* stream, ChunkId* chunkID, __int64 maximumSize) + { + m_stream = stream; + + // Write chunk ID + chunkID->ToStream(m_stream); + + // Write chunk size + m_chunkSizePosition = m_stream->GetCurrentPosition(); + LEB128::Write(m_stream, 0, maximumSize); + + // Prepare for writeing chunk content + m_previousPosition = m_stream->GetCurrentPosition(); + m_maximumSize = maximumSize; + } + + void EndWrite() + { + // Update chunk size + long newPosition = m_stream->GetCurrentPosition(); + long chunkSize = newPosition - m_previousPosition; + m_stream->Seek(m_chunkSizePosition, SEEK_ORIGIN::BEGIN); + LEB128::Write(m_stream, chunkSize, m_maximumSize); + m_stream->Seek(newPosition, SEEK_ORIGIN::BEGIN); + } +}; + +class ChunkWriter { +private: + BaseStream* m_stream; + +public: + ChunkWriter(__int32 magicNumber, BaseStream* stream) + { + m_stream = stream; + + __int32 value = 0; + + m_stream->WriteInt32(&magicNumber); + m_stream->WriteInt32(&value); + } + + BaseStream* GetRawStream() + { + return m_stream; + } + + void WriteChunkEnd() + { + byte value = 0; + m_stream->WriteByte(&value); + } + + void WriteChunkEmpty(ChunkId* chunkID) + { + chunkID->ToStream(m_stream); + LEB128::Write(m_stream, 0); + } + + void WriteChunkArrayOfBytes(ChunkId* chunkID, byte* value, __int32 length) + { + chunkID->ToStream(m_stream); + LEB128::Write(m_stream, length); + m_stream->WriteBytes(value, length); + } + + void WriteChunkInt(ChunkId* chunkID, __int64 value) + { + chunkID->ToStream(m_stream); + LEB128::Write(m_stream, LEB128::GetLength(m_stream, value)); + LEB128::Write(m_stream, value); + } + + ChunkWritingState* WriteChunk(ChunkId* chunkID, __int64 maximumSize = LEB128::MaximumSize4Byte) + { + return new ChunkWritingState(m_stream, chunkID, maximumSize); + } + + void WriteChunk(ChunkId* chunkID, void(*writeChunk)(__int32), __int32 arg, __int64 maximumSize = LEB128::MaximumSize4Byte) + { + ChunkWritingState* state = WriteChunk(chunkID, maximumSize); + writeChunk(arg); + state->EndWrite(); + delete state; + } + + void WriteChunkWithChildren(ChunkId* chunkID, void(*writeChunk)(__int32), __int32 arg, __int64 maximumSize = LEB128::MaximumSize4Byte) + { + ChunkWritingState* state = WriteChunk(chunkID, maximumSize); + writeChunk(arg); + WriteChunkEnd(); + state->EndWrite(); + delete state; + } +}; \ No newline at end of file diff --git a/TR5Main/Specific/IO/LEB128.h b/TR5Main/Specific/IO/LEB128.h new file mode 100644 index 000000000..f9d04be05 --- /dev/null +++ b/TR5Main/Specific/IO/LEB128.h @@ -0,0 +1,116 @@ +#pragma once + +#include +#include +#include + +#include "Streams.h" + +extern char* LevelDataPtr; + +typedef struct LEB128 { + static const __int64 MaximumSize1Byte = 64L - 1; + static const __int64 MaximumSize2Byte = 64L * 128 - 1; + static const __int64 MaximumSize3Byte = 64L * 128 * 128 - 1; + static const __int64 MaximumSize4Byte = 64L * 128 * 128 * 128 - 1; + static const __int64 MaximumSize5Byte = 64L * 128 * 128 * 128 * 128 - 1; + static const __int64 MaximumSize6Byte = 64L * 128 * 128 * 128 * 128 * 128 - 1; + static const __int64 MaximumSize7Byte = 64L * 128 * 128 * 128 * 128 * 128 * 128 - 1; + static const __int64 MaximumSize8Byte = 64L * 128 * 128 * 128 * 128 * 128 * 128 * 128 - 1; + static const __int64 MaximumSize9Byte = 64L * 128 * 128 * 128 * 128 * 128 * 128 * 128 * 128 - 1; + static const __int64 MaximumSize10Byte = UINT64_MAX; + + static __int64 ReadLong(BaseStream* stream) + { + __int64 result = 0; + __int32 currentShift = 0; + + byte currentByte; + do + { + stream->Read(reinterpret_cast(¤tByte), 1); + + result |= (__int64)(currentByte & 0x7F) << currentShift; + currentShift += 7; + } while ((currentByte & 0x80) != 0); + + // Sign extend + __int32 shift = 64 - currentShift; + if (shift > 0) + result = (result << shift) >> shift; + return result; + } + + static __int32 ReadInt32(BaseStream* stream) + { + __int64 value = ReadLong(stream); + return (__int32)min(max(value, INT32_MIN), INT32_MAX); + } + + static __int16 ReadInt16(BaseStream* stream) + { + __int64 value = ReadLong(stream); + return (__int16)min(max(value, INT16_MIN), INT16_MAX); + } + + static byte ReadByte(BaseStream* stream) + { + __int64 value = ReadLong(stream); + return (byte)min(max(value, 0), UINT8_MAX); + } + + static unsigned __int32 ReadUInt32(BaseStream* stream) + { + __int64 value = ReadLong(stream); + return (unsigned __int32)min(max(value, 0), UINT32_MAX); + } + + static unsigned __int16 ReadUInt16(BaseStream* stream) + { + __int64 value = ReadLong(stream); + return (unsigned __int16)min(max(value, 0), UINT16_MAX); + } + + static void Write(BaseStream* stream, __int64 value, __int64 maximumSize) + { + do + { + // Write byte + byte currentByte = ((byte)(value & 0x7F)); + if (maximumSize >> 6 == 0 || maximumSize >> 6 == -1) + { + stream->WriteByte(¤tByte); + + if (value >> 6 != 0 && value >> 6 != -1) + throw "Unable to write integer because the available space overflowed."; + + return; + } + + byte b = currentByte | 0x80; + stream->WriteByte(&b); + + // Move data to next byte + value >>= 7; + maximumSize >>= 7; + } while (true); + } + + static void Write(BaseStream* stream, __int64 value) + { + Write(stream, value, value); + } + + static __int32 GetLength(BaseStream* stream, __int64 value) + { + __int32 length = 1; + value >>= 6; + while (value > 0) + { + value >>= 7; + length += 1; + } + return length; + } +}; + diff --git a/TR5Main/Specific/IO/Streams.h b/TR5Main/Specific/IO/Streams.h new file mode 100644 index 000000000..d3ac3dba1 --- /dev/null +++ b/TR5Main/Specific/IO/Streams.h @@ -0,0 +1,200 @@ +#pragma once + +#include +#include +#include +#include +#include + +using namespace std; + +enum SEEK_ORIGIN { + BEGIN, + CURRENT +}; + +class BaseStream { +public: + virtual bool Read(char* buffer, __int32 length) = 0; + virtual bool Write(char* buffer, __int32 length) = 0; + virtual __int32 GetCurrentPosition() = 0; + virtual bool Seek(__int32 seek, SEEK_ORIGIN origin) = 0; + virtual bool IsEOF() = 0; + virtual bool Close() = 0; + + bool ReadBytes(byte* value, __int32 length) + { + return Read(reinterpret_cast(value), length); + } + + bool ReadByte(byte* value) + { + return Read(reinterpret_cast(value), 1); + } + + bool ReadInt16(__int16* value) + { + return Read(reinterpret_cast(value), 2); + } + + bool ReadInt32(__int32* value) + { + return Read(reinterpret_cast(value), 4); + } + + bool ReadString(char** value) + { + __int32 length; + ReadInt32(&length); + *value = (char*)malloc(length + 1); + Read(*value, length); + (*value)[length] = NULL; + + return true; + } + + bool WriteBytes(byte* value, __int32 length) + { + return Write(reinterpret_cast(value), length); + } + + bool WriteByte(byte* value) + { + return Write(reinterpret_cast(value), 1); + } + + bool WriteInt16(__int16* value) + { + return Write(reinterpret_cast(value), 3); + } + + bool WriteInt32(__int32* value) + { + return Write(reinterpret_cast(value), 4); + } + + bool WriteString(char* str) + { + __int32 length = (__int32)strlen(str); + WriteInt32(&length); + Write(str, length); + return true; + } +}; + +class MemoryStream : public BaseStream { +private: + char* m_startBuffer; + char* m_buffer; + __int32 m_size; + +public: + MemoryStream(char* buffer, __int32 size) + { + m_buffer = (char*)malloc(size); + m_startBuffer = m_buffer; + memcpy(m_buffer, buffer, size); + m_size = size; + } + + MemoryStream(__int32 size) + { + m_buffer = (char*)malloc(size); + m_startBuffer = m_buffer; + m_size = size; + } + + ~MemoryStream() + { + free(m_startBuffer); + } + + bool Read(char* buffer, __int32 length) + { + memcpy(buffer, m_buffer, length); + m_buffer += length; + return true; + } + + bool Write(char* buffer, __int32 length) + { + memcpy(m_buffer, buffer, length); + m_buffer += length; + return true; + } + + __int32 GetCurrentPosition() + { + return (m_buffer - m_startBuffer); + } + + bool Seek(__int32 seek, SEEK_ORIGIN origin) + { + if (origin == SEEK_ORIGIN::BEGIN) + m_buffer = m_startBuffer + seek; + else + m_buffer += seek; + return true; + } + + bool IsEOF() + { + return (GetCurrentPosition() > m_size); + } + + bool Close() + { + return true; + } +}; + +class FileStream : public BaseStream { +private: + fstream m_stream; + +public: + FileStream(char* fileName) + { + m_stream.open(fileName, fstream::in | fstream::out | fstream::trunc); + } + + ~FileStream() + { + m_stream.close(); + } + + bool Read(char* buffer, __int32 length) + { + m_stream.read(buffer, length); + return true; + } + + bool Write(char* buffer, __int32 length) + { + m_stream.write(buffer, length); + return true; + } + + __int32 GetCurrentPosition() + { + return (__int32)(m_stream.tellg()); + } + + bool Seek(__int32 seek, SEEK_ORIGIN origin) + { + m_stream.seekg(seek, (origin == SEEK_ORIGIN::BEGIN ? m_stream.beg : m_stream.cur)); + return true; + } + + bool IsEOF() + { + return (m_stream.eof()); + } + + bool Close() + { + m_stream.flush(); + m_stream.close(); + return true; + } +}; \ No newline at end of file diff --git a/TR5Main/Specific/patch.cpp b/TR5Main/Specific/patch.cpp index 89373e591..35a949315 100644 --- a/TR5Main/Specific/patch.cpp +++ b/TR5Main/Specific/patch.cpp @@ -1,6 +1,7 @@ #include "patch.h" -#include "..\Global\global.h"1 +#include "..\Global\global.h" #include +#include "roomload.h" // Remapped variables. Some variables must be moved from EXE space to DLL space for having free space for bigger arrays __int32 NumItems; @@ -97,6 +98,106 @@ void PatchGameCode() newValue = (__int32)&StaticObjects[0] + 16; WriteProcessMemory(gameHandle, (LPVOID)0x4192B0, &newValue, 4, NULL); // ObjectOnLOS2 - //newValue = 0xFF; - //WriteProcessMemory(gameHandle, (LPVOID)0x004A7385, &newValue, 4, NULL); // ObjectOnLOS2 + // Move LevelDataPtr for having more space for Objects[] + newValue = (__int32)&LevelDataPtr; + + // LoadRooms + WriteProcessMemory(gameHandle, (LPVOID)0x004916C4, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004916CE, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004916DA, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x00491706, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x00491710, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x0049171B, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x00491726, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x00491745, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x0049175B, &newValue, 4, NULL); + + // LoadRooms + WriteProcessMemory(gameHandle, (LPVOID)0x004A4DCF, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A4DDA, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A4DEA, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A4E0C, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A4E1C, &newValue, 4, NULL); + + // LoadSprites + WriteProcessMemory(gameHandle, (LPVOID)0x004A59E2, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A59EC, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A59F6, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5A20, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5A4B, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5B61, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5B6E, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5B8C, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5B9D, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5BBF, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5BE4, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5BF7, &newValue, 4, NULL); + + // LoadSoundEffects + WriteProcessMemory(gameHandle, (LPVOID)0x004A5D9D, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5DB5, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5DE2, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5E08, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5E13, &newValue, 4, NULL); + + // LoadBoxes + WriteProcessMemory(gameHandle, (LPVOID)0x004A5E62, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5E6E, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5E8D, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5EB2, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5EBA, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5EC4, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5ED3, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5EF7, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5F04, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5F26, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5F4D, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5F5C, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5F79, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5F9F, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A5FAE, &newValue, 4, NULL); + + //LoadAnimatedTextures + WriteProcessMemory(gameHandle, (LPVOID)0x004A6061, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A6070, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A607F, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A609E, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A60A7, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A60B3, &newValue, 4, NULL); + + // LoadTextureInfos + WriteProcessMemory(gameHandle, (LPVOID)0x004A60F1, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A60F9, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A610B, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A613D, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A6159, &newValue, 4, NULL); + + // Sub_4A6760 + WriteProcessMemory(gameHandle, (LPVOID)0x004A6761, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A6771, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A677C, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A679B, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A67A4, &newValue, 4, NULL); + + // LoadDemoData + WriteProcessMemory(gameHandle, (LPVOID)0x004A67D1, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A67D9, &newValue, 4, NULL); + + // LoadAIObjects + WriteProcessMemory(gameHandle, (LPVOID)0x004A67F2, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A67FF, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A681E, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A683D, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A6846, &newValue, 4, NULL); + + // LoadSamples + WriteProcessMemory(gameHandle, (LPVOID)0x004A689F, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A68B2, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A68BC, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A68D4, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A6913, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A6939, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A6941, &newValue, 4, NULL); + WriteProcessMemory(gameHandle, (LPVOID)0x004A694D, &newValue, 4, NULL); + } \ No newline at end of file diff --git a/TR5Main/Specific/roomload.cpp b/TR5Main/Specific/roomload.cpp index 30d41245e..36c6d3f09 100644 --- a/TR5Main/Specific/roomload.cpp +++ b/TR5Main/Specific/roomload.cpp @@ -4,11 +4,23 @@ #include "..\Specific\setup.h" #include "..\Game\draw.h" #include "..\Game\lot.h" +#include "..\Game\savegame.h" + +#include "IO/ChunkId.h" +#include "IO/ChunkReader.h" +#include "IO/ChunkWriter.h" +#include "IO/LEB128.h" #include #include #include #include +#include "IO/Streams.h" + +ChunkId* ChunkTriggersList = ChunkId::FromString("Tr5Triggers"); +ChunkId* ChunkTrigger = ChunkId::FromString("Tr5Trigger"); +ChunkId* ChunkLuaIds = ChunkId::FromString("Tr5LuaIds"); +ChunkId* ChunkLuaId = ChunkId::FromString("Tr5LuaId"); extern GameScript* g_GameScript; @@ -25,6 +37,8 @@ __int32 NumMeshPointers; __int32 NumObjectTextures; uintptr_t hLoadLevel; +unsigned __int32 ThreadId; +__int32 IsLevelLoading; using namespace std; @@ -33,6 +47,9 @@ vector<__int32> StaticObjectsIds; extern GameFlow* g_GameFlow; extern __int16 LaraVehicle; +char* LevelDataPtr; + +ChunkReader* chunkIO; __int16 ReadInt16() { @@ -287,6 +304,23 @@ void __cdecl LoadObjects() MoveablesIds.push_back(ID_DEFAULT_SPRITES); } +void __cdecl LoadCameras() +{ + NumberCameras = ReadInt32(); + if (NumberCameras != 0) + { + Cameras = (OBJECT_VECTOR*)GameMalloc(NumberCameras * sizeof(OBJECT_VECTOR)); + ReadBytes(Cameras, NumberCameras * sizeof(OBJECT_VECTOR)); + } + + NumberSpotcams = ReadInt32(); + + if (NumberSpotcams != 0) + { + ReadBytes(SpotCam, NumberSpotcams * sizeof(SPOTCAM)); + } +} + void __cdecl LoadTextures() { DB_Log(2, "LoadTextures - DLL"); @@ -422,6 +456,7 @@ void __cdecl FreeLevel() MallocPtr = MallocBuffer; MallocFree = MallocSize; g_Renderer->FreeRendererData(); + g_GameScript->FreeLevelScripts(); } unsigned __stdcall LoadLevel(void* data) @@ -479,9 +514,20 @@ unsigned __stdcall LoadLevel(void* data) LoadAIObjects(); LoadDemoData(); LoadSamples(); - - LoadNewData(); + __int32 extraSize = 0; + ReadFileEx(&extraSize, 1, 4, LevelFilePtr); + if (extraSize > 0) + { + ReadFileEx(&extraSize, 1, 4, LevelFilePtr); + //free(LevelDataPtr); + LevelDataPtr = (char*)malloc(extraSize); + ReadFileEx(LevelDataPtr, extraSize, 1, LevelFilePtr); + + LoadNewData(extraSize); + } + + //free(LevelDataPtr); LevelDataPtr = NULL; FileClose(LevelFilePtr); } @@ -491,7 +537,7 @@ unsigned __stdcall LoadLevel(void* data) } g_Renderer->PrepareDataForTheRenderer(); - + // Initialise the game GameScriptLevel* level = g_GameFlow->GetLevel(CurrentLevel); @@ -506,6 +552,7 @@ unsigned __stdcall LoadLevel(void* data) SeedRandomDraw(0xD371F947); SeedRandomControl(0xD371F947); LaraVehicle = -1; + g_GameScript->AssignVariables(); // Level loaded IsLevelLoading = false; @@ -545,32 +592,33 @@ void __cdecl AdjustUV(__int32 num) NumObjectTextures = num; } -void __cdecl LoadNewData() +bool __cdecl ReadLuaIds(ChunkId* chunkId, __int32 maxSize) { - // Free old level scripts - g_GameScript->FreeLevelScripts(); - - // Check for magic word - __int32 magicWord; - ReadFileEx(&magicWord, 1, 4, LevelFilePtr); - if (magicWord != 0x5455354D) - return; - - // Load LUA triggers - __int32 numTriggers; - ReadFileEx(&numTriggers, 1, 4, LevelFilePtr); - for (__int32 i = 0; i < numTriggers; i++) + if (chunkId->EqualsTo(ChunkLuaId)) { - __int32 strSize; - ReadFileEx(&strSize, 1, 4, LevelFilePtr); - char* functionName = (char*)malloc(strSize + 1); - ZeroMemory(functionName, strSize + 1); - ReadFileEx(functionName, strSize, 1, LevelFilePtr); + __int32 luaId = 0; + chunkIO->GetRawStream()->ReadInt32(&luaId); + + __int32 itemId = 0; + chunkIO->GetRawStream()->ReadInt32(&itemId); - ReadFileEx(&strSize, 1, 4, LevelFilePtr); - char* functionCode = (char*)malloc(strSize + 1); - ZeroMemory(functionCode, strSize + 1); - ReadFileEx(functionCode, strSize, 1, LevelFilePtr); + g_GameScript->AddLuaId(luaId, itemId); + + return true; + } + else + return false; +} + +bool __cdecl ReadLuaTriggers(ChunkId* chunkId, __int32 maxSize) +{ + if (chunkId->EqualsTo(ChunkTrigger)) + { + char* functionName = NULL; + chunkIO->GetRawStream()->ReadString(&functionName); + + char* functionCode = NULL; + chunkIO->GetRawStream()->ReadString(&functionCode); LuaFunction* function = new LuaFunction(); function->Name = string(functionName); @@ -581,7 +629,51 @@ void __cdecl LoadNewData() delete functionName; delete functionCode; + + return true; } + else + return false; +} + +bool __cdecl ReadNewDataChunks(ChunkId* chunkId, __int32 maxSize) +{ + if (chunkId->EqualsTo(ChunkTriggersList)) + return chunkIO->ReadChunks(ReadLuaTriggers); + else if (chunkId->EqualsTo(ChunkLuaIds)) + return chunkIO->ReadChunks(ReadLuaIds); + return false; +} + +//ChunkWriter* writer; + +void __cdecl SaveTest() +{ + +} + +void __cdecl LoadNewData(__int32 size) +{ + // Free old level scripts + //g_GameScript->FreeLevelScripts(); + MemoryStream stream(LevelDataPtr, size); + chunkIO = new ChunkReader(0x4D355254, &stream); + if (!chunkIO->IsValid()) + return; + + chunkIO->ReadChunks(ReadNewDataChunks); + + /*ChunkId* testChunkId = ChunkId::FromString("TR5MSavItems"); + FileStream fs("H:\\test.sav"); + writer = new ChunkWriter(0x4D355254, &fs); + writer->WriteChunkInt(testChunkId, 1234); + fs.Close();*/ + + SaveGame::Save((char*)"H:\\test.sav"); + + /*SaveGame* sg = new SaveGame(); + sg->Save("H:\\test.sav"); + delete sg;*/ } void Inject_RoomLoad() @@ -593,4 +685,5 @@ void Inject_RoomLoad() INJECT(0x0040130C, S_LoadLevelFile); INJECT(0x004A7130, FreeLevel); INJECT(0x004A5430, AdjustUV); + INJECT(0x004A5CA0, LoadCameras); } \ No newline at end of file diff --git a/TR5Main/Specific/roomload.h b/TR5Main/Specific/roomload.h index ad54c539f..0c3346fa8 100644 --- a/TR5Main/Specific/roomload.h +++ b/TR5Main/Specific/roomload.h @@ -5,6 +5,13 @@ #include #include +#include "IO/ChunkId.h" +#include "IO/ChunkReader.h" +#include "IO/LEB128.h" + +struct ChunkId; +struct LEB128; + #define DoSomethingWithRooms ((void (__cdecl*)()) 0x004774D0) #define FreeItemsStuff ((void (__cdecl*)(__int32)) 0x00440590) #define InitialiseClosedDoors ((void (__cdecl*)()) 0x00473600) @@ -17,7 +24,7 @@ #define FileOpen ((FILE* (__cdecl*)(char*)) 0x004A3CD0) #define FileClose ((void (__cdecl*)(FILE*)) 0x004A3DA0) #define LoadSprites ((void (__cdecl*)()) 0x004A59D0) -#define LoadCameras ((void (__cdecl*)()) 0x004A5CA0) +//#define LoadCameras ((void (__cdecl*)()) 0x004A5CA0) #define LoadSoundEffects ((void (__cdecl*)()) 0x004A5D90) #define LoadBoxes ((void (__cdecl*)()) 0x004A5E50) #define LoadAnimatedTextures ((void (__cdecl*)()) 0x004A6060) @@ -53,6 +60,7 @@ extern vector<__int32> StaticObjectsIds; extern __int32* RawMeshPointers; extern __int16* RawMeshData; extern __int32 NumObjectTextures; +extern char* LevelDataPtr; void __cdecl LoadTextures(); __int32 __cdecl LoadRoomsNew(); @@ -61,8 +69,14 @@ void __cdecl LoadObjects(); __int32 __cdecl S_LoadLevelFile(__int32 levelIndex); void __cdecl FreeLevel(); void __cdecl AdjustUV(__int32 num); -void __cdecl LoadNewData(); +void __cdecl LoadCameras(); unsigned __stdcall LoadLevel(void* data); +// New functions for loading data from TR5M footer +bool __cdecl ReadLuaIds(ChunkId* chunkId, __int32 maxSize); +bool __cdecl ReadLuaTriggers(ChunkId* chunkId, __int32 maxSize); +bool __cdecl ReadNewDataChunks(ChunkId* chunkId, __int32 maxSize); +void __cdecl LoadNewData(__int32 size); + void Inject_RoomLoad(); \ No newline at end of file diff --git a/TR5Main/TR5Main.vcxproj b/TR5Main/TR5Main.vcxproj index 2232d3078..b8b995a25 100644 --- a/TR5Main/TR5Main.vcxproj +++ b/TR5Main/TR5Main.vcxproj @@ -169,6 +169,10 @@ xcopy /Y "$(ProjectDir)Scripting\Scripts\*.lua" "$(TargetDir)\Scripts" + + + + @@ -259,6 +263,7 @@ xcopy /Y "$(ProjectDir)Scripting\Scripts\*.lua" "$(TargetDir)\Scripts" + diff --git a/TR5Main/TR5Main.vcxproj.filters b/TR5Main/TR5Main.vcxproj.filters index 6210c0123..e766fb3ae 100644 --- a/TR5Main/TR5Main.vcxproj.filters +++ b/TR5Main/TR5Main.vcxproj.filters @@ -300,6 +300,21 @@ File di intestazione + + File di intestazione + + + File di intestazione + + + File di intestazione + + + File di intestazione + + + File di intestazione +