diff --git a/r5dev/public/binstream.cpp b/r5dev/public/binstream.cpp index 77f1abf4..dd52e0ba 100644 --- a/r5dev/public/binstream.cpp +++ b/r5dev/public/binstream.cpp @@ -42,12 +42,7 @@ bool CIOStream::Open(const string& svFilePath, Mode_t eMode) m_iStream.close(); } m_iStream.open(m_svFilePath.c_str(), std::ios::binary | std::ios::in); - if (!m_iStream.is_open()) - { - Error(eDLL_T::FS, "Error opening file '%s' for read operation.\n", m_svFilePath.c_str()); - m_eCurrentMode = Mode_t::NONE; - } - if (!m_iStream.good()) + if (!m_iStream.is_open() || !m_iStream.good()) { Error(eDLL_T::FS, "Error opening file '%s' for read operation.\n", m_svFilePath.c_str()); m_eCurrentMode = Mode_t::NONE; @@ -67,13 +62,7 @@ bool CIOStream::Open(const string& svFilePath, Mode_t eMode) m_oStream.close(); } m_oStream.open(m_svFilePath.c_str(), std::ios::binary | std::ios::out); - if (!m_oStream.is_open()) - { - Error(eDLL_T::FS, "Error opening file '%s' for write operation.\n", m_svFilePath.c_str()); - m_eCurrentMode = Mode_t::NONE; - return false; - } - if (!m_oStream.good()) + if (!m_oStream.is_open() || !m_oStream.good()) { Error(eDLL_T::FS, "Error opening file '%s' for write operation.\n", m_svFilePath.c_str()); m_eCurrentMode = Mode_t::NONE; diff --git a/r5dev/vpklib/packedstore.cpp b/r5dev/vpklib/packedstore.cpp index b1c4f4fb..4b840e90 100644 --- a/r5dev/vpklib/packedstore.cpp +++ b/r5dev/vpklib/packedstore.cpp @@ -182,17 +182,32 @@ vector<string> CPackedStore::GetEntryPaths(const string& svPathIn, const nlohman } //----------------------------------------------------------------------------- -// Purpose: gets the raw level name from the directory file name +// Purpose: gets the parts of the directory file name (1 = locale + context, 2 = levelname) // Input : &svDirectoryName - +// nCaptureGroup - // Output : string //----------------------------------------------------------------------------- -string CPackedStore::GetLevelName(const string& svDirectoryName) const +string CPackedStore::GetNameParts(const string& svDirectoryName, int nCaptureGroup) const { - std::regex rgArchiveRegex{ R"([^_]*_(.*)(.bsp.pak000_dir).*)" }; + std::regex rgArchiveRegex{ R"((?:.*\/)?([^_]*_)(.*)(.bsp.pak000_dir).*)" }; std::smatch smRegexMatches; std::regex_search(svDirectoryName, smRegexMatches, rgArchiveRegex); - return smRegexMatches[1].str(); + return smRegexMatches[nCaptureGroup].str(); +} + +//----------------------------------------------------------------------------- +// Purpose: gets the source of the directory file name +// Input : &svDirectoryName - +// Output : string +//----------------------------------------------------------------------------- +string CPackedStore::GetSourceName(const string& svDirectoryName) const +{ + std::regex rgArchiveRegex{ R"((?:.*\/)?([^_]*_)(.*)(.bsp.pak000_dir).*)" }; + std::smatch smRegexMatches; + std::regex_search(svDirectoryName, smRegexMatches, rgArchiveRegex); + + return smRegexMatches[1].str() + smRegexMatches[2].str(); } //----------------------------------------------------------------------------- @@ -309,7 +324,7 @@ void CPackedStore::BuildManifest(const vector<VPKEntryBlock_t>& vBlock, const st }; } - string svPathOut = svWorkSpace + "manifest\\"; + string svPathOut = svWorkSpace + "manifest/"; fs::create_directories(svPathOut); ofstream oManifest(svPathOut + svManifestName + ".json"); @@ -361,10 +376,9 @@ void CPackedStore::PackAll(const VPKPair_t& vPair, const string& svPathIn, const { CIOStream writer(svPathOut + vPair.m_svBlockName, CIOStream::Mode_t::WRITE); - string svLevelName = GetLevelName(vPair.m_svDirectoryName); vector<string> vPaths; vector<VPKEntryBlock_t> vEntryBlocks; - nlohmann::json jManifest = GetManifest(svPathIn, svLevelName); + nlohmann::json jManifest = GetManifest(svPathIn, GetSourceName(vPair.m_svDirectoryName)); if (bManifestOnly) { @@ -375,6 +389,9 @@ void CPackedStore::PackAll(const VPKPair_t& vPair, const string& svPathIn, const vPaths = GetEntryPaths(svPathIn); } + uint64_t nSharedTotal = 0i64; + uint32_t nSharedCount = 0i32; + for (size_t i = 0; i < vPaths.size(); i++) { CIOStream reader(vPaths[i], CIOStream::Mode_t::READ); @@ -406,7 +423,7 @@ void CPackedStore::PackAll(const VPKPair_t& vPair, const string& svPathIn, const Warning(eDLL_T::FS, "Exception while reading VPK manifest file: '%s'\n", ex.what()); } } - DevMsg(eDLL_T::FS, "Processing file '%s'\n", svDestPath.c_str()); + DevMsg(eDLL_T::FS, "Packing block '%llu' ('%s')\n", i, svDestPath.c_str()); vEntryBlocks.push_back(VPKEntryBlock_t(reader.GetVector(), writer.GetPosition(), nPreloadData, 0, nEntryFlags, nTextureFlags, svDestPath)); for (size_t j = 0; j < vEntryBlocks[i].m_vvEntries.size(); j++) @@ -422,10 +439,13 @@ void CPackedStore::PackAll(const VPKPair_t& vPair, const string& svPathIn, const if (bUseCompression) { - m_lzCompStatus = lzham_compress_memory(&m_lzCompParams, pDest, &vEntryBlocks[i].m_vvEntries[j].m_nCompressedSize, pSrc, vEntryBlocks[i].m_vvEntries[j].m_nUncompressedSize, &m_nAdler32_Internal, &m_nCrc32_Internal); + m_lzCompStatus = lzham_compress_memory(&m_lzCompParams, pDest, + &vEntryBlocks[i].m_vvEntries[j].m_nCompressedSize, pSrc, + vEntryBlocks[i].m_vvEntries[j].m_nUncompressedSize, &m_nAdler32_Internal, &m_nCrc32_Internal); if (m_lzCompStatus != lzham_compress_status_t::LZHAM_COMP_STATUS_SUCCESS) { - Warning(eDLL_T::FS, "Status '%d' for entry '%llu' within block '%llu' for chunk '%hu' (entry packed without compression)\n", m_lzCompStatus, j, i, vEntryBlocks[i].m_iArchiveIndex); + Warning(eDLL_T::FS, "Status '%d' for entry '%llu' within block '%llu' for chunk '%hu' (entry packed without compression)\n", + m_lzCompStatus, j, i, vEntryBlocks[i].m_iArchiveIndex); vEntryBlocks[i].m_vvEntries[j].m_nCompressedSize = vEntryBlocks[i].m_vvEntries[j].m_nUncompressedSize; memmove(pDest, pSrc, vEntryBlocks[i].m_vvEntries[j].m_nUncompressedSize); @@ -445,6 +465,8 @@ void CPackedStore::PackAll(const VPKPair_t& vPair, const string& svPathIn, const if (auto it{ m_mEntryHashMap.find(svEntryHash) }; it != std::end(m_mEntryHashMap)) { vEntryBlocks[i].m_vvEntries[j].m_nArchiveOffset = it->second.m_nArchiveOffset; + nSharedTotal += it->second.m_nCompressedSize; + nSharedCount++; bShared = true; } else // Add entry to hashmap. @@ -463,8 +485,9 @@ void CPackedStore::PackAll(const VPKPair_t& vPair, const string& svPathIn, const } } } - + DevMsg(eDLL_T::FS, "*** Build chunk totalling '%llu' bytes with '%llu' shared bytes between '%lu' blocks\n", writer.GetPosition(), nSharedTotal, nSharedCount); m_mEntryHashMap.clear(); + VPKDir_t vDir = VPKDir_t(); vDir.Build(svPathOut + vPair.m_svDirectoryName, vEntryBlocks); } @@ -476,7 +499,14 @@ void CPackedStore::PackAll(const VPKPair_t& vPair, const string& svPathIn, const //----------------------------------------------------------------------------- void CPackedStore::UnpackAll(const VPKDir_t& vpkDir, const string& svPathOut) { - BuildManifest(vpkDir.m_vvEntryBlocks, svPathOut, GetLevelName(vpkDir.m_svDirPath)); + if (vpkDir.m_vHeader.m_nHeaderMarker != VPK_HEADER_MARKER || + vpkDir.m_vHeader.m_nMajorVersion != VPK_MAJOR_VERSION || + vpkDir.m_vHeader.m_nMinorVersion != VPK_MINOR_VERSION) + { + Error(eDLL_T::FS, "Invalid VPK directory file (header doesn't match criteria)\n"); + return; + } + BuildManifest(vpkDir.m_vvEntryBlocks, svPathOut, GetSourceName(vpkDir.m_svDirPath)); for (size_t i = 0; i < vpkDir.m_vsvArchives.size(); i++) { @@ -484,15 +514,16 @@ void CPackedStore::UnpackAll(const VPKDir_t& vpkDir, const string& svPathOut) string svPath = fspVpkPath.parent_path().u8string() + '\\' + vpkDir.m_vsvArchives[i]; CIOStream iStream(svPath, CIOStream::Mode_t::READ); // Create stream to read from each archive. - for ( VPKEntryBlock_t vBlock : vpkDir.m_vvEntryBlocks) + //for ( VPKEntryBlock_t vBlock : vpkDir.m_vvEntryBlocks) + for ( size_t j = 0; j < vpkDir.m_vvEntryBlocks.size(); j++) { - if (vBlock.m_iArchiveIndex != static_cast<uint16_t>(i)) + if (vpkDir.m_vvEntryBlocks[j].m_iArchiveIndex != static_cast<uint16_t>(i)) { goto escape; } else // Chunk belongs to this block. { - string svFilePath = CreateDirectories(svPathOut + vBlock.m_svBlockPath, true); + string svFilePath = CreateDirectories(svPathOut + vpkDir.m_vvEntryBlocks[j].m_svBlockPath, true); CIOStream oStream(svFilePath, CIOStream::Mode_t::WRITE); if (!oStream.IsWritable()) @@ -500,9 +531,9 @@ void CPackedStore::UnpackAll(const VPKDir_t& vpkDir, const string& svPathOut) Error(eDLL_T::FS, "Unable to write file '%s'\n", svFilePath.c_str()); continue; } - DevMsg(eDLL_T::FS, "Processing file '%s'\n", vBlock.m_svBlockPath.c_str()); + DevMsg(eDLL_T::FS, "Unpacking block '%llu' from chunk '%llu' ('%s')\n", j, i, vpkDir.m_vvEntryBlocks[j].m_svBlockPath.c_str()); - for (VPKEntryDescriptor_t vEntry : vBlock.m_vvEntries) + for (VPKEntryDescriptor_t vEntry : vpkDir.m_vvEntryBlocks[j].m_vvEntries) { m_nEntryCount++; @@ -520,7 +551,8 @@ void CPackedStore::UnpackAll(const VPKDir_t& vpkDir, const string& svPathOut) if (m_lzDecompStatus != lzham_decompress_status_t::LZHAM_DECOMP_STATUS_SUCCESS) { - Error(eDLL_T::FS, "Status '%d' for entry '%llu' within block '%llu' for chunk '%hu' (entry not decompressed)\n", m_lzDecompStatus, m_nEntryCount, i, vBlock.m_iArchiveIndex); + Error(eDLL_T::FS, "Status '%d' for entry '%llu' within block '%llu' for chunk '%hu' (entry not decompressed)\n", + m_lzDecompStatus, m_nEntryCount, i, vpkDir.m_vvEntryBlocks[j].m_iArchiveIndex); } else // If successfully decompressed, write to file. { @@ -535,10 +567,10 @@ void CPackedStore::UnpackAll(const VPKDir_t& vpkDir, const string& svPathOut) delete[] pCompressedData; } - if (m_nEntryCount == vBlock.m_vvEntries.size()) // Only validate after last entry in block had been written. + if (m_nEntryCount == vpkDir.m_vvEntryBlocks[j].m_vvEntries.size()) // Only validate after last entry in block had been written. { m_nEntryCount = 0; - m_nCrc32_Internal = vBlock.m_nCrc32; + m_nCrc32_Internal = vpkDir.m_vvEntryBlocks[j].m_nCrc32; oStream.Flush(); ValidateCRC32PostDecomp(svFilePath); @@ -590,6 +622,7 @@ VPKEntryBlock_t::VPKEntryBlock_t(const vector<uint8_t> &vData, int64_t nOffset, int nEntryCount = (vData.size() + ENTRY_MAX_LEN - 1) / ENTRY_MAX_LEN; uint64_t nDataSize = vData.size(); int64_t nCurrentOffset = nOffset; + for (int i = 0; i < nEntryCount; i++) { uint64_t nSize = std::min<uint64_t>(ENTRY_MAX_LEN, nDataSize); @@ -600,7 +633,21 @@ VPKEntryBlock_t::VPKEntryBlock_t(const vector<uint8_t> &vData, int64_t nOffset, } //----------------------------------------------------------------------------- -// Purpose: 'VPKEntryDescriptor_t' constructor +// Purpose: 'VPKDir_t' file constructor +// Input : *pReader - +//----------------------------------------------------------------------------- +VPKEntryDescriptor_t::VPKEntryDescriptor_t(CIOStream* pReader) +{ + pReader->Read<uint32_t>(this->m_nEntryFlags); // + pReader->Read<uint16_t>(this->m_nTextureFlags); // + pReader->Read<uint64_t>(this->m_nArchiveOffset); // + pReader->Read<uint64_t>(this->m_nCompressedSize); // + pReader->Read<uint64_t>(this->m_nUncompressedSize); // + this->m_bIsCompressed = (this->m_nCompressedSize != this->m_nUncompressedSize); +} + +//----------------------------------------------------------------------------- +// Purpose: 'VPKEntryDescriptor_t' memory constructor // Input : &nEntryFlags - // &nTextureFlags - // &nArchiveOffset - @@ -617,20 +664,6 @@ VPKEntryDescriptor_t::VPKEntryDescriptor_t(uint32_t nEntryFlags, uint16_t nTextu m_nUncompressedSize = nUncompressedSize; } -//----------------------------------------------------------------------------- -// Purpose: 'VPKDir_t' constructor -// Input : *pReader - -//----------------------------------------------------------------------------- -VPKEntryDescriptor_t::VPKEntryDescriptor_t(CIOStream* pReader) -{ - pReader->Read<uint32_t>(this->m_nEntryFlags); // - pReader->Read<uint16_t>(this->m_nTextureFlags); // - pReader->Read<uint64_t>(this->m_nArchiveOffset); // - pReader->Read<uint64_t>(this->m_nCompressedSize); // - pReader->Read<uint64_t>(this->m_nUncompressedSize); // - this->m_bIsCompressed = (this->m_nCompressedSize != this->m_nUncompressedSize); -} - //----------------------------------------------------------------------------- // Purpose: 'VPKDir_t' file constructor // Input : &szPath - @@ -638,14 +671,8 @@ VPKEntryDescriptor_t::VPKEntryDescriptor_t(CIOStream* pReader) VPKDir_t::VPKDir_t(const string& svPath) { CIOStream reader(svPath, CIOStream::Mode_t::READ); + reader.Read<uint32_t>(this->m_vHeader.m_nHeaderMarker); - - if (this->m_vHeader.m_nHeaderMarker != VPK_HEADER_MARKER) - { - Error(eDLL_T::FS, "VPK directory file '%s' has invalid magic!\n", svPath.c_str()); - return; - } - reader.Read<uint16_t>(this->m_vHeader.m_nMajorVersion); // reader.Read<uint16_t>(this->m_vHeader.m_nMinorVersion); // reader.Read<uint32_t>(this->m_vHeader.m_nDirectorySize); // @@ -711,25 +738,25 @@ void VPKDir_t::Build(const string& svDirectoryFile, const vector<VPKEntryBlock_t for (auto& jKeyValue : iKeyValue.second) { writer.WriteString(jKeyValue.first); - for (auto& eKeyValue : jKeyValue.second) + for (auto& vEntry : jKeyValue.second) { - writer.WriteString(GetFileName(eKeyValue.m_svBlockPath, true)); + writer.WriteString(GetFileName(vEntry.m_svBlockPath, true)); {/*Write entry block*/ - writer.Write(eKeyValue.m_nCrc32); - writer.Write(eKeyValue.m_nPreloadData); - writer.Write(eKeyValue.m_iArchiveIndex); + writer.Write(vEntry.m_nCrc32); + writer.Write(vEntry.m_nPreloadData); + writer.Write(vEntry.m_iArchiveIndex); - for (size_t i = 0; i < eKeyValue.m_vvEntries.size(); i++) + for (size_t i = 0; i < vEntry.m_vvEntries.size(); i++) { {/*Write entry descriptor*/ - writer.Write(eKeyValue.m_vvEntries[i].m_nEntryFlags); - writer.Write(eKeyValue.m_vvEntries[i].m_nTextureFlags); - writer.Write(eKeyValue.m_vvEntries[i].m_nArchiveOffset); - writer.Write(eKeyValue.m_vvEntries[i].m_nCompressedSize); - writer.Write(eKeyValue.m_vvEntries[i].m_nUncompressedSize); + writer.Write(vEntry.m_vvEntries[i].m_nEntryFlags); + writer.Write(vEntry.m_vvEntries[i].m_nTextureFlags); + writer.Write(vEntry.m_vvEntries[i].m_nArchiveOffset); + writer.Write(vEntry.m_vvEntries[i].m_nCompressedSize); + writer.Write(vEntry.m_vvEntries[i].m_nUncompressedSize); } - if (i != (eKeyValue.m_vvEntries.size() - 1)) + if (i != (vEntry.m_vvEntries.size() - 1)) { const ushort s = 0; writer.Write(s); @@ -752,6 +779,8 @@ void VPKDir_t::Build(const string& svDirectoryFile, const vector<VPKEntryBlock_t writer.SetPosition(offsetof(VPKDir_t, m_vHeader.m_nDirectorySize)); writer.Write(this->m_vHeader.m_nDirectorySize); writer.Write(0); + + DevMsg(eDLL_T::FS, "*** Build directory file totalling '%llu' bytes with '%llu' entries\n", writer.GetPosition(), vEntryBlocks.size()); } /////////////////////////////////////////////////////////////////////////////// CPackedStore* g_pPackedStore = new CPackedStore(); diff --git a/r5dev/vpklib/packedstore.h b/r5dev/vpklib/packedstore.h index 5feab579..f64b4e43 100644 --- a/r5dev/vpklib/packedstore.h +++ b/r5dev/vpklib/packedstore.h @@ -94,8 +94,8 @@ struct VPKDir_t vector<string> m_vsvArchives {}; // Vector of archive file names. string m_svDirPath {}; // Path to vpk_dir file. - VPKDir_t() { m_vHeader.m_nHeaderMarker = VPK_HEADER_MARKER; m_vHeader.m_nMajorVersion = VPK_MAJOR_VERSION; m_vHeader.m_nMinorVersion = VPK_MINOR_VERSION; }; VPKDir_t(const string& svPath); + VPKDir_t() { m_vHeader.m_nHeaderMarker = VPK_HEADER_MARKER; m_vHeader.m_nMajorVersion = VPK_MAJOR_VERSION; m_vHeader.m_nMinorVersion = VPK_MINOR_VERSION; }; void Build(const string& svDirectoryFile, const vector<VPKEntryBlock_t>& vEntryBlocks); }; @@ -128,7 +128,8 @@ public: vector<VPKEntryBlock_t> GetEntryBlocks(CIOStream* reader) const; vector<string> GetEntryPaths(const string& svPathIn) const; vector<string> GetEntryPaths(const string& svPathIn, const nlohmann::json& jManifest) const; - string GetLevelName(const string& svDirectoryName) const; + string GetNameParts(const string& svDirectoryName, int nCaptureGroup) const; + string GetSourceName(const string& svDirectoryName) const; nlohmann::json GetManifest(const string& svWorkSpace, const string& svManifestName) const; string FormatBlockPath(string svName, const string& svPath, const string& svExtension) const; diff --git a/r5dev/vstdlib/callback.cpp b/r5dev/vstdlib/callback.cpp index c6690544..357229b3 100644 --- a/r5dev/vstdlib/callback.cpp +++ b/r5dev/vstdlib/callback.cpp @@ -309,7 +309,7 @@ void Host_Unban_f(const CCommand& args) } catch (std::exception& e) { - Error(eDLL_T::SERVER, "Unban Error: %s", e.what()); + Error(eDLL_T::SERVER, "Unban error: %s", e.what()); return; } }