Skip to content

Commit 6033402

Browse files
Xenius97qaisjp
authored andcommitted
Initial commit
1 parent 5d1bd07 commit 6033402

3 files changed

Lines changed: 273 additions & 7 deletions

File tree

Client/mods/deathmatch/logic/CClientIMG.cpp

Lines changed: 244 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ struct tImgHeader
1818
};
1919

2020
CClientIMG::CClientIMG(class CClientManager* pManager, ElementID ID)
21-
: ClassInit(this), CClientEntity(ID), m_pImgManager(pManager->GetIMGManager()), m_ucArchiveID(INVALID_ARCHIVE_ID), m_LargestFileSizeBlocks(0)
21+
: ClassInit(this),
22+
CClientEntity(ID),
23+
m_pImgManager(pManager->GetIMGManager()),
24+
m_ucArchiveID(INVALID_ARCHIVE_ID),
25+
m_LargestFileSizeBlocks(0),
26+
m_bFolderMode(false),
27+
m_bTempImgCreated(false)
2228
{
2329
m_pManager = pManager;
2430
SetTypeName("img");
@@ -36,7 +42,7 @@ void CClientIMG::Unlink()
3642
if (IsStreamed())
3743
StreamDisable();
3844

39-
if (m_ifs.is_open())
45+
if (m_ifs.is_open() || m_bFolderMode)
4046
Unload();
4147
}
4248

@@ -101,6 +107,39 @@ void CClientIMG::Unload()
101107
m_fileInfos.clear();
102108
m_fileInfos.shrink_to_fit();
103109
m_ifs.close();
110+
111+
if (m_bFolderMode)
112+
{
113+
m_folderFilePaths.clear();
114+
m_folderFilePaths.shrink_to_fit();
115+
116+
if (m_bTempImgCreated && !m_tempImgPath.empty())
117+
{
118+
std::error_code ec;
119+
fs::remove(m_tempImgPath, ec);
120+
m_tempImgPath.clear();
121+
}
122+
123+
m_bTempImgCreated = false;
124+
m_bFolderMode = false;
125+
}
126+
127+
if (!m_tempImgDirPath.empty())
128+
{
129+
std::error_code ec;
130+
for (const auto& entry : fs::directory_iterator(m_tempImgDirPath, ec))
131+
{
132+
if (ec)
133+
break;
134+
135+
if (!entry.is_regular_file())
136+
continue;
137+
138+
const auto filename = entry.path().filename().string();
139+
if (filename.find("runtime-image") != std::string::npos)
140+
fs::remove(entry.path(), ec);
141+
}
142+
}
104143
}
105144

106145
bool CClientIMG::GetFile(size_t fileID, std::string& buffer)
@@ -120,6 +159,17 @@ bool CClientIMG::GetFile(size_t fileID, std::string& buffer)
120159
throw std::invalid_argument("Out of memory");
121160
}
122161

162+
if (!m_ifs.is_open())
163+
{
164+
m_ifs = std::ifstream(m_filePath, std::ios::binary);
165+
if (m_ifs.fail())
166+
{
167+
m_ifs.close();
168+
return false;
169+
}
170+
}
171+
172+
m_ifs.clear();
123173
m_ifs.seekg((std::streampos)pFileInfo->uiOffset * 2048);
124174
m_ifs.read(buffer.data(), toReadBytes);
125175

@@ -148,6 +198,192 @@ bool CClientIMG::IsStreamed()
148198
return m_ucArchiveID != INVALID_ARCHIVE_ID;
149199
}
150200

201+
bool CClientIMG::LoadFolder(fs::path folderPath, fs::path tempDirPath)
202+
{
203+
if (!m_fileInfos.empty())
204+
return false;
205+
206+
if (folderPath.empty())
207+
return false;
208+
209+
if (!fs::is_directory(folderPath))
210+
return false;
211+
212+
m_tempImgDirPath = std::move(tempDirPath);
213+
214+
if (!m_tempImgDirPath.empty())
215+
{
216+
std::error_code ec;
217+
fs::create_directories(m_tempImgDirPath, ec);
218+
if (ec)
219+
return false;
220+
221+
for (const auto& entry : fs::directory_iterator(m_tempImgDirPath, ec))
222+
{
223+
if (ec)
224+
break;
225+
226+
if (!entry.is_regular_file())
227+
continue;
228+
229+
const auto filename = entry.path().filename().string();
230+
if (filename.find("runtime-image") != std::string::npos)
231+
fs::remove(entry.path(), ec);
232+
}
233+
}
234+
235+
// Collect regular files and sort alphabetically for deterministic ordering
236+
std::vector<fs::directory_entry> entries;
237+
for (const auto& entry : fs::directory_iterator(folderPath))
238+
{
239+
if (entry.is_regular_file())
240+
entries.push_back(entry);
241+
}
242+
std::sort(entries.begin(), entries.end(), [](const auto& a, const auto& b) { return a.path().filename() < b.path().filename(); });
243+
244+
CResourceManager* pResourceManager = m_pManager ? m_pManager->GetResourceManager() : nullptr;
245+
246+
unsigned int uiCurrentOffset = 0;
247+
for (const auto& entry : entries)
248+
{
249+
const auto& filePath = entry.path();
250+
const auto filename = filePath.filename().string();
251+
252+
// tImgFileInfo::szFileName is 24 bytes (23 usable chars + null terminator)
253+
if (filename.size() > 23)
254+
continue;
255+
256+
if (pResourceManager)
257+
{
258+
SString strFilePath = SharedUtil::ToUTF8(filePath.wstring());
259+
SString strConformed = PathConform(strFilePath).ToLower();
260+
261+
// Include only files known to the resource download manager
262+
if (!pResourceManager->GetDownloadableResourceFile(strConformed))
263+
continue;
264+
}
265+
266+
std::error_code ec;
267+
const auto fileSize = entry.file_size(ec);
268+
if (ec || fileSize == 0)
269+
continue;
270+
271+
const auto sizeInBlocks = static_cast<unsigned short>((fileSize + 2047) / 2048);
272+
273+
tImgFileInfo fileInfo{};
274+
fileInfo.uiOffset = uiCurrentOffset;
275+
fileInfo.usSize = sizeInBlocks;
276+
fileInfo.usUnpackedSize = 0;
277+
std::strncpy(fileInfo.szFileName, filename.c_str(), 23);
278+
fileInfo.szFileName[23] = '\0';
279+
280+
m_fileInfos.push_back(fileInfo);
281+
m_folderFilePaths.push_back(filePath);
282+
283+
uiCurrentOffset += sizeInBlocks;
284+
}
285+
286+
if (m_fileInfos.empty())
287+
return false;
288+
289+
m_bFolderMode = true;
290+
return true;
291+
}
292+
293+
bool CClientIMG::BuildTempIMG()
294+
{
295+
if (m_tempImgDirPath.empty())
296+
return false;
297+
298+
std::error_code ec;
299+
fs::create_directories(m_tempImgDirPath, ec);
300+
if (ec)
301+
return false;
302+
303+
const unsigned long long base = static_cast<unsigned long long>(GetTickCount64_());
304+
const unsigned long long pid = static_cast<unsigned long long>(GetCurrentProcessId());
305+
const std::wstring fileName = L"runtime-image-" + std::to_wstring(base) + L"-" + std::to_wstring(pid) + L".tmp";
306+
m_tempImgPath = m_tempImgDirPath / fileName;
307+
308+
ec.clear();
309+
if (fs::exists(m_tempImgPath, ec) || ec)
310+
return false;
311+
312+
std::ofstream ofs(m_tempImgPath, std::ios::binary | std::ios::trunc);
313+
if (!ofs.is_open())
314+
{
315+
fs::remove(m_tempImgPath);
316+
return false;
317+
}
318+
319+
std::string readBuf;
320+
std::vector<char> zeroPad;
321+
322+
for (size_t i = 0; i < m_folderFilePaths.size(); i++)
323+
{
324+
const auto& filePath = m_folderFilePaths[i];
325+
const auto& fileInfo = m_fileInfos[i];
326+
327+
std::error_code ec;
328+
const auto fileSize = fs::file_size(filePath, ec);
329+
if (ec)
330+
{
331+
ofs.close();
332+
fs::remove(m_tempImgPath);
333+
return false;
334+
}
335+
336+
try
337+
{
338+
readBuf.resize(fileSize);
339+
}
340+
catch (const std::bad_alloc&)
341+
{
342+
ofs.close();
343+
fs::remove(m_tempImgPath);
344+
return false;
345+
}
346+
347+
std::ifstream ifs(filePath, std::ios::binary);
348+
if (!ifs.is_open())
349+
{
350+
ofs.close();
351+
fs::remove(m_tempImgPath);
352+
return false;
353+
}
354+
355+
ifs.read(readBuf.data(), fileSize);
356+
if (ifs.fail())
357+
{
358+
ofs.close();
359+
fs::remove(m_tempImgPath);
360+
return false;
361+
}
362+
363+
ofs.write(readBuf.data(), fileSize);
364+
365+
const size_t paddedSize = static_cast<size_t>(fileInfo.usSize) * 2048;
366+
const size_t paddingNeeded = paddedSize - fileSize;
367+
if (paddingNeeded > 0)
368+
{
369+
zeroPad.assign(paddingNeeded, '\0');
370+
ofs.write(zeroPad.data(), paddingNeeded);
371+
}
372+
373+
if (ofs.fail())
374+
{
375+
ofs.close();
376+
fs::remove(m_tempImgPath);
377+
return false;
378+
}
379+
}
380+
381+
ofs.close();
382+
m_filePath = m_tempImgPath;
383+
m_bTempImgCreated = true;
384+
return true;
385+
}
386+
151387
bool CClientIMG::StreamEnable()
152388
{
153389
if (m_fileInfos.empty())
@@ -156,6 +392,12 @@ bool CClientIMG::StreamEnable()
156392
if (IsStreamed())
157393
return false;
158394

395+
if (m_bFolderMode && !m_bTempImgCreated)
396+
{
397+
if (!BuildTempIMG())
398+
return false;
399+
}
400+
159401
if (m_LargestFileSizeBlocks == 0)
160402
{
161403
for (const auto& fileInfo : m_fileInfos)

Client/mods/deathmatch/logic/CClientIMG.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class CClientIMG : public CClientEntity
6161
auto GetLargestFileSizeBlocks() const { return m_LargestFileSizeBlocks; }
6262

6363
bool Load(fs::path filePath);
64+
bool LoadFolder(fs::path folderPath, fs::path tempDirPath = {});
6465
void Unload();
6566

6667
tImgFileInfo* GetFileInfo(size_t fileID);
@@ -70,11 +71,14 @@ class CClientIMG : public CClientEntity
7071
bool StreamEnable();
7172
bool StreamDisable();
7273
bool IsStreamed();
74+
bool IsFolderMode() const noexcept { return m_bFolderMode; }
7375

7476
bool LinkModel(unsigned int usModelID, size_t fileID);
7577
bool UnlinkModel(unsigned int usModelID);
7678

7779
private:
80+
bool BuildTempIMG();
81+
7882
class CClientIMGManager* m_pImgManager;
7983

8084
std::ifstream m_ifs;
@@ -83,5 +87,11 @@ class CClientIMG : public CClientEntity
8387
std::vector<tImgFileInfo> m_fileInfos;
8488
size_t m_LargestFileSizeBlocks; // The size of the largest file [in streaming blocks/sectors]
8589

90+
bool m_bFolderMode;
91+
bool m_bTempImgCreated;
92+
fs::path m_tempImgPath;
93+
fs::path m_tempImgDirPath;
94+
std::vector<fs::path> m_folderFilePaths;
95+
8696
std::vector<tLinkedModelRestoreInfo> m_restoreInfo;
8797
};

Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -696,21 +696,35 @@ CClientIMG* CLuaEngineDefs::EngineLoadIMG(lua_State* const luaVM, std::string st
696696
// Create the img handle
697697
CClientIMG* pImg = new CClientIMG(m_pManager, INVALID_ELEMENT_ID);
698698

699-
// Fix path encoding for sdt::filesystem
699+
// Fix path encoding for std::filesystem
700700
std::wstring utf8Path = SharedUtil::MbUTF8ToUTF16(strFullPath);
701701

702-
// Attempt loading the file
703-
if (pImg->Load(std::move(utf8Path)))
702+
std::error_code ec;
703+
const bool bIsFolder = std::filesystem::is_directory(utf8Path, ec) && !ec;
704+
705+
// Detect if this is a folder IMG or a regular file IMG and call the appropriate loading function.
706+
bool bLoaded = false;
707+
if (bIsFolder)
708+
{
709+
const SString strResourceRoot = pResource->GetResourceDirectoryPath();
710+
const std::filesystem::path resourceRootPath = SharedUtil::MbUTF8ToUTF16(strResourceRoot);
711+
bLoaded = pImg->LoadFolder(std::move(utf8Path), resourceRootPath);
712+
}
713+
else
714+
{
715+
bLoaded = pImg->Load(std::move(utf8Path));
716+
}
717+
718+
if (bLoaded)
704719
{
705720
// Success. Make it a child of the resource img root
706721
pImg->SetParent(pRoot);
707-
708722
return pImg;
709723
}
710724
else
711725
{
712726
delete pImg;
713-
throw std::invalid_argument("Error loading IMG");
727+
throw std::invalid_argument(bIsFolder ? "Error creating runtime IMG from folder" : "Error loading IMG");
714728
}
715729
}
716730

0 commit comments

Comments
 (0)