mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2025-12-31 00:10:26 -06:00
199 lines
5.7 KiB
C++
199 lines
5.7 KiB
C++
// Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/disc_image_device.cc
|
|
|
|
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2023 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "iso_file_system.h"
|
|
|
|
#include <stack>
|
|
|
|
ISOFileSystem::ISOFileSystem(const std::filesystem::path &isoPath)
|
|
{
|
|
mappedFile.open(isoPath);
|
|
if (!mappedFile.isOpen())
|
|
{
|
|
return;
|
|
}
|
|
|
|
name = (const char *)(isoPath.filename().u8string().data());
|
|
|
|
// Find root sector.
|
|
const uint8_t *mappedFileData = mappedFile.data();
|
|
uint32_t gameOffset = 0;
|
|
const size_t XeSectorSize = 2048;
|
|
static const size_t PossibleOffsets[] = { 0x00000000, 0x0000FB20, 0x00020600, 0x02080000, 0x0FD90000, };
|
|
bool magicFound = false;
|
|
const char RefMagic[] = "MICROSOFT*XBOX*MEDIA";
|
|
for (size_t i = 0; i < std::size(PossibleOffsets); i++)
|
|
{
|
|
size_t fileOffset = PossibleOffsets[i] + (32 * XeSectorSize);
|
|
if ((fileOffset + strlen(RefMagic)) > mappedFile.size())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (std::memcmp(&mappedFileData[fileOffset], RefMagic, strlen(RefMagic)) == 0)
|
|
{
|
|
gameOffset = PossibleOffsets[i];
|
|
magicFound = true;
|
|
}
|
|
}
|
|
|
|
size_t rootInfoOffset = gameOffset + (32 * XeSectorSize) + 20;
|
|
if (!magicFound || (rootInfoOffset + 8) > mappedFile.size())
|
|
{
|
|
mappedFile.close();
|
|
return;
|
|
}
|
|
|
|
// Parse root information.
|
|
uint32_t rootSector = *(uint32_t *)(&mappedFileData[rootInfoOffset + 0]);
|
|
uint32_t rootSize = *(uint32_t *)(&mappedFileData[rootInfoOffset + 4]);
|
|
size_t rootOffset = gameOffset + (rootSector * XeSectorSize);
|
|
const uint32_t MinRootSize = 13;
|
|
const uint32_t MaxRootSize = 32 * 1024 * 1024;
|
|
if ((rootSize < MinRootSize) || (rootSize > MaxRootSize))
|
|
{
|
|
mappedFile.close();
|
|
return;
|
|
}
|
|
|
|
struct IterationStep
|
|
{
|
|
std::string fileNameBase;
|
|
size_t nodeOffset = 0;
|
|
size_t entryOffset = 0;
|
|
|
|
IterationStep() = default;
|
|
IterationStep(std::string fileNameBase, size_t nodeOffset, size_t entryOffset) : fileNameBase(fileNameBase), nodeOffset(nodeOffset), entryOffset(entryOffset) { }
|
|
};
|
|
|
|
std::stack<IterationStep> iterationStack;
|
|
iterationStack.emplace("", rootOffset, 0);
|
|
|
|
IterationStep step;
|
|
uint16_t nodeL, nodeR;
|
|
uint32_t sector, length;
|
|
uint8_t attributes, nameLength;
|
|
char fileName[256];
|
|
const uint8_t FileAttributeDirectory = 0x10;
|
|
while (!iterationStack.empty())
|
|
{
|
|
step = iterationStack.top();
|
|
iterationStack.pop();
|
|
|
|
size_t infoOffset = step.nodeOffset + step.entryOffset;
|
|
if ((infoOffset + 14) > mappedFile.size())
|
|
{
|
|
mappedFile.close();
|
|
return;
|
|
}
|
|
|
|
nodeL = *(uint16_t *)(&mappedFileData[infoOffset + 0]);
|
|
nodeR = *(uint16_t *)(&mappedFileData[infoOffset + 2]);
|
|
sector = *(uint32_t *)(&mappedFileData[infoOffset + 4]);
|
|
length = *(uint32_t *)(&mappedFileData[infoOffset + 8]);
|
|
attributes = *(uint8_t *)(&mappedFileData[infoOffset + 12]);
|
|
nameLength = *(uint8_t *)(&mappedFileData[infoOffset + 13]);
|
|
|
|
size_t nameOffset = infoOffset + 14;
|
|
if ((nameOffset + nameLength) > mappedFile.size())
|
|
{
|
|
mappedFile.close();
|
|
return;
|
|
}
|
|
|
|
memcpy(fileName, &mappedFileData[nameOffset], nameLength);
|
|
fileName[nameLength] = '\0';
|
|
|
|
if (nodeL)
|
|
{
|
|
iterationStack.emplace(step.fileNameBase, step.nodeOffset, nodeL * 4);
|
|
}
|
|
|
|
if (nodeR)
|
|
{
|
|
iterationStack.emplace(step.fileNameBase, step.nodeOffset, nodeR * 4);
|
|
}
|
|
|
|
std::string fileNameUTF8 = step.fileNameBase + fileName;
|
|
if (attributes & FileAttributeDirectory)
|
|
{
|
|
if (length > 0)
|
|
{
|
|
iterationStack.emplace(fileNameUTF8 + "/", gameOffset + sector * XeSectorSize, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fileMap[fileNameUTF8] = { gameOffset + sector * XeSectorSize, length};
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ISOFileSystem::load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const
|
|
{
|
|
auto it = fileMap.find(path);
|
|
if (it != fileMap.end())
|
|
{
|
|
if (fileDataMaxByteCount < std::get<1>(it->second))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const uint8_t *mappedFileData = mappedFile.data();
|
|
memcpy(fileData, &mappedFileData[std::get<0>(it->second)], std::get<1>(it->second));
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
size_t ISOFileSystem::getSize(const std::string &path) const
|
|
{
|
|
auto it = fileMap.find(path);
|
|
if (it != fileMap.end())
|
|
{
|
|
return std::get<1>(it->second);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool ISOFileSystem::exists(const std::string &path) const
|
|
{
|
|
return fileMap.find(path) != fileMap.end();
|
|
}
|
|
|
|
const std::string &ISOFileSystem::getName() const
|
|
{
|
|
return name;
|
|
}
|
|
|
|
bool ISOFileSystem::empty() const
|
|
{
|
|
return !mappedFile.isOpen();
|
|
}
|
|
|
|
std::unique_ptr<ISOFileSystem> ISOFileSystem::create(const std::filesystem::path &isoPath) {
|
|
std::unique_ptr<ISOFileSystem> isoFs = std::make_unique<ISOFileSystem>(isoPath);
|
|
if (!isoFs->empty())
|
|
{
|
|
return isoFs;
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|