diff --git a/src/WraithX/WraithX/TextReader.cpp b/src/WraithX/WraithX/TextReader.cpp index 2e9a1f3..15ecdd9 100644 --- a/src/WraithX/WraithX/TextReader.cpp +++ b/src/WraithX/WraithX/TextReader.cpp @@ -139,15 +139,17 @@ void TextReader::SetPosition(uint64_t Offset) } } -std::string TextReader::ReadLine() +std::string TextReader::ReadLine(bool& Success) { + Success = true; // Make sure we have a file if (FileHandle != nullptr) { // Get the next line from the stream char LineBuffer[0x2000]; // Read - fgets(LineBuffer, 0x2000, FileHandle); + if(fgets(LineBuffer, 0x2000, FileHandle) == NULL) + Success = false; // Return return Strings::EndTrim(std::string(LineBuffer)); } diff --git a/src/WraithX/WraithX/TextReader.h b/src/WraithX/WraithX/TextReader.h index 7823bc5..38ef8fb 100644 --- a/src/WraithX/WraithX/TextReader.h +++ b/src/WraithX/WraithX/TextReader.h @@ -37,7 +37,7 @@ class TextReader void SetPosition(uint64_t Offset); // Read the next line from the file - std::string ReadLine(); + std::string ReadLine(bool& Success); // Reads the entire file to the end std::string ReadToEnd(); // Parse the next line from the file diff --git a/src/WraithXCOD/WraithXCOD.sln b/src/WraithXCOD/WraithXCOD.sln index 834cb08..7910e04 100644 --- a/src/WraithXCOD/WraithXCOD.sln +++ b/src/WraithXCOD/WraithXCOD.sln @@ -11,156 +11,199 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTex", "..\WraithX\Ex EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libFLAC_static", "..\WraithX\ExternalDeps\Flac1.3.2\src\libFLAC\libFLAC_static.vcxproj", "{4CEFBC84-C215-11DB-8314-0800200C9A66}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXMesh", "..\DirectXMesh\DirectXMesh_Desktop_2017.vcxproj", "{6857F086-F6FE-4150-9ED7-7446F1C1C220}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CascLib", "..\CascLib\CascLib_vs17.vcxproj", "{78424708-1F6E-4D4B-920C-FB6D26847055}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 DebugAD|x64 = DebugAD|x64 + DebugAD|x86 = DebugAD|x86 DebugAS|x64 = DebugAS|x64 + DebugAS|x86 = DebugAS|x86 DebugUD|x64 = DebugUD|x64 + DebugUD|x86 = DebugUD|x86 DebugUS|x64 = DebugUS|x64 + DebugUS|x86 = DebugUS|x86 Profile|x64 = Profile|x64 + Profile|x86 = Profile|x86 Release|x64 = Release|x64 + Release|x86 = Release|x86 ReleaseAD|x64 = ReleaseAD|x64 + ReleaseAD|x86 = ReleaseAD|x86 ReleaseAS|x64 = ReleaseAS|x64 + ReleaseAS|x86 = ReleaseAS|x86 ReleaseUD|x64 = ReleaseUD|x64 + ReleaseUD|x86 = ReleaseUD|x86 ReleaseUS|x64 = ReleaseUS|x64 + ReleaseUS|x86 = ReleaseUS|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.Debug|x64.ActiveCfg = Debug|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.Debug|x64.Build.0 = Debug|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.Debug|x86.ActiveCfg = Debug|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugAD|x64.ActiveCfg = Debug|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugAD|x64.Build.0 = Debug|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugAD|x86.ActiveCfg = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugAD|x86.Build.0 = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugAS|x64.ActiveCfg = Debug|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugAS|x64.Build.0 = Debug|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugAS|x86.ActiveCfg = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugAS|x86.Build.0 = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugUD|x64.ActiveCfg = Debug|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugUD|x64.Build.0 = Debug|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugUD|x86.ActiveCfg = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugUD|x86.Build.0 = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugUS|x64.ActiveCfg = Debug|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugUS|x64.Build.0 = Debug|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugUS|x86.ActiveCfg = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.DebugUS|x86.Build.0 = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.Profile|x64.ActiveCfg = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.Profile|x64.Build.0 = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.Profile|x86.ActiveCfg = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.Profile|x86.Build.0 = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.Release|x64.ActiveCfg = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.Release|x64.Build.0 = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.Release|x86.ActiveCfg = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseAD|x64.ActiveCfg = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseAD|x64.Build.0 = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseAD|x86.ActiveCfg = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseAD|x86.Build.0 = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseAS|x64.ActiveCfg = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseAS|x64.Build.0 = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseAS|x86.ActiveCfg = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseAS|x86.Build.0 = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseUD|x64.ActiveCfg = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseUD|x64.Build.0 = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseUD|x86.ActiveCfg = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseUD|x86.Build.0 = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseUS|x64.ActiveCfg = Release|x64 {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseUS|x64.Build.0 = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseUS|x86.ActiveCfg = Release|x64 + {948F6C8F-B11C-4E84-AC0E-8A8B1E94D119}.ReleaseUS|x86.Build.0 = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.Debug|x64.ActiveCfg = Debug|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.Debug|x64.Build.0 = Debug|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.Debug|x86.ActiveCfg = Debug|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugAD|x64.ActiveCfg = Debug|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugAD|x64.Build.0 = Debug|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugAD|x86.ActiveCfg = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugAD|x86.Build.0 = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugAS|x64.ActiveCfg = Debug|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugAS|x64.Build.0 = Debug|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugAS|x86.ActiveCfg = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugAS|x86.Build.0 = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugUD|x64.ActiveCfg = Debug|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugUD|x64.Build.0 = Debug|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugUD|x86.ActiveCfg = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugUD|x86.Build.0 = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugUS|x64.ActiveCfg = Debug|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugUS|x64.Build.0 = Debug|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugUS|x86.ActiveCfg = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.DebugUS|x86.Build.0 = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.Profile|x64.ActiveCfg = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.Profile|x64.Build.0 = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.Profile|x86.ActiveCfg = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.Profile|x86.Build.0 = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.Release|x64.ActiveCfg = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.Release|x64.Build.0 = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.Release|x86.ActiveCfg = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseAD|x64.ActiveCfg = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseAD|x64.Build.0 = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseAD|x86.ActiveCfg = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseAD|x86.Build.0 = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseAS|x64.ActiveCfg = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseAS|x64.Build.0 = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseAS|x86.ActiveCfg = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseAS|x86.Build.0 = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseUD|x64.ActiveCfg = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseUD|x64.Build.0 = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseUD|x86.ActiveCfg = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseUD|x86.Build.0 = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseUS|x64.ActiveCfg = Release|x64 {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseUS|x64.Build.0 = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseUS|x86.ActiveCfg = Release|x64 + {BFF7E83B-ACD0-447F-A4DE-A11D726ACAE1}.ReleaseUS|x86.Build.0 = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.ActiveCfg = Debug|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.Build.0 = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x86.ActiveCfg = Debug|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugAD|x64.ActiveCfg = Debug|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugAD|x64.Build.0 = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugAD|x86.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugAD|x86.Build.0 = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugAS|x64.ActiveCfg = Debug|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugAS|x64.Build.0 = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugAS|x86.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugAS|x86.Build.0 = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugUD|x64.ActiveCfg = Debug|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugUD|x64.Build.0 = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugUD|x86.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugUD|x86.Build.0 = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugUS|x64.ActiveCfg = Debug|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugUS|x64.Build.0 = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugUS|x86.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.DebugUS|x86.Build.0 = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.ActiveCfg = Profile|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.Build.0 = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x86.ActiveCfg = Profile|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.ActiveCfg = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.Build.0 = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x86.ActiveCfg = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseAD|x64.ActiveCfg = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseAD|x64.Build.0 = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseAD|x86.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseAD|x86.Build.0 = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseAS|x64.ActiveCfg = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseAS|x64.Build.0 = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseAS|x86.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseAS|x86.Build.0 = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseUD|x64.ActiveCfg = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseUD|x64.Build.0 = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseUD|x86.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseUD|x86.Build.0 = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseUS|x64.ActiveCfg = Release|x64 {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseUS|x64.Build.0 = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseUS|x86.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.ReleaseUS|x86.Build.0 = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.Debug|x64.ActiveCfg = Debug|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.Debug|x64.Build.0 = Debug|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.Debug|x86.ActiveCfg = Debug|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugAD|x64.ActiveCfg = Debug|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugAD|x64.Build.0 = Debug|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugAD|x86.ActiveCfg = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugAD|x86.Build.0 = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugAS|x64.ActiveCfg = Debug|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugAS|x64.Build.0 = Debug|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugAS|x86.ActiveCfg = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugAS|x86.Build.0 = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugUD|x64.ActiveCfg = Debug|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugUD|x64.Build.0 = Debug|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugUD|x86.ActiveCfg = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugUD|x86.Build.0 = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugUS|x64.ActiveCfg = Debug|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugUS|x64.Build.0 = Debug|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugUS|x86.ActiveCfg = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.DebugUS|x86.Build.0 = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.Profile|x64.ActiveCfg = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.Profile|x64.Build.0 = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.Profile|x86.ActiveCfg = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.Profile|x86.Build.0 = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.Release|x64.ActiveCfg = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.Release|x64.Build.0 = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.Release|x86.ActiveCfg = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseAD|x64.ActiveCfg = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseAD|x64.Build.0 = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseAD|x86.ActiveCfg = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseAD|x86.Build.0 = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseAS|x64.ActiveCfg = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseAS|x64.Build.0 = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseAS|x86.ActiveCfg = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseAS|x86.Build.0 = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseUD|x64.ActiveCfg = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseUD|x64.Build.0 = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseUD|x86.ActiveCfg = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseUD|x86.Build.0 = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseUS|x64.ActiveCfg = Release|x64 {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseUS|x64.Build.0 = Release|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.Debug|x64.ActiveCfg = Debug|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.Debug|x64.Build.0 = Debug|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.DebugAD|x64.ActiveCfg = Debug|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.DebugAD|x64.Build.0 = Debug|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.DebugAS|x64.ActiveCfg = Debug|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.DebugAS|x64.Build.0 = Debug|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.DebugUD|x64.ActiveCfg = Debug|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.DebugUD|x64.Build.0 = Debug|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.DebugUS|x64.ActiveCfg = Debug|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.DebugUS|x64.Build.0 = Debug|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.Profile|x64.ActiveCfg = Profile|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.Profile|x64.Build.0 = Profile|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.Release|x64.ActiveCfg = Release|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.Release|x64.Build.0 = Release|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.ReleaseAD|x64.ActiveCfg = Release|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.ReleaseAD|x64.Build.0 = Release|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.ReleaseAS|x64.ActiveCfg = Release|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.ReleaseAS|x64.Build.0 = Release|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.ReleaseUD|x64.ActiveCfg = Release|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.ReleaseUD|x64.Build.0 = Release|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.ReleaseUS|x64.ActiveCfg = Release|x64 - {6857F086-F6FE-4150-9ED7-7446F1C1C220}.ReleaseUS|x64.Build.0 = Release|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.Debug|x64.ActiveCfg = DebugAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugAD|x64.ActiveCfg = DebugAD|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugAD|x64.Build.0 = DebugAD|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugAS|x64.ActiveCfg = DebugAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugAS|x64.Build.0 = DebugAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUD|x64.ActiveCfg = DebugUD|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUD|x64.Build.0 = DebugUD|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUS|x64.ActiveCfg = DebugUS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUS|x64.Build.0 = DebugUS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.Profile|x64.ActiveCfg = ReleaseAD|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.Profile|x64.Build.0 = ReleaseAD|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.Release|x64.ActiveCfg = ReleaseAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.Release|x64.Build.0 = ReleaseAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseAD|x64.ActiveCfg = ReleaseAD|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseAD|x64.Build.0 = ReleaseAD|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseAS|x64.ActiveCfg = ReleaseAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseAS|x64.Build.0 = ReleaseAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUD|x64.ActiveCfg = ReleaseUD|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUD|x64.Build.0 = ReleaseUD|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUS|x64.ActiveCfg = ReleaseUS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUS|x64.Build.0 = ReleaseUS|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseUS|x86.ActiveCfg = Release|x64 + {4CEFBC84-C215-11DB-8314-0800200C9A66}.ReleaseUS|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/WraithXCOD/WraithXCOD/CASCCache.cpp b/src/WraithXCOD/WraithXCOD/CASCCache.cpp index 7fc34af..fbd312e 100644 --- a/src/WraithXCOD/WraithXCOD/CASCCache.cpp +++ b/src/WraithXCOD/WraithXCOD/CASCCache.cpp @@ -13,34 +13,10 @@ #include "MemoryReader.h" #include "Siren.h" -#include "CASCFileReader.h" +// We need Casc +#include "Casc.h" -std::map GetFiles(HANDLE StorageHandle) -{ - // Data - HANDLE FoundHandle; - CASC_FIND_DATA CASCFindData; - std::map CASCFiles; - FoundHandle = CascFindFirstFile(StorageHandle, "*", &CASCFindData, NULL); - - if (FoundHandle != NULL) - { - do - { - // Check if this is a local file - if (CASCFindData.bFileAvailable == 1) - { - CASCFiles[CASCFindData.szFileName] = CASCFindData; - } - } while (CascFindNextFile(FoundHandle, &CASCFindData)); - } - - CascFindClose(FoundHandle); - return CASCFiles; - -} - -CASCCache::CASCCache() : StorageHandle(nullptr) +CASCCache::CASCCache() { // Default, attempt to load the siren lib Siren::Initialize(L"oo2core_6_win64.dll"); @@ -51,7 +27,7 @@ CASCCache::~CASCCache() // Clean up if need be Siren::Shutdown(); // Close Handle - CascCloseStorage(StorageHandle); + } void CASCCache::LoadPackageCache(const std::string& BasePath) @@ -59,26 +35,31 @@ void CASCCache::LoadPackageCache(const std::string& BasePath) // Call Base function first! CoDPackageCache::LoadPackageCache(BasePath); - if (!CascOpenStorage(Strings::ToUnicodeString(BasePath).c_str(), NULL, &StorageHandle)) + try { -#if _DEBUG - std::cout << "Failed to open CASC Storage\n"; -#endif - return; - } - - // Find Files in CASC - auto Files = GetFiles(StorageHandle); + // Open Storage + Container.Open(BasePath); - // Iterate over file paths - for (auto& File : Files) - { - // We only want XPAKs - if (FileSystems::GetExtension(File.first) == ".xpak") + for (auto& File : Container.GetFileEntries()) { - this->LoadPackage(File.first); + // We only want XPAKs + if (FileSystems::GetExtension(File.first) == ".xpak" && File.second.Exists) + { + try + { + this->LoadPackage(File.first); + } + catch (...) + { + + } + } } } + catch (...) + { + + } // We've finished loading, set status this->SetLoadedState(); @@ -97,13 +78,7 @@ bool CASCCache::LoadPackage(const std::string& FilePath) auto PackageIndex = (uint32_t)PackageFilePaths.size(); // Open CASC File - auto Reader = CASCFileReader(StorageHandle, FilePath); - - // Validate - if (!Reader.IsValid()) - { - return false; - } + auto Reader = Container.OpenFile(FilePath); // Read the header auto Header = Reader.Read(); @@ -112,14 +87,13 @@ bool CASCCache::LoadPackage(const std::string& FilePath) if (Header.Version == 0xD) { Reader.SetPosition(0); - uint64_t Result; - Reader.Read((uint8_t*)&Header, 24, Result); + Reader.Read((uint8_t*)&Header, 0, 24); Reader.Advance(288); - Reader.Read((uint8_t*)&Header + 24, 96, Result); + Reader.Read((uint8_t*)&Header + 24, 0, 96); } // Verify the magic and offset - if (Header.Magic == 0x4950414b && Header.HashOffset < Reader.GetLength()) + if (Header.Magic == 0x4950414b && (int64_t)Header.HashOffset < Reader.GetLength()) { // Jump to hash offset Reader.SetPosition(Header.HashOffset); @@ -232,7 +206,7 @@ std::unique_ptr CASCCache::ExtractPackageObject(uint64_t CacheID, uin // Get the XPAK name auto& XPAKFileName = PackageFilePaths[CacheInfo.PackageFileIndex]; // Open CASC File - auto Reader = CASCFileReader(StorageHandle, XPAKFileName); + auto Reader = Container.OpenFile(XPAKFileName); #if _DEBUG printf("CASCCache::ExtractPackageObject(): Streaming Object: 0x%llx from CASC File: %s\n", CacheID, XPAKFileName.c_str()); @@ -413,11 +387,7 @@ std::unique_ptr CASCCache::ExtractPackageObject(const std::string & P ResultSize = 0; // Open CASC File - auto Reader = CASCFileReader(StorageHandle, PackageName); - - // Check if the file handle is valid - if (Reader.IsValid()) - return nullptr; + auto Reader = Container.OpenFile(PackageName); // Jump to the offset Reader.SetPosition(AssetOffset); diff --git a/src/WraithXCOD/WraithXCOD/CASCCache.h b/src/WraithXCOD/WraithXCOD/CASCCache.h index 0e4034a..000e264 100644 --- a/src/WraithXCOD/WraithXCOD/CASCCache.h +++ b/src/WraithXCOD/WraithXCOD/CASCCache.h @@ -2,15 +2,14 @@ // We need the package cache #include "CoDAssets.h" #include "CoDPackageCache.h" -#include "CascLib.h" +#include "Casc.h" #include // A class that handles reading, caching and extracting CASC Resources class CASCCache : public CoDPackageCache { private: - // Storage Handle - HANDLE StorageHandle; + Casc::Container Container; // Cache Mutex std::shared_mutex ReadMutex; public: diff --git a/src/WraithXCOD/WraithXCOD/CASCFileReader.cpp b/src/WraithXCOD/WraithXCOD/CASCFileReader.cpp deleted file mode 100644 index d6b7b15..0000000 --- a/src/WraithXCOD/WraithXCOD/CASCFileReader.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "CASCFileReader.h" - -CASCFileReader::CASCFileReader(HANDLE Storage, std::string fileName) : FileHandle(nullptr) -{ - if (!CascOpenFile(Storage, fileName.c_str(), NULL, NULL, &FileHandle)) - { - FileHandle = nullptr; - } -} - -CASCFileReader::~CASCFileReader() -{ - CascCloseFile(FileHandle); -} - -uint64_t CASCFileReader::GetLength() const -{ - uint64_t Result = 0; - CascGetFileSize64(FileHandle, &Result); - return Result; -} - -uint64_t CASCFileReader::GetPosition() const -{ - TCascFile* hf; - - hf = TCascFile::IsValid(FileHandle); - if (hf == NULL) - return 0; - - return hf->FilePointer; -} - -const bool CASCFileReader::IsValid() const -{ - return FileHandle != nullptr; -} - -void CASCFileReader::SetPosition(uint64_t Offset) -{ - CascSetFilePointer64(FileHandle, Offset, NULL, FILE_BEGIN); -} - -void CASCFileReader::Advance(uint64_t Length) -{ - CascSetFilePointer64(FileHandle, Length, NULL, FILE_CURRENT); -} - -std::string CASCFileReader::ReadNullTerminatedString() -{ - // Make sure we are loaded - if (FileHandle != nullptr) - { - // We can read - std::stringstream ResultBuffer; - // First char - char CurrentChar = Read(); - // Loop until null or EOF - while (CurrentChar != 0) - { - // Add - ResultBuffer << CurrentChar; - // Read again - CurrentChar = Read(); - } - // Return it - return ResultBuffer.str(); - } - // Failed to perform read -#ifdef _DEBUG - throw new std::exception("No file is open"); -#else - throw new std::exception(""); -#endif -} - -std::unique_ptr CASCFileReader::Read(uint64_t Length, uint64_t& Result) -{ - // Make sure we are loaded - if (FileHandle != nullptr) - { - // Result - DWORD LengthRead = 0; - // We can read the block - auto ResultBlock = std::make_unique((size_t)Length); - // Zero out the memory - std::memset(ResultBlock.get(), 0, (size_t)Length); - // Read it - CascReadFile(FileHandle, ResultBlock.get(), (DWORD)Length, &LengthRead); - // Set result - Result = LengthRead; - // Return result - return ResultBlock; - } - // Failed - return nullptr; -} - -void CASCFileReader::Read(uint8_t* Buffer, uint64_t Length, uint64_t& Result) -{ - // Make sure we are loaded - if (FileHandle != nullptr) - { - // Result - DWORD LengthRead = 0; - // Read it - CascReadFile(FileHandle, Buffer, (DWORD)Length, &LengthRead); - // Set result - Result = LengthRead; - } -} diff --git a/src/WraithXCOD/WraithXCOD/CASCFileReader.h b/src/WraithXCOD/WraithXCOD/CASCFileReader.h deleted file mode 100644 index 4ec7689..0000000 --- a/src/WraithXCOD/WraithXCOD/CASCFileReader.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once -#include "stdafx.h" -#include "CascLib.h" -#include "CascCommon.h" -class CASCFileReader -{ -private: - // A pointer to the File Handle - HANDLE FileHandle; -public: - CASCFileReader(HANDLE Storage, std::string fileName); - ~CASCFileReader(); - - // Gets the length of the file - uint64_t GetLength() const; - // Gets the current position of the file - uint64_t GetPosition() const; - // Checks if the current file is value - const bool IsValid() const; - - // Sets the position of the file - void SetPosition(uint64_t Offset); - // Advances the position of the file - void Advance(uint64_t Length); - - template - // Read a block of data from the file with the given type - T Read() - { - // Ensure the file is open - if (FileHandle != nullptr) - { - // We must read the data based on type. - T ResultValue; - // Zero out the memory - std::memset(&ResultValue, 0, sizeof(ResultValue)); - // Read the value from the process - CascReadFile(FileHandle, &ResultValue, sizeof(ResultValue), NULL); - // Return the result - return ResultValue; - } - // Failed to perform read -#ifdef _DEBUG - throw new std::exception("No file is open"); -#else - throw new std::exception(""); -#endif - } - // Read a null-terminated string from the file - std::string ReadNullTerminatedString(); - // Read a block of data from the file with a given length - std::unique_ptr Read(uint64_t Length, uint64_t& Result); - // Read a block of data from the file with a given length to the buffer - void Read(uint8_t* Buffer, uint64_t Length, uint64_t& Result); -}; - diff --git a/src/WraithXCOD/WraithXCOD/Casc.cpp b/src/WraithXCOD/WraithXCOD/Casc.cpp new file mode 100644 index 0000000..f1fff21 --- /dev/null +++ b/src/WraithXCOD/WraithXCOD/Casc.cpp @@ -0,0 +1,913 @@ +#include "stdafx.h" +#include "Casc.h" + +#include "Hashing.h" +#include "FileSystems.h" +#include "Strings.h" +#include "TextReader.h" +#include "BinaryReader.h" +#include "Compression.h" +#include "MemoryReader.h" + +// TODO: Heavy Improvements are required, this was implemented in less than a day for specific use in Greyhound with Call of Duty +// so I'd advise against using this unless you know exactly what you're doing + +void Casc::Container::LoadIndexFiles() +{ + auto IndexFiles = FileSystems::GetFiles(DataPath, "*.idx"); + + for (auto IndexFile : IndexFiles) + { +#if _DEBUG + printf("LoadIndexFiles(): Loading %s...\n", FileSystems::GetFileName(IndexFile).c_str()); +#endif // DEBUG + BinaryReader IndexReader; + + if (!IndexReader.Open(IndexFile, true)) + continue; + + auto HeaderBlock = IndexReader.Read(); + + // Size must be equal to the size of the Index Header + if (HeaderBlock.BlockSize == sizeof(IndexFileHeader)) + { + // Parse Header + auto Header = IndexReader.Read(); + + // and validate (these validations are done in CoD, and so we expect them for CoD + if ( + Header.ExtraBytes == 0 && + Header.EncodedSizeLength == 4 && + Header.StorageOffsetLength == 5 && + Header.EncodingKeyLength == 9 + ) + { + // Align to the next block + IndexReader.SetPosition((IndexReader.GetPosition() + 0x17) & 0xFFFFFFF0); + + + auto DataBlock = IndexReader.Read(); + + auto EntrySize = Header.EncodedSizeLength + Header.StorageOffsetLength + Header.EncodingKeyLength; + auto EntryBuffer = new uint8_t[EntrySize]; + + for (uint32_t bytesConsumed = 0; bytesConsumed < DataBlock.BlockSize; bytesConsumed += EntrySize) + { + uint64_t BytesRead; + IndexReader.Read(EntryBuffer, EntrySize, BytesRead); + + if (BytesRead != EntrySize) + break; + + DataEntries[IndexKey(EntryBuffer, Header)] = IndexEntry(EntryBuffer, Header); + } + + delete[] EntryBuffer; + IndexReader.Close(); + } +#if _DEBUG + else + { + printf("LoadIndexFiles(): Header Data is invalid\n"); + } +#endif // DEBUG + + } + } +} + +void Casc::Container::LoadDataFiles() +{ + // TODO: This needs a different implementation, destructor is called each time... + auto DataFileNames = FileSystems::GetFiles(DataPath, "data.*"); + + DataFiles.resize(DataFileNames.size()); + + for (auto DataFileName : DataFileNames) + { + DataFile DataFile(DataFileName); + + if (DataFile.GetIndex() == -1) + continue; + if (DataFile.GetFileHandle() == INVALID_HANDLE_VALUE) + continue; + + DataFiles[DataFile.GetIndex()] = DataFile; + } +} + +void Casc::Container::LoadBuildInfo() +{ + if (!FileSystems::FileExists(BuildInfoPath)) + throw std::exception("Failed to locate .build.info"); + + TextReader Reader; + + Reader.Open(BuildInfoPath, true); + + int32_t BuildKeyIndex = 0; + + bool Success = true; + + for (size_t i = 0; i < 2; i++) + { + auto Line = Reader.ReadLine(Success); + + if (!Success) + break; + + auto Split = Strings::SplitString(Line, '|', true); + + if (i == 0) + { + for(int32_t j = 0; j < (int32_t)Split.size(); j++) + { + if (Split[j] == "Build Key!HEX:16") + { + BuildKeyIndex = j; + break; + } + } + } + else + { + BuildKey = Split.at(BuildKeyIndex); + break; + } + } +} + +void Casc::Container::LoadConfigInfo() +{ + if(BuildKey.size() < 4) + throw std::exception("Invalid Build Key"); + + auto FolderName = FileSystems::CombinePath(Path, FileSystems::CombinePath("Data\\config", FileSystems::CombinePath(BuildKey.substr(0, 2), BuildKey.substr(2, 2)))); + auto FileName = FileSystems::CombinePath(FolderName, BuildKey); + + if (!FileSystems::FileExists(FileName)) + throw std::exception("Failed to locate build config"); + + TextReader Reader; + + Reader.Open(FileName, true); + + + while (true) + { + auto Success = true; + auto Line = Reader.ReadLine(Success); + + if (!Success) + break; + if (Strings::StartsWith(Line, "#")) + continue; + if (Strings::IsNullOrWhiteSpace(Line)) + continue; + + auto LineSplit = Strings::SplitString(Line, '=', true); + + if (Strings::Trim(LineSplit.at(0)) == "vfs-root") + { + auto ValueSplit = Strings::SplitString(LineSplit.at(1), ' ', true); + + VFSRootKey = ValueSplit.at(1); + break; + } + } +} + +Casc::Container::Container(const std::string& path) +{ + // For Call of Duty purposes, we assume these paths + Path = path; + DataPath = FileSystems::CombinePath(Path, "Data\\data"); + BuildInfoPath = FileSystems::CombinePath(Path, ".build.info"); + Closed = false; + + LoadBuildInfo(); + LoadConfigInfo(); + LoadDataFiles(); + LoadIndexFiles(); + + // Attempt to locate VFS Root + auto LookUpKey = IndexKey(VFSRootKey, 9); + auto Found = DataEntries.find(LookUpKey); + + // Attempt to load File System + if (Found != DataEntries.end()) + { + auto Entry = Found->second; + + auto Reader = OpenFile(Entry); + // Consume for fast access + Reader.Consume(); + + // Validate header + if (Reader.Read() == 0x53465654) + { + // Load TVFS + FileSystemHandler = std::make_unique(); + FileSystemHandler->Parse(Reader); + + // Run a check against the entries we got back + // and see if they are loaded in this CASC, some won't be + // such as German files on a Polish install + for (auto& File : FileSystemHandler->FileEntries) + { + // Valite if file eixsts + for (auto& FileEntry : File.second.KeyEntries) + { + if (DataEntries.find(FileEntry) == DataEntries.end()) + { + // Doesn't Eixst + File.second.Exists = false; + break; + } + } + } + + } + } +} + +Casc::Container::~Container() +{ + Close(); +} + +void Casc::Container::Open(const std::string & path) +{ + DataFiles.clear(); + DataEntries.clear(); + FileSystemHandler = nullptr; + + // For Call of Duty purposes, we assume these paths + Path = path; + DataPath = FileSystems::CombinePath(Path, "Data\\data"); + BuildInfoPath = FileSystems::CombinePath(Path, ".build.info"); + Closed = false; + + LoadBuildInfo(); + LoadConfigInfo(); + LoadDataFiles(); + LoadIndexFiles(); + + // Attempt to locate VFS Root + auto LookUpKey = IndexKey(VFSRootKey, 9); + auto Found = DataEntries.find(LookUpKey); + + // Attempt to load File System + if (Found != DataEntries.end()) + { + auto Entry = Found->second; + + auto Reader = OpenFile(Entry); + // Consume for fast access + Reader.Consume(); + + // Validate header + if (Reader.Read() == 0x53465654) + { + // Load TVFS + FileSystemHandler = std::make_unique(); + FileSystemHandler->Parse(Reader); + + // Run a check against the entries we got back + // and see if they are loaded in this CASC, some won't be + // such as German files on a Polish install + for (auto& File : FileSystemHandler->FileEntries) + { + // Valite if file eixsts + for (auto& FileEntry : File.second.KeyEntries) + { + if (DataEntries.find(FileEntry) == DataEntries.end()) + { + // Doesn't Eixst + File.second.Exists = false; + break; + } + } + } + + } + } +} + +Casc::FileReader Casc::Container::OpenFile(const std::string& FileName) +{ + if (FileSystemHandler == nullptr) + throw std::exception("Opening by File Name requires a File System Handler"); + + // Run some checks + auto Result = FileSystemHandler->FileEntries.find(FileName); + + if (Result == FileSystemHandler->FileEntries.end()) + throw std::exception("Failed to find File in File System"); + + auto File = Result->second; + + if(!File.Exists) + throw std::exception("Failed to find file in Casc"); + // Our virtual offset + uint64_t VirtualOffset = 0; + // New Reader with this container + FileReader Reader(*this); + // Parse each entry, some files are spread across files/entries + for (auto& KeyEntry : File.KeyEntries) + { + IndexEntry& Entry = DataEntries.at(KeyEntry); + DataFile& DataFile = DataFiles.at(Entry.ArchiveIndex); + + FileSpan Span(Entry.ArchiveIndex); + + DataFile.SetPosition(Entry.Offset); + + BlockTableHeader BLTEHeader{}; + + if (DataFile.Read((void*)&BLTEHeader, sizeof(BLTEHeader)) == sizeof(BlockTableHeader) && BLTEHeader.HeaderSize > 0) + { + auto FrameCount = BLTEHeader.GetFrameCount(); + + auto BLTEntries = std::make_unique(FrameCount); + + DataFile.Read((void*)BLTEntries.get(), FrameCount * sizeof(BlockTableEntry)); + + auto ArchiveOffset = DataFile.GetPosition(); + + Span.ArchiveOffset = ArchiveOffset; + Span.VirtualStartOffset = VirtualOffset; + Span.VirtualEndOffset = VirtualOffset; + + + for (uint32_t i = 0; i < FrameCount; i++) + { + FileFrame Frame; + + Frame.ArchiveOffset = ArchiveOffset; + Frame.EncodedSize = BLTEntries[i].GetEncodedSize(); + Frame.ContentSize = BLTEntries[i].GetContentSize(); + Frame.VirtualStartOffset = VirtualOffset; + Frame.VirtualEndOffset = VirtualOffset + Frame.ContentSize; + + Span.VirtualEndOffset += Frame.ContentSize; + + ArchiveOffset += Frame.EncodedSize; + VirtualOffset += Frame.ContentSize; + + Span.Frames.push_back(Frame); + } + } + + Reader.AddSpan(Span); + } + + // Initialize Info + Reader.SetLength(VirtualOffset); + + + return Reader; +} + + + +Casc::FileReader Casc::Container::OpenFile(IndexEntry Entry) +{ + // Lock for multithreading + std::lock_guard Guard(Mutex); + // Check if storage is closed + if (Closed) + throw std::exception("Attempted to open file from a closed Container"); + + uint64_t VirtualOffset = 0; + + FileReader Reader(*this); + + DataFile& DataFile = DataFiles.at(Entry.ArchiveIndex); + + FileSpan Span(Entry.ArchiveIndex); + + DataFile.SetPosition(Entry.Offset); + + BlockTableHeader BLTEHeader{}; + + if (DataFile.Read((void*)&BLTEHeader, sizeof(BLTEHeader)) == sizeof(BlockTableHeader) && BLTEHeader.HeaderSize > 0) + { + auto FrameCount = BLTEHeader.GetFrameCount(); + + auto BLTEntries = std::make_unique(FrameCount); + + DataFile.Read((void*)BLTEntries.get(), FrameCount * sizeof(BlockTableEntry)); + + auto ArchiveOffset = DataFile.GetPosition(); + + Span.ArchiveOffset = ArchiveOffset; + Span.VirtualStartOffset = VirtualOffset; + Span.VirtualEndOffset = VirtualOffset; + + + for (uint32_t i = 0; i < FrameCount; i++) + { + FileFrame Frame; + + Frame.ArchiveOffset = ArchiveOffset; + Frame.EncodedSize = BLTEntries[i].GetEncodedSize(); + Frame.ContentSize = BLTEntries[i].GetContentSize(); + Frame.VirtualStartOffset = VirtualOffset; + Frame.VirtualEndOffset = VirtualOffset + Frame.ContentSize; + + Span.VirtualEndOffset += Frame.ContentSize; + + ArchiveOffset += Frame.EncodedSize; + VirtualOffset += Frame.ContentSize; + + Span.Frames.push_back(Frame); + } + } + + Reader.AddSpan(Span); + + // Initialize Info + Reader.SetLength(VirtualOffset); + + + Reader.Create(VirtualOffset, false); + + + return Reader; +} + +const int32_t Casc::Container::ReadDataFile(const size_t ArchiveIndex, void * Pointer, const int64_t Offset, const int32_t Size) +{ + // Lock for multithreading + std::lock_guard Guard(Mutex); + // Check if storage is closed + if (Closed) + return 0; + + + DataFile& File = DataFiles[ArchiveIndex]; + + File.SetPosition(Offset); + + return File.Read(Pointer, Size); +} + +const std::map& Casc::Container::GetFileEntries() const +{ + if(FileSystemHandler == nullptr) + throw std::exception("No File System handler present"); + + return FileSystemHandler->FileEntries; +} + +void Casc::Container::Close() +{ + // Lock for multithreading + std::lock_guard Guard(Mutex); + // Check if storage is closed + if (Closed) + return; + + DataFiles.clear(); + DataEntries.clear(); + FileSystemHandler = nullptr; + Closed = true; +} + +Casc::IndexEntry::IndexEntry() : Size(0), Offset(0), ArchiveIndex(0), EncodingKeySize(0) +{ + std::memset(EncodingKey, 0, 16); +} + +Casc::IndexEntry::IndexEntry(const uint8_t* EntryBuffer, const IndexFileHeader& Header) +{ + uint64_t FileOffsetMask = (1 << Header.FileOffsetBits) - 1; + uint64_t PackedOffsetAndIndex = 0; + + PackedOffsetAndIndex = (PackedOffsetAndIndex << 0x08) | EntryBuffer[0 + Header.EncodingKeyLength]; + PackedOffsetAndIndex = (PackedOffsetAndIndex << 0x08) | EntryBuffer[1 + Header.EncodingKeyLength]; + PackedOffsetAndIndex = (PackedOffsetAndIndex << 0x08) | EntryBuffer[2 + Header.EncodingKeyLength]; + PackedOffsetAndIndex = (PackedOffsetAndIndex << 0x08) | EntryBuffer[3 + Header.EncodingKeyLength]; + PackedOffsetAndIndex = (PackedOffsetAndIndex << 0x08) | EntryBuffer[4 + Header.EncodingKeyLength]; + + Size = 0; + Size = (Size << 0x08) | EntryBuffer[3 + Header.EncodingKeyLength + Header.StorageOffsetLength]; + Size = (Size << 0x08) | EntryBuffer[2 + Header.EncodingKeyLength + Header.StorageOffsetLength]; + Size = (Size << 0x08) | EntryBuffer[1 + Header.EncodingKeyLength + Header.StorageOffsetLength]; + Size = (Size << 0x08) | EntryBuffer[0 + Header.EncodingKeyLength + Header.StorageOffsetLength]; + + ArchiveIndex = (int)(PackedOffsetAndIndex >> Header.FileOffsetBits); + Offset = PackedOffsetAndIndex & FileOffsetMask; + EncodingKeySize = Header.EncodingKeyLength; + + std::memset(EncodingKey, 0, 16); + std::memcpy(EncodingKey, EntryBuffer, Header.EncodingKeyLength > 16 ? 16 : Header.EncodingKeyLength); +} + +Casc::IndexKey::IndexKey() +{ + std::memset(EncodingKey, 0, 16); +} + +Casc::IndexKey::IndexKey(const uint8_t* EntryBuffer, const IndexFileHeader & Header) +{ + EncodingKeySize = Header.EncodingKeyLength > 16 ? 16 : Header.EncodingKeyLength; + + std::memset(EncodingKey, 0, 16); + std::memcpy(EncodingKey, EntryBuffer, EncodingKeySize); +} + +Casc::IndexKey::IndexKey(const uint8_t* Key, const size_t KeySize) +{ + EncodingKeySize = KeySize > 16 ? 16 : KeySize; + + std::memset(EncodingKey, 0, 16); + std::memcpy(EncodingKey, Key, EncodingKeySize); +} + +Casc::IndexKey::IndexKey(const std::string& Key, const size_t KeySize) +{ + EncodingKeySize = KeySize > 16 ? 16 : KeySize; + + std::memset(EncodingKey, 0, 16); + + // mapping of ASCII characters to hex values + const uint8_t hashmap[] = + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + // https://gist.github.com/vi/dd3b5569af8a26b97c8e20ae06e804cb + for (uint8_t pos = 0; ((pos < (EncodingKeySize * 2)) && (pos < Key.size())); pos += 2) + { + auto idx0 = ((uint8_t)Key[pos + 0] & 0x1F) ^ 0x10; + auto idx1 = ((uint8_t)Key[pos + 1] & 0x1F) ^ 0x10; + EncodingKey[pos / 2] = (uint8_t)(hashmap[idx0] << 4) | hashmap[idx1]; + }; +} + +Casc::DataFile::DataFile() : FileHandle(INVALID_HANDLE_VALUE), ArchiveIndex(-1) +{ +} + +Casc::DataFile::DataFile(const std::string& Path) +{ + auto Index = Strings::SplitString(Path, '.'); + + if (Index.size() < 2) + ArchiveIndex = -1; + else + ArchiveIndex = std::stoi(Index[1]); + + FileHandle = CreateFileA(Path.c_str(), + FILE_READ_DATA | FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); +} + +Casc::DataFile::~DataFile() +{ + //if (FileHandle != INVALID_HANDLE_VALUE) + //{ + // CloseHandle(FileHandle); + //} +} + +const int32_t Casc::DataFile::Read(void * Pointer, int32_t Size) +{ + if (FileHandle == INVALID_HANDLE_VALUE) + return -1; + + DWORD Result = 0; + ReadFile(FileHandle, Pointer, (DWORD)Size, (LPDWORD)&Result, NULL); + return (size_t)Result; +} + +const HANDLE Casc::DataFile::GetFileHandle() const +{ + return FileHandle; +} + +const int64_t Casc::DataFile::GetIndex() const +{ + return ArchiveIndex; +} + +const uint64_t Casc::DataFile::SetPosition(uint64_t Position) +{ + if (FileHandle == INVALID_HANDLE_VALUE) + return -1; + + LARGE_INTEGER Input; + Input.QuadPart = (LONGLONG)Position; + LARGE_INTEGER Result { 0 }; + SetFilePointerEx(FileHandle, Input, &Result, FILE_BEGIN); + return Result.QuadPart; +} + +const int64_t Casc::DataFile::GetPosition() +{ + if (FileHandle == INVALID_HANDLE_VALUE) + return -1; + + LARGE_INTEGER Result{ 0 }; + SetFilePointerEx(FileHandle, { 0 }, &Result, FILE_CURRENT); + return Result.QuadPart; +} + +void Casc::FileReader::Create(uint64_t TotalLength, bool Consume) +{ + Length = TotalLength; +} + +void Casc::FileReader::Consume() +{ + Cache = std::make_unique(Length); + CacheStartPosition = 0; + CacheEndPosition = Length; + + for (auto& Span : Spans) + { + for (auto& Frame : Span.Frames) + { + auto TempBuffer = std::make_unique(Frame.EncodedSize); + + Parent.ReadDataFile(Span.ArchiveIndex, (void*)TempBuffer.get(), Frame.ArchiveOffset, Frame.EncodedSize); + + + switch (*TempBuffer.get()) + { + case 0x4E: + std::memcpy(TempBuffer.get() + 1, Cache.get() + Frame.VirtualStartOffset, Frame.ContentSize); + break; + case 0x5A: + Compression::DecompressZLibBlock((int8_t*)TempBuffer.get() + 1, (int8_t*)Cache.get() + Frame.VirtualStartOffset, Frame.EncodedSize - 1, Frame.ContentSize); + break; + default: + break; + } + } + } +} + +std::unique_ptr Casc::FileReader::Read(uint64_t Length, uint64_t & Result) +{ + // Result + DWORD LengthRead = 0; + // We can read the block + auto ResultBlock = std::make_unique((size_t)Length); + // Zero out the memory + std::memset(ResultBlock.get(), 0, (size_t)Length); + // Read it + Result = (uint64_t)Read(ResultBlock.get(), 0, Length); + // Return result + return ResultBlock; +} + +size_t Casc::FileReader::Read(uint8_t* buffer, size_t offset, size_t count) +{ + auto readStartPos = InternalPosition; + + // Our we outside the file? + if (readStartPos >= Length) + return 0; + + auto toRead = count; + size_t consumed = 0; + + while (true) + { + auto readEndPos = readStartPos + offset; + auto cacheAvailable = CacheEndPosition - readStartPos; + + if (cacheAvailable > 0 && Cache != nullptr) + { + // We can take from the cache + if (CacheStartPosition <= readStartPos && CacheEndPosition > readStartPos) + { + auto p = (int)(readStartPos - CacheStartPosition); + auto n = (int)(toRead <= (size_t)cacheAvailable) ? toRead : cacheAvailable; + + if (n <= 8) + { + int byteCount = (int)n; + while (--byteCount >= 0) + buffer[offset + byteCount] = Cache[p + byteCount]; + } + else + { + std::memcpy(buffer + offset, Cache.get() + p, n); + } + + toRead -= n; + InternalPosition += n; + offset += n; + consumed += n; + } + } + + // We've satisfied what we need + if (toRead == 0) + break; + + readStartPos = InternalPosition; + + // Our we outside the file? + if (readStartPos >= Length) + break; + + // Find next span that is at the current position and request buffer + auto span = FindSpan(readStartPos); + auto frame = span->FindFrame(readStartPos); + + auto TempBuffer = std::make_unique(frame->EncodedSize); + + if(Parent.ReadDataFile(span->ArchiveIndex, (void*)TempBuffer.get(), frame->ArchiveOffset, frame->EncodedSize) != frame->EncodedSize) + break; + + // New Cache + Cache = std::make_unique(frame->ContentSize); + CacheStartPosition = frame->VirtualStartOffset; + CacheEndPosition = frame->VirtualEndOffset; + + switch (*TempBuffer.get()) + { + case 0x4E: + std::memcpy(Cache.get(), TempBuffer.get() + 1, frame->ContentSize); + break; + case 0x5A: + Compression::DecompressZLibBlock((int8_t*)TempBuffer.get() + 1, (int8_t*)Cache.get(), frame->EncodedSize - 1, frame->ContentSize); + break; + default: + break; + } + } + + return consumed; +} + +Casc::TVFSHandler::PathTableNode Casc::TVFSHandler::ParsePathNode(FileReader& Reader) +{ + PathTableNode entry = {}; + + auto buf = Reader.Peek(); + + if (buf == 0) + { + entry.Flags |= PathTableNodeFlags::PathSeparatorPre; + Reader.Advance(1); + buf = Reader.Peek(); + } + + if (buf < 0x7F && buf != 0xFF) + { + Reader.Advance(1); + Reader.Read((uint8_t*)entry.Name, 0, buf); + entry.NameSize = buf; + buf = Reader.Peek(); + } + + if (buf == 0) + { + entry.Flags |= PathTableNodeFlags::PathSeparatorPost; + Reader.Advance(1); + buf = Reader.Peek(); + } + + if (buf == 0xFF) + { + Reader.Advance(1); + entry.Value = Reader.Read(); + entry.Flags |= PathTableNodeFlags::IsNodeValue; + } + else + { + entry.Flags |= PathTableNodeFlags::PathSeparatorPost; + } + + return entry; + + return PathTableNode(); +} + +void Casc::TVFSHandler::AddFileEntry(FileReader& Reader, const std::string & Name, const uint32_t Offset) +{ + auto PathTableCurrent = Reader.GetPosition(); + + Reader.SetPosition(Header.VFSTableOffset + Offset); + + auto SpanCount = Reader.Read(); + + FileSystem::Entry FileEntry(Name); + + for (uint8_t i = 0; i < SpanCount; i++) + { + auto RefFileOffset = (uint32_t)Reader.Read(); + auto sizeOfSpan = (uint32_t)Reader.Read(); + auto CFTOffset = ReadCFTOffset(Reader); + + auto VFSTableCurrent = Reader.GetPosition(); + Reader.SetPosition(Header.CFTTableOffset + CFTOffset); + + // Attempt to locate this entry, since some may not exist or may be online + IndexKey Index(Header.EncodingKeySize); + + Reader.Read(Index.EncodingKey, 0, Header.EncodingKeySize); + + FileEntry.KeyEntries.push_back(Index); + + Reader.SetPosition(VFSTableCurrent); + } + + FileEntries[FileEntry.Name] = FileEntry; + + Reader.SetPosition(PathTableCurrent); +} + +void Casc::TVFSHandler::ParsePathTable(FileReader& Reader, int64_t end, std::string& Builder) +{ + auto CurrentPosition = Builder.size(); + + while (Reader.GetPosition() < end) + { + auto Entry = ParsePathNode(Reader); + + if ((Entry.Flags & PathTableNodeFlags::PathSeparatorPre)) + Builder.append("\\"); + Builder.append(Entry.Name); + if ((Entry.Flags & PathTableNodeFlags::PathSeparatorPost)) + Builder.append("\\"); + + if ((Entry.Flags & PathTableNodeFlags::IsNodeValue)) + { + if ((Entry.Value & 0x80000000) != 0) + { + auto FolderSize = Entry.Value & 0x7FFFFFFF; + auto FolderStart = Reader.GetPosition(); + auto FolderEnd = FolderStart + FolderSize - 4; + + ParsePathTable(Reader, FolderEnd, Builder); + } + else + { + AddFileEntry(Reader, Builder, Entry.Value); + } + + Builder.erase(CurrentPosition, Builder.size() - CurrentPosition); + } + } +} + +uint32_t Casc::TVFSHandler::ReadCFTOffset(FileReader & Reader) +{ + uint8_t Buffer[4]; + + if (Header.CFTTableSize > 0xFFFFFF) + { + Reader.Read(Buffer, 0, 4); + return (uint32_t)((Buffer[0] << 24) | (Buffer[1] << 16) | (Buffer[2] << 8) | Buffer[3]); + } + else if (Header.CFTTableSize > 0xFFFF) + { + Reader.Read(Buffer, 0, 3); + return (uint32_t)((Buffer[0] << 16) | (Buffer[1] << 8) | Buffer[2]); + } + else if (Header.CFTTableSize > 0xFF) + { + Reader.Read(Buffer, 0, 2); + return (uint32_t)((Buffer[0] << 8) | Buffer[1]); + } + else + { + Reader.Read(Buffer, 0, 1); + return Buffer[0]; + } +} + +void Casc::TVFSHandler::Parse(FileReader& Reader) +{ + Reader.SetPosition(0); + Reader.Read((uint8_t*)&Header, 0, sizeof(Header)); + + if ( + Header.FormatVersion == 1 && + Header.HeaderSize == 0x26 && + Header.EncodingKeySize == 9 && + Header.PatchKeySize == 9 + ) + { + Reader.SetPosition(Header.PathTableOffset); + + std::string Builder; + Builder.reserve(MAX_PATH); + ParsePathTable(Reader, Reader.GetPosition() + Header.PathTableSize, Builder); + } + +} diff --git a/src/WraithXCOD/WraithXCOD/Casc.h b/src/WraithXCOD/WraithXCOD/Casc.h new file mode 100644 index 0000000..cf57586 --- /dev/null +++ b/src/WraithXCOD/WraithXCOD/Casc.h @@ -0,0 +1,457 @@ +#pragma once +#include "Hashing.h" + +namespace Casc +{ +#pragma pack(push, 1) + struct BlockTableHeader + { + uint8_t EncodingKey[16]; + uint32_t ContentSize; + uint16_t Flags; + uint32_t JenkinsHash; + uint32_t Checksum; + uint32_t Signature; + uint32_t HeaderSize; + uint8_t TableFormat; + uint8_t FrameCountBE[3]; + + const uint32_t GetFrameCount() + { + return (FrameCountBE[0] << 16) | (FrameCountBE[1] << 8) | FrameCountBE[2]; + } + }; +#pragma pack(pop) + + struct BlockTableEntry + { + private: + uint8_t EncodedSizeBE[4]; + uint8_t ContentSizeBE[4]; + uint8_t Hash[16]; + + public: + const uint32_t GetEncodedSize() + { + return (EncodedSizeBE[0] << 24) | (EncodedSizeBE[1] << 16) | (EncodedSizeBE[2] << 8) | EncodedSizeBE[3]; + } + + const uint32_t GetContentSize() + { + return (ContentSizeBE[0] << 24) | (ContentSizeBE[1] << 16) | (ContentSizeBE[2] << 8) | ContentSizeBE[3]; + } + }; + struct uint32be_t + { + uint8_t Data[4]; + + operator uint32_t() const + { + return (uint32_t)((Data[0] << 24) | (Data[1] << 16) | (Data[2] << 8) | Data[3]); + } + }; + struct IndexFileBlock + { + uint32_t BlockSize; + uint32_t BlockHash; + }; + + struct IndexFileHeader + { + uint16_t IndexVersion; + uint8_t BucketIndex; + uint8_t ExtraBytes; + uint8_t EncodedSizeLength; + uint8_t StorageOffsetLength; + uint8_t EncodingKeyLength; + uint8_t FileOffsetBits; + uint64_t SegmentSize; + + }; + + class IndexKey + { + public: + // Encoding Key + uint8_t EncodingKey[16]; + // Encoding Key Size + size_t EncodingKeySize; + + // Creates a new Index Key + IndexKey(); + // Creates a new Index Key with the given Size + IndexKey(const size_t KeySize) : EncodingKeySize(KeySize) {} + // Creates a new Index Key with the given Key from an Index File + IndexKey(const uint8_t* EntryBuffer, const IndexFileHeader& Header); + // Creates a new Index Key with the given Key and Size + IndexKey(const uint8_t* Key, const size_t KeySize); + // Creates a new Index Key with the given Key and Size + IndexKey(const std::string& Key, const size_t KeySize); + + // Compares the Keys for Equality + bool operator==(const IndexKey& Compare) const + { + // Check sizes + if (Compare.EncodingKeySize != EncodingKeySize) + return false; + + return std::memcmp(Compare.EncodingKey, EncodingKey, EncodingKeySize > 16 ? 16 : EncodingKeySize) == 0; + } + }; + + // A structure for Index Key Hashing for use in a Map + struct IndexKeyHasher + { + // Generates a Hash for the given Key + std::size_t operator() (const IndexKey & ToHash) const + { + return (size_t)Hashing::HashXXHashStream((int8_t*)ToHash.EncodingKey, ToHash.EncodingKeySize > 16 ? 16 : ToHash.EncodingKeySize); + } + }; + + // A class to hold an Index Entry + class IndexEntry + { + public: + // Encoding Key + uint8_t EncodingKey[16]; + // Encoding Key Size + size_t EncodingKeySize; + // Offset within Archive + uint64_t Offset; + // Full size of the Data within Archive + uint64_t Size; + // Archive Index + uint64_t ArchiveIndex; + + // Creates a new Index Entry + IndexEntry(); + // Creates a new Index Entry from the Buffer from the File + IndexEntry(const uint8_t* EntryBuffer, const IndexFileHeader& Header); + }; + + // A class to hold a Data File + class DataFile + { + private: + // Data File Handle + HANDLE FileHandle; + // Index + int64_t ArchiveIndex; + public: + // Creates a new Data File + DataFile(); + // Creates a new Data File from the given Path + DataFile(const std::string& Path); + // Closes the Data File + ~DataFile(); + + // Reads from the Data File + const int32_t Read(void* Pointer, int32_t Size); + // Sets the current position of the Data File + const uint64_t SetPosition(uint64_t Position); + // Gets the current position of the Data File + const int64_t GetPosition(); + + // Gets the File Handle of this Data File + const HANDLE GetFileHandle() const; + // Gets the index of this Data File + const int64_t GetIndex() const; + }; + + // A class to hold a File Frame + class FileFrame + { + public: + // Virtual Start Offset + uint64_t VirtualStartOffset; + // Virtual End Offset + uint64_t VirtualEndOffset; + // Offset within the Data File + uint64_t ArchiveOffset; + // Encoded Size (compressed, including flag, etc) + uint32_t EncodedSize; + // Content Size + uint32_t ContentSize; + }; + + class FileSpan + { + public: + // Span Frames + std::vector Frames; + // Virtual Start Offset + uint64_t VirtualStartOffset; + // Virtual End Offset + uint64_t VirtualEndOffset; + // Offset within the Data File + uint64_t ArchiveOffset; + // Data File Index + uint64_t ArchiveIndex; + + // Creates a new Span + FileSpan(uint64_t Index) : ArchiveIndex(Index) {} + + // Finds a frame by offset within this Span + FileFrame* FindFrame(uint64_t Position) + { + for (auto& Frame : Frames) + { + if (Position >= Frame.VirtualStartOffset && Position < Frame.VirtualEndOffset) + { + return &Frame; + } + } + + // Nothing Found + return nullptr; + } + }; + + class Container; + + class FileReader + { + // File Spans + std::vector Spans; + // Buffer Cache + std::unique_ptr Cache; + // Start Position of the Cache + int64_t CacheStartPosition; + // End Position of the Cache + int64_t CacheEndPosition; + // Our Internal Position + int64_t InternalPosition; + // The length of the File + int64_t Length; + // Parent Storage + Container& Parent; + public: + // Initializes a new File Reader + FileReader(Container& ParentContainer) : Length(-1), Parent(ParentContainer){} + + void Create(uint64_t TotalLength, bool Consume); + + FileSpan* FindSpan(uint64_t Position) + { + for (auto& Span : Spans) + { + if (Position >= Span.VirtualStartOffset && Position < Span.VirtualEndOffset) + { + return &Span; + } + } + + return nullptr; + } + + void Consume(); + + // Read a block of data from the file with the given type + template + T Read() + { + // We must read the data based on type. + T ResultValue; + // Zero out the memory + std::memset(&ResultValue, 0, sizeof(ResultValue)); + // Read the value from the process + Read((uint8_t*)&ResultValue, 0, sizeof(ResultValue)); + // Return the result + return ResultValue; + } + + template + uint8_t Peek() + { + auto Temp = InternalPosition; + auto Result = Read(); + InternalPosition = Temp; + return Result; + } + + // Read a block of data from the file with a given length + std::unique_ptr Read(uint64_t Length, uint64_t& Result); + + std::string ReadString(size_t size) + { + std::string result; + result.resize(size); + + Read((uint8_t*)result.c_str(), 0, size); + + return result; + } + + size_t Read(uint8_t* Pointer, size_t Offset, size_t Count); + + void AddSpan(const FileSpan& Span) + { + Spans.push_back(Span); + } + + void Advance(uint64_t position) + { + InternalPosition += position; + } + + void SetPosition(uint64_t position) + { + InternalPosition = position; + } + + + const int64_t GetPosition() const + { + return InternalPosition; + } + + const int64_t GetLength() const + { + return Length; + } + + void SetLength(int64_t TotalLength) + { + if (Length != -1) + throw std::exception("File Length has already been set."); + + Length = TotalLength; + } + }; + + class FileSystem + { + public: + class Entry + { + public: + // Full name of the Entry + std::string Name; + // Index Entries + std::vector KeyEntries; + // Size of the Entry + size_t Size; + // Whether this file exists or not + bool Exists; + + Entry() : Size(0), Exists(true) {} + + Entry(const std::string & EntryName) : Size(0), Exists(true), Name(EntryName) {} + }; + + std::map FileEntries; + + public: + // Parses the File System + virtual void Parse(FileReader& Reader) = 0; + }; + + + class TVFSHandler : public FileSystem + { + private: + struct + { + uint32_t Signature; + uint8_t FormatVersion; + uint8_t HeaderSize; + uint8_t EncodingKeySize; + uint8_t PatchKeySize; + uint32be_t Flags; + uint32be_t PathTableOffset; + uint32be_t PathTableSize; + uint32be_t VFSTableOffset; + uint32be_t VFSTableSize; + uint32be_t CFTTableOffset; + uint32be_t CFTTableSize; + uint16_t MaxDepth; + } Header; + + enum PathTableNodeFlags : uint32_t + { + None = 0x0000, + PathSeparatorPre = 0x0001, + PathSeparatorPost = 0x0002, + IsNodeValue = 0x0004, + }; + + struct PathTableNode + { + char Name[MAX_PATH]; + uint8_t NameSize; + uint32_t Flags; + uint32_t Value; + + PathTableNode() : Flags(0), Value(0) + { + std::memset(Name, 0, sizeof(Name)); + } + }; + private: + PathTableNode ParsePathNode(FileReader& Reader); + + void AddFileEntry(FileReader& Reader, const std::string& Name, const uint32_t Offset); + + void ParsePathTable(FileReader& Reader, int64_t end, std::string& Builder); + + uint32_t ReadCFTOffset(FileReader& Reader); + public: + void Parse(FileReader& Reader); + }; + + class Container + { + private: + // Main Path + std::string Path; + // Data Path + std::string DataPath; + // Data Path + std::string BuildInfoPath; + // Config Build Key + std::string BuildKey; + // VFS Root Key + std::string VFSRootKey; + // Data Entries + std::unordered_map DataEntries; + // Data Files + std::vector DataFiles; + // File System Handler + std::unique_ptr FileSystemHandler; + // Container Mutex + std::mutex Mutex; + // If we're closed + bool Closed; + + // Loads Indices Files + void LoadIndexFiles(); + + void LoadDataFiles(); + + void LoadBuildInfo(); + + void LoadConfigInfo(); + public: + Container() + { + + } + Container(const std::string& path); + ~Container(); + + void Open(const std::string& path); + + FileReader OpenFile(const std::string& FileName); + + FileReader OpenFile(IndexEntry entry); + // Reads from the Data File + const int32_t ReadDataFile(const size_t ArchiveIndex, void* Pointer, const int64_t Offset, const int32_t Size); + + // Gets the Files + const std::map& GetFileEntries() const; + + void Close(); + }; +} + diff --git a/src/WraithXCOD/WraithXCOD/GameModernWarfare4.cpp b/src/WraithXCOD/WraithXCOD/GameModernWarfare4.cpp index 7e3824f..a49d813 100644 --- a/src/WraithXCOD/WraithXCOD/GameModernWarfare4.cpp +++ b/src/WraithXCOD/WraithXCOD/GameModernWarfare4.cpp @@ -9,9 +9,6 @@ #include "CoDXPoolParser.h" #include "DBGameFiles.h" -// We need the DirectX Mesh Class -#include "..\..\DirectXMesh\DirectXMesh.h" - // We need the following WraithX classes #include "Strings.h" #include "FileSystems.h" diff --git a/src/WraithXCOD/WraithXCOD/WraithXCOD.rc b/src/WraithXCOD/WraithXCOD/WraithXCOD.rc index a8ca3d5..d503163 100644 --- a/src/WraithXCOD/WraithXCOD/WraithXCOD.rc +++ b/src/WraithXCOD/WraithXCOD/WraithXCOD.rc @@ -362,8 +362,8 @@ IDI_CHECKMARK ICON "..\\..\\WraithX\\Resources\\Che // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,5,9,0 - PRODUCTVERSION 1,5,9,0 + FILEVERSION 1,6,0,0 + PRODUCTVERSION 1,6,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -380,12 +380,12 @@ BEGIN BEGIN VALUE "CompanyName", "DTZxPorter / Scobalula" VALUE "FileDescription", "Greyhound" - VALUE "FileVersion", "1.5.9.0" + VALUE "FileVersion", "1.6.0.0" VALUE "InternalName", "Greyhound" VALUE "LegalCopyright", "Copyright (C) 2020 DTZxPorter / Scobalula" VALUE "OriginalFilename", "Greyhound.exe" VALUE "ProductName", "Greyhound" - VALUE "ProductVersion", "1.5.9.0" + VALUE "ProductVersion", "1.6.0.0" END END BLOCK "VarFileInfo" diff --git a/src/WraithXCOD/WraithXCOD/WraithXCOD.vcxproj b/src/WraithXCOD/WraithXCOD/WraithXCOD.vcxproj index 4860e7f..171a3f3 100644 --- a/src/WraithXCOD/WraithXCOD/WraithXCOD.vcxproj +++ b/src/WraithXCOD/WraithXCOD/WraithXCOD.vcxproj @@ -81,20 +81,14 @@ - - {78424708-1f6e-4d4b-920c-fb6d26847055} - - - {6857f086-f6fe-4150-9ed7-7446f1c1c220} - {bff7e83b-acd0-447f-a4de-a11d726acae1} + - @@ -146,8 +140,8 @@ + - diff --git a/src/WraithXCOD/WraithXCOD/WraithXCOD.vcxproj.filters b/src/WraithXCOD/WraithXCOD/WraithXCOD.vcxproj.filters index e133e8d..6d65755 100644 --- a/src/WraithXCOD/WraithXCOD/WraithXCOD.vcxproj.filters +++ b/src/WraithXCOD/WraithXCOD/WraithXCOD.vcxproj.filters @@ -180,15 +180,15 @@ Source Files\Packages - - Source Files\Helpers - Source Files\Games Source Files\Games + + Source Files\Helpers + @@ -353,7 +353,7 @@ Header Files\Games - + Header Files\Helpers