2019-08-17 13:51:31 -04:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cassert>
|
2019-10-29 12:47:03 -04:00
|
|
|
#include <cstring>
|
2015-07-23 22:48:01 -04:00
|
|
|
#include "make_unique.h"
|
2015-07-22 00:30:06 -04:00
|
|
|
#include "stricmp.h"
|
|
|
|
#include "DiskUtils.h"
|
2021-07-16 10:54:45 -04:00
|
|
|
#include "discimages/ChdImageStream.h"
|
2020-10-03 16:59:18 -04:00
|
|
|
#include "discimages/CsoImageStream.h"
|
2020-10-04 09:32:55 -04:00
|
|
|
#include "discimages/CueSheet.h"
|
2020-10-04 09:28:53 -04:00
|
|
|
#include "discimages/IszImageStream.h"
|
2020-10-03 16:59:18 -04:00
|
|
|
#include "discimages/MdsDiscImage.h"
|
2015-07-22 00:30:06 -04:00
|
|
|
#include "StdStream.h"
|
2019-06-04 19:39:08 +01:00
|
|
|
#include "StringUtils.h"
|
2019-07-05 23:43:14 +01:00
|
|
|
#ifdef HAS_AMAZON_S3
|
2018-02-16 17:25:45 -05:00
|
|
|
#include "s3stream/S3ObjectStream.h"
|
2019-07-05 23:43:14 +01:00
|
|
|
#endif
|
2017-02-21 15:47:18 +00:00
|
|
|
#ifdef _WIN32
|
2015-07-22 00:30:06 -04:00
|
|
|
#include "VolumeStream.h"
|
|
|
|
#else
|
|
|
|
#include "Posix_VolumeStream.h"
|
|
|
|
#endif
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
#include "PosixFileStream.h"
|
2021-08-13 17:21:17 -04:00
|
|
|
#include "android/ContentStream.h"
|
2021-08-25 13:44:26 -04:00
|
|
|
#include "android/ContentUtils.h"
|
2015-07-22 00:30:06 -04:00
|
|
|
#endif
|
2022-01-09 15:02:45 -05:00
|
|
|
#ifdef __EMSCRIPTEN__
|
|
|
|
#include "Js_DiscImageDeviceStream.h"
|
|
|
|
#endif
|
2017-02-11 19:34:36 -05:00
|
|
|
#ifdef __APPLE__
|
|
|
|
#include "TargetConditionals.h"
|
|
|
|
#endif
|
2015-07-22 00:30:06 -04:00
|
|
|
|
2019-10-16 20:51:11 -04:00
|
|
|
static Framework::CStream* CreateImageStream(const fs::path& imagePath)
|
2015-07-22 00:30:06 -04:00
|
|
|
{
|
2019-10-26 12:28:49 -04:00
|
|
|
static const char* s3ImagePathPrefix = "//s3/";
|
2018-02-26 09:11:04 -05:00
|
|
|
auto imagePathString = imagePath.string();
|
2019-10-26 12:28:49 -04:00
|
|
|
if(imagePathString.find(s3ImagePathPrefix) == 0)
|
2018-02-26 09:11:04 -05:00
|
|
|
{
|
2019-07-05 23:43:14 +01:00
|
|
|
#ifdef HAS_AMAZON_S3
|
2019-10-26 12:28:49 -04:00
|
|
|
auto fullObjectPath = std::string(imagePathString.c_str() + strlen(s3ImagePathPrefix));
|
2020-12-14 13:42:18 +00:00
|
|
|
std::replace(fullObjectPath.begin(), fullObjectPath.end(), '\\', '/');
|
2018-02-28 09:11:30 -05:00
|
|
|
auto objectPathPos = fullObjectPath.find('/');
|
|
|
|
if(objectPathPos == std::string::npos)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Invalid S3 object path.");
|
|
|
|
}
|
|
|
|
auto bucketName = std::string(fullObjectPath.begin(), fullObjectPath.begin() + objectPathPos);
|
|
|
|
return new CS3ObjectStream(bucketName.c_str(), fullObjectPath.c_str() + objectPathPos + 1);
|
2019-07-05 23:43:14 +01:00
|
|
|
#else
|
|
|
|
throw std::runtime_error("S3 support was disabled during build configuration.");
|
|
|
|
#endif
|
2018-02-26 09:11:04 -05:00
|
|
|
}
|
2015-07-22 00:30:06 -04:00
|
|
|
#ifdef __ANDROID__
|
2021-08-25 13:44:26 -04:00
|
|
|
if(Framework::Android::CContentUtils::IsContentPath(imagePath))
|
2021-08-13 17:21:17 -04:00
|
|
|
{
|
2021-10-23 11:33:41 -04:00
|
|
|
auto uri = Framework::Android::CContentUtils::BuildUriFromPath(imagePath);
|
2021-08-25 10:28:20 -04:00
|
|
|
return new Framework::Android::CContentStream(uri.c_str(), "r");
|
2021-08-13 17:21:17 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return new Framework::CPosixFileStream(imagePathString.c_str(), O_RDONLY);
|
|
|
|
}
|
2022-01-09 15:02:45 -05:00
|
|
|
#elif defined(__EMSCRIPTEN__)
|
|
|
|
return new CJsDiscImageDeviceStream();
|
2015-07-22 00:30:06 -04:00
|
|
|
#else
|
2018-02-26 09:11:04 -05:00
|
|
|
return new Framework::CStdStream(imagePathString.c_str(), "rb");
|
2015-07-22 00:30:06 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-10-04 09:32:55 -04:00
|
|
|
static DiskUtils::OpticalMediaPtr CreateOpticalMediaFromCueSheet(const fs::path& imagePath)
|
|
|
|
{
|
|
|
|
auto currentPath = imagePath.parent_path();
|
|
|
|
auto imageStream = std::unique_ptr<Framework::CStream>(CreateImageStream(imagePath));
|
|
|
|
auto fileStream = std::shared_ptr<Framework::CStream>();
|
|
|
|
CCueSheet cueSheet(*imageStream);
|
|
|
|
for(const auto& command : cueSheet.GetCommands())
|
|
|
|
{
|
|
|
|
if(auto fileCommand = dynamic_cast<CCueSheet::COMMAND_FILE*>(command.get()))
|
|
|
|
{
|
|
|
|
assert(fileCommand->filetype == "BINARY");
|
|
|
|
auto filePath = currentPath / fileCommand->filename;
|
|
|
|
fileStream = std::shared_ptr<Framework::CStream>(CreateImageStream(filePath));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!fileStream)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Could not build media from cuesheet.");
|
|
|
|
}
|
|
|
|
return COpticalMedia::CreateAuto(fileStream);
|
|
|
|
}
|
|
|
|
|
2020-10-04 09:28:53 -04:00
|
|
|
static DiskUtils::OpticalMediaPtr CreateOpticalMediaFromMds(const fs::path& imagePath)
|
|
|
|
{
|
|
|
|
auto imageStream = std::unique_ptr<Framework::CStream>(CreateImageStream(imagePath));
|
|
|
|
auto discImage = CMdsDiscImage(*imageStream);
|
|
|
|
|
|
|
|
//Create image data path
|
|
|
|
auto imageDataPath = imagePath;
|
|
|
|
imageDataPath.replace_extension("mdf");
|
|
|
|
auto imageDataStream = std::shared_ptr<Framework::CStream>(CreateImageStream(imageDataPath));
|
|
|
|
|
|
|
|
return COpticalMedia::CreateDvd(imageDataStream, discImage.IsDualLayer(), discImage.GetLayerBreak());
|
|
|
|
}
|
|
|
|
|
2021-07-20 17:34:11 -04:00
|
|
|
static DiskUtils::OpticalMediaPtr CreateOpticalMediaFromChd(const fs::path& imagePath)
|
|
|
|
{
|
|
|
|
//Some notes about CHD support:
|
|
|
|
//- We don't support multi track CDs
|
|
|
|
auto imageStream = std::make_shared<CChdImageStream>(CreateImageStream(imagePath));
|
2022-04-29 10:03:58 -04:00
|
|
|
auto blockProvider = [&imageStream]() -> COpticalMedia::BlockProviderPtr {
|
|
|
|
switch(imageStream->GetTrack0Type())
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
[[fallthrough]];
|
|
|
|
case CChdImageStream::TRACK_TYPE_MODE1:
|
|
|
|
return std::make_shared<ISO9660::CBlockProviderCustom<0x990, 0>>(imageStream);
|
|
|
|
case CChdImageStream::TRACK_TYPE_MODE2_RAW:
|
|
|
|
return std::make_shared<ISO9660::CBlockProviderCustom<0x990, 0x18>>(imageStream);
|
|
|
|
}
|
|
|
|
}();
|
2021-07-20 17:34:11 -04:00
|
|
|
return COpticalMedia::CreateCustomSingleTrack(blockProvider);
|
|
|
|
}
|
|
|
|
|
2020-10-04 11:33:59 -04:00
|
|
|
const DiskUtils::ExtensionList& DiskUtils::GetSupportedExtensions()
|
|
|
|
{
|
2021-07-16 10:54:45 -04:00
|
|
|
static auto extensionList = ExtensionList{".iso", ".mds", ".isz", ".cso", ".cue", ".chd"};
|
2020-10-04 11:33:59 -04:00
|
|
|
return extensionList;
|
|
|
|
}
|
|
|
|
|
2020-10-01 15:04:15 -04:00
|
|
|
DiskUtils::OpticalMediaPtr DiskUtils::CreateOpticalMediaFromPath(const fs::path& imagePath, uint32 opticalMediaCreateFlags)
|
2015-07-22 00:30:06 -04:00
|
|
|
{
|
|
|
|
assert(!imagePath.empty());
|
|
|
|
|
|
|
|
std::shared_ptr<Framework::CStream> stream;
|
|
|
|
auto extension = imagePath.extension().string();
|
|
|
|
|
|
|
|
//Gotta think of something better than that...
|
|
|
|
if(!stricmp(extension.c_str(), ".isz"))
|
|
|
|
{
|
|
|
|
stream = std::make_shared<CIszImageStream>(CreateImageStream(imagePath));
|
|
|
|
}
|
2021-07-16 10:54:45 -04:00
|
|
|
else if(!stricmp(extension.c_str(), ".chd"))
|
|
|
|
{
|
2021-07-20 17:34:11 -04:00
|
|
|
return CreateOpticalMediaFromChd(imagePath);
|
2021-07-16 10:54:45 -04:00
|
|
|
}
|
2015-07-22 00:30:06 -04:00
|
|
|
else if(!stricmp(extension.c_str(), ".cso"))
|
|
|
|
{
|
|
|
|
stream = std::make_shared<CCsoImageStream>(CreateImageStream(imagePath));
|
|
|
|
}
|
2020-10-04 09:32:55 -04:00
|
|
|
else if(!stricmp(extension.c_str(), ".cue"))
|
|
|
|
{
|
|
|
|
return CreateOpticalMediaFromCueSheet(imagePath);
|
|
|
|
}
|
2018-10-12 17:04:31 -04:00
|
|
|
else if(!stricmp(extension.c_str(), ".mds"))
|
|
|
|
{
|
2020-10-04 09:28:53 -04:00
|
|
|
return CreateOpticalMediaFromMds(imagePath);
|
2018-10-12 17:04:31 -04:00
|
|
|
}
|
2017-02-21 15:47:18 +00:00
|
|
|
#ifdef _WIN32
|
2015-07-22 00:30:06 -04:00
|
|
|
else if(imagePath.string()[0] == '\\')
|
|
|
|
{
|
2019-10-02 13:07:03 -04:00
|
|
|
stream = std::make_shared<Framework::Win32::CVolumeStream>(imagePath.native().c_str());
|
2015-07-22 00:30:06 -04:00
|
|
|
}
|
|
|
|
#elif !defined(__ANDROID__) && !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
|
2017-08-22 22:40:55 -04:00
|
|
|
else if(imagePath.string().find("/dev/") == 0)
|
2015-07-22 00:30:06 -04:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2015-07-24 22:09:19 -04:00
|
|
|
stream = std::make_shared<Framework::Posix::CVolumeStream>(imagePath.string().c_str());
|
2015-07-22 00:30:06 -04:00
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
//Ok if it fails here, might be a standard ISO image file
|
|
|
|
//which will be handled below
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//If it's null after all that, just feed it to a StdStream
|
|
|
|
if(!stream)
|
|
|
|
{
|
|
|
|
stream = std::shared_ptr<Framework::CStream>(CreateImageStream(imagePath));
|
|
|
|
}
|
|
|
|
|
2020-10-01 15:57:55 -04:00
|
|
|
return COpticalMedia::CreateAuto(stream, opticalMediaCreateFlags);
|
2015-07-22 00:30:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
DiskUtils::SystemConfigMap DiskUtils::ParseSystemConfigFile(Framework::CStream* systemCnfFile)
|
|
|
|
{
|
|
|
|
SystemConfigMap result;
|
2018-02-07 14:23:21 -05:00
|
|
|
auto line = systemCnfFile->ReadLine();
|
2015-07-22 00:30:06 -04:00
|
|
|
while(!systemCnfFile->IsEOF())
|
|
|
|
{
|
|
|
|
auto trimmedEnd = std::remove_if(line.begin(), line.end(), isspace);
|
|
|
|
auto trimmedLine = std::string(line.begin(), trimmedEnd);
|
2019-07-01 11:39:19 +01:00
|
|
|
std::vector<std::string> components = StringUtils::Split(trimmedLine, '=', true);
|
2015-07-22 00:30:06 -04:00
|
|
|
if(components.size() >= 2)
|
|
|
|
{
|
|
|
|
result.insert(std::make_pair(components[0], components[1]));
|
|
|
|
}
|
2018-02-07 14:23:21 -05:00
|
|
|
line = systemCnfFile->ReadLine();
|
2015-07-22 00:30:06 -04:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string GetDiskIdFromPath(const std::string& filePath)
|
|
|
|
{
|
|
|
|
//Expecting something like SCUS_XXX.XX;1
|
|
|
|
if(filePath.length() < 13)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("File name too short");
|
|
|
|
}
|
|
|
|
|
|
|
|
auto subFilePath = filePath.substr(filePath.length() - 13);
|
|
|
|
auto regionCode = subFilePath.substr(0, 4);
|
|
|
|
auto serial1 = subFilePath.substr(5, 3);
|
|
|
|
auto serial2 = subFilePath.substr(9, 2);
|
2019-01-06 00:01:23 +00:00
|
|
|
return regionCode + "-" + serial1 + serial2;
|
2015-07-22 00:30:06 -04:00
|
|
|
}
|
|
|
|
|
2019-10-16 20:51:11 -04:00
|
|
|
bool DiskUtils::TryGetDiskId(const fs::path& imagePath, std::string* diskIdPtr)
|
2015-07-22 00:30:06 -04:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2020-10-01 15:04:15 -04:00
|
|
|
auto opticalMedia = CreateOpticalMediaFromPath(imagePath, COpticalMedia::CREATE_AUTO_DISABLE_DL_DETECT);
|
2017-05-16 19:54:38 -04:00
|
|
|
auto fileSystem = opticalMedia->GetFileSystem();
|
|
|
|
auto systemConfigFile = std::unique_ptr<Framework::CStream>(fileSystem->Open("SYSTEM.CNF;1"));
|
2015-07-22 00:30:06 -04:00
|
|
|
if(!systemConfigFile) return false;
|
|
|
|
|
|
|
|
auto systemConfig = ParseSystemConfigFile(systemConfigFile.get());
|
|
|
|
auto bootItemIterator = systemConfig.find("BOOT2");
|
|
|
|
if(bootItemIterator == std::end(systemConfig)) return false;
|
|
|
|
|
|
|
|
auto diskId = GetDiskIdFromPath(bootItemIterator->second);
|
|
|
|
if(diskIdPtr)
|
|
|
|
{
|
|
|
|
(*diskIdPtr) = diskId;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch(const std::exception&)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|