mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-12-14 03:59:36 +00:00
Fix up some file handling, add support for initializing a house file
This commit is contained in:
@@ -11,11 +11,14 @@
|
|||||||
#include "PLPasStr.h"
|
#include "PLPasStr.h"
|
||||||
#include "PLResources.h"
|
#include "PLResources.h"
|
||||||
#include "PLSound.h"
|
#include "PLSound.h"
|
||||||
|
#include "PLSysCalls.h"
|
||||||
#include "DialogUtils.h"
|
#include "DialogUtils.h"
|
||||||
#include "Externs.h"
|
#include "Externs.h"
|
||||||
#include "FileManager.h"
|
#include "FileManager.h"
|
||||||
|
#include "HostFileSystem.h"
|
||||||
#include "House.h"
|
#include "House.h"
|
||||||
#include "RectUtils.h"
|
#include "RectUtils.h"
|
||||||
|
#include "ResourceManager.h"
|
||||||
|
|
||||||
|
|
||||||
#define kGoToDialogID 1043
|
#define kGoToDialogID 1043
|
||||||
@@ -51,32 +54,23 @@ Boolean CreateNewHouse (void)
|
|||||||
AEKeyword theKeyword;
|
AEKeyword theKeyword;
|
||||||
DescType actualType;
|
DescType actualType;
|
||||||
Size actualSize;
|
Size actualSize;
|
||||||
NavReplyRecord theReply;
|
|
||||||
NavDialogOptions dialogOptions;
|
|
||||||
VFileSpec tempSpec;
|
VFileSpec tempSpec;
|
||||||
VFileSpec theSpec;
|
VFileSpec theSpec;
|
||||||
PLError_t theErr;
|
PLError_t theErr;
|
||||||
|
|
||||||
theErr = NavGetDefaultDialogOptions(&dialogOptions);
|
|
||||||
theErr = NavPutFile(nil, &theReply, &dialogOptions, nil, 'gliH', 'ozm5', nil);
|
|
||||||
if (theErr == PLErrors::kUserCancelled_TEMP)
|
|
||||||
return false;
|
|
||||||
if (!theReply.validRecord)
|
|
||||||
return (false);
|
|
||||||
|
|
||||||
theErr = AEGetNthPtr(&(theReply.selection), 1, typeFSS, &theKeyword,
|
|
||||||
&actualType, &theSpec, sizeof(VFileSpec), &actualSize);
|
|
||||||
|
|
||||||
PortabilityLayer::FileManager *fm = PortabilityLayer::FileManager::GetInstance();
|
PortabilityLayer::FileManager *fm = PortabilityLayer::FileManager::GetInstance();
|
||||||
|
|
||||||
if (theReply.replacing)
|
theSpec.m_dir = PortabilityLayer::VirtualDirectories::kUserData;
|
||||||
{
|
PasStringCopy(PSTR("My House"), theSpec.m_name);
|
||||||
if (fm->FileExists(theSpec.m_dir, theSpec.m_name))
|
|
||||||
{
|
|
||||||
CheckFileError(PLErrors::kFileNotFound, theSpec.m_name);
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
char savePath[sizeof(theSpec.m_name) + 1];
|
||||||
|
size_t savePathLength = 0;
|
||||||
|
|
||||||
|
if (!fm->PromptSaveFile(theSpec.m_dir, savePath, savePathLength, sizeof(theSpec.m_name), PSTR("My House")))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (fm->FileExists(theSpec.m_dir, theSpec.m_name))
|
||||||
|
{
|
||||||
if (!fm->DeleteFile(theSpec.m_dir, theSpec.m_name))
|
if (!fm->DeleteFile(theSpec.m_dir, theSpec.m_name))
|
||||||
{
|
{
|
||||||
CheckFileError(PLErrors::kAccessDenied, theSpec.m_name);
|
CheckFileError(PLErrors::kAccessDenied, theSpec.m_name);
|
||||||
@@ -94,7 +88,7 @@ Boolean CreateNewHouse (void)
|
|||||||
if (!CheckFileError(theErr, PSTR("New House")))
|
if (!CheckFileError(theErr, PSTR("New House")))
|
||||||
return (false);
|
return (false);
|
||||||
|
|
||||||
theErr = HCreateResFile(theSpec.m_dir, theSpec.m_name);
|
theErr = PortabilityLayer::ResourceManager::GetInstance()->CreateBlankResFile(theSpec.m_dir, theSpec.m_name);
|
||||||
if (theErr != PLErrors::kNone)
|
if (theErr != PLErrors::kNone)
|
||||||
YellowAlert(kYellowFailedResCreate, theErr);
|
YellowAlert(kYellowFailedResCreate, theErr);
|
||||||
|
|
||||||
|
|||||||
@@ -572,8 +572,9 @@ void DoDirSearch (void)
|
|||||||
for (i = 0; i < kMaxDirectories; i++)
|
for (i = 0; i < kMaxDirectories; i++)
|
||||||
theDirs[i] = PortabilityLayer::VirtualDirectories::kUnspecified;
|
theDirs[i] = PortabilityLayer::VirtualDirectories::kUnspecified;
|
||||||
currentDir = 0;
|
currentDir = 0;
|
||||||
theDirs[currentDir] = PortabilityLayer::VirtualDirectories::kGameData;
|
theDirs[0] = PortabilityLayer::VirtualDirectories::kGameData;
|
||||||
numDirs = 1;
|
theDirs[1] = PortabilityLayer::VirtualDirectories::kUserData;
|
||||||
|
numDirs = 2;
|
||||||
|
|
||||||
PortabilityLayer::FileManager *fm = PortabilityLayer::FileManager::GetInstance();
|
PortabilityLayer::FileManager *fm = PortabilityLayer::FileManager::GetInstance();
|
||||||
|
|
||||||
|
|||||||
15
GpCommon/GpFileCreationDisposition.h
Normal file
15
GpCommon/GpFileCreationDisposition.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace GpFileCreationDispositions
|
||||||
|
{
|
||||||
|
enum GpFileCreationDisposition
|
||||||
|
{
|
||||||
|
kCreateOrOverwrite, // If exists: Overwrite. If not exists: Create.
|
||||||
|
kCreateNew, // If exists: Fail. If not exists: Create.
|
||||||
|
kCreateOrOpen, // If exists: Open. If not exists: Create.
|
||||||
|
kOpenExisting, // If exists: Open. If not exists: Fail.
|
||||||
|
kOverwriteExisting, // If exists: Overwrite. If not exists: Fail.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef GpFileCreationDispositions::GpFileCreationDisposition GpFileCreationDisposition_t;
|
||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
#undef CreateMutex
|
#undef CreateMutex
|
||||||
|
#undef DeleteFile
|
||||||
|
|
||||||
|
|
||||||
struct IGpFiber;
|
struct IGpFiber;
|
||||||
struct IGpColorCursor_Win32;
|
struct IGpColorCursor_Win32;
|
||||||
|
|||||||
@@ -170,6 +170,7 @@
|
|||||||
<ClInclude Include="..\GpCommon\EGpInputDriverType.h" />
|
<ClInclude Include="..\GpCommon\EGpInputDriverType.h" />
|
||||||
<ClInclude Include="..\GpCommon\EGpStandardCursor.h" />
|
<ClInclude Include="..\GpCommon\EGpStandardCursor.h" />
|
||||||
<ClInclude Include="..\GpCommon\GpDisplayDriverTickStatus.h" />
|
<ClInclude Include="..\GpCommon\GpDisplayDriverTickStatus.h" />
|
||||||
|
<ClInclude Include="..\GpCommon\GpFileCreationDisposition.h" />
|
||||||
<ClInclude Include="..\GpCommon\GpInputDriverProperties.h" />
|
<ClInclude Include="..\GpCommon\GpInputDriverProperties.h" />
|
||||||
<ClInclude Include="..\GpCommon\IGpColorCursor.h" />
|
<ClInclude Include="..\GpCommon\IGpColorCursor.h" />
|
||||||
<ClInclude Include="..\GpCommon\IGpAudioChannelCallbacks.h" />
|
<ClInclude Include="..\GpCommon\IGpAudioChannelCallbacks.h" />
|
||||||
|
|||||||
@@ -194,6 +194,9 @@
|
|||||||
<ClInclude Include="..\GpCommon\GpDisplayDriverTickStatus.h">
|
<ClInclude Include="..\GpCommon\GpDisplayDriverTickStatus.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\GpCommon\GpFileCreationDisposition.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="GpD3D.rc">
|
<ResourceCompile Include="GpD3D.rc">
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <Shlwapi.h>
|
#include <Shlwapi.h>
|
||||||
#include <ShlObj.h>
|
#include <ShlObj.h>
|
||||||
|
#include <commdlg.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
class GpDirectoryCursor_Win32 final : public PortabilityLayer::HostDirectoryCursor
|
class GpDirectoryCursor_Win32 final : public PortabilityLayer::HostDirectoryCursor
|
||||||
@@ -112,12 +114,17 @@ GpFileSystem_Win32::GpFileSystem_Win32()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_prefsDir.append(L"\\GlidePort");
|
m_prefsDir.append(L"\\GlidePort");
|
||||||
|
|
||||||
|
m_userHousesDir = m_prefsDir + L"\\Houses";
|
||||||
m_scoresDir = m_prefsDir + L"\\Scores";
|
m_scoresDir = m_prefsDir + L"\\Scores";
|
||||||
|
|
||||||
CreateDirectoryW(m_prefsDir.c_str(), nullptr);
|
CreateDirectoryW(m_prefsDir.c_str(), nullptr);
|
||||||
CreateDirectoryW(m_scoresDir.c_str(), nullptr);
|
CreateDirectoryW(m_scoresDir.c_str(), nullptr);
|
||||||
|
CreateDirectoryW(m_userHousesDir.c_str(), nullptr);
|
||||||
|
|
||||||
m_prefsDir.append(L"\\");
|
m_prefsDir.append(L"\\");
|
||||||
m_scoresDir.append(L"\\");
|
m_scoresDir.append(L"\\");
|
||||||
|
m_userHousesDir.append(L"\\");
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD modulePathSize = GetModuleFileNameW(nullptr, m_executablePath, MAX_PATH);
|
DWORD modulePathSize = GetModuleFileNameW(nullptr, m_executablePath, MAX_PATH);
|
||||||
@@ -192,7 +199,7 @@ bool GpFileSystem_Win32::FileLocked(PortabilityLayer::VirtualDirectory_t virtual
|
|||||||
return (attribs & FILE_ATTRIBUTE_READONLY) != 0;
|
return (attribs & FILE_ATTRIBUTE_READONLY) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PortabilityLayer::IOStream *GpFileSystem_Win32::OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, bool create)
|
PortabilityLayer::IOStream *GpFileSystem_Win32::OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition)
|
||||||
{
|
{
|
||||||
wchar_t winPath[MAX_PATH + 1];
|
wchar_t winPath[MAX_PATH + 1];
|
||||||
|
|
||||||
@@ -200,15 +207,58 @@ PortabilityLayer::IOStream *GpFileSystem_Win32::OpenFile(PortabilityLayer::Virtu
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
const DWORD desiredAccess = writeAccess ? (GENERIC_WRITE | GENERIC_READ) : GENERIC_READ;
|
const DWORD desiredAccess = writeAccess ? (GENERIC_WRITE | GENERIC_READ) : GENERIC_READ;
|
||||||
const DWORD creationDisposition = create ? OPEN_ALWAYS : OPEN_EXISTING;
|
DWORD winCreationDisposition = 0;
|
||||||
|
|
||||||
HANDLE h = CreateFileW(winPath, desiredAccess, FILE_SHARE_READ, nullptr, creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
|
switch (createDisposition)
|
||||||
|
{
|
||||||
|
case GpFileCreationDispositions::kCreateOrOverwrite:
|
||||||
|
winCreationDisposition = CREATE_ALWAYS;
|
||||||
|
break;
|
||||||
|
case GpFileCreationDispositions::kCreateNew:
|
||||||
|
winCreationDisposition = CREATE_NEW;
|
||||||
|
break;
|
||||||
|
case GpFileCreationDispositions::kCreateOrOpen:
|
||||||
|
winCreationDisposition = OPEN_ALWAYS;
|
||||||
|
break;
|
||||||
|
case GpFileCreationDispositions::kOpenExisting:
|
||||||
|
winCreationDisposition = OPEN_EXISTING;
|
||||||
|
break;
|
||||||
|
case GpFileCreationDispositions::kOverwriteExisting:
|
||||||
|
winCreationDisposition = TRUNCATE_EXISTING;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE h = CreateFileW(winPath, desiredAccess, FILE_SHARE_READ, nullptr, winCreationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
if (h == INVALID_HANDLE_VALUE)
|
if (h == INVALID_HANDLE_VALUE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return new GpFileStream_Win32(h, true, writeAccess, true);
|
return new GpFileStream_Win32(h, true, writeAccess, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GpFileSystem_Win32::DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed)
|
||||||
|
{
|
||||||
|
wchar_t winPath[MAX_PATH + 1];
|
||||||
|
|
||||||
|
if (!ResolvePath(virtualDirectory, path, winPath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (DeleteFileW(winPath))
|
||||||
|
{
|
||||||
|
existed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD err = GetLastError();
|
||||||
|
if (err == ERROR_FILE_NOT_FOUND)
|
||||||
|
existed = false;
|
||||||
|
else
|
||||||
|
existed = true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
PortabilityLayer::HostDirectoryCursor *GpFileSystem_Win32::ScanDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory)
|
PortabilityLayer::HostDirectoryCursor *GpFileSystem_Win32::ScanDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory)
|
||||||
{
|
{
|
||||||
wchar_t winPath[MAX_PATH + 2];
|
wchar_t winPath[MAX_PATH + 2];
|
||||||
@@ -225,6 +275,118 @@ PortabilityLayer::HostDirectoryCursor *GpFileSystem_Win32::ScanDirectory(Portabi
|
|||||||
return GpDirectoryCursor_Win32::Create(ff, findData);
|
return GpDirectoryCursor_Win32::Create(ff, findData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GpFileSystem_Win32::PromptSaveFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, char *path, size_t &outPathLength, size_t pathCapacity, const char *initialFileName)
|
||||||
|
{
|
||||||
|
wchar_t baseFN[MAX_PATH + 5];
|
||||||
|
wchar_t baseDir[MAX_PATH + 5];
|
||||||
|
|
||||||
|
const size_t existingPathLen = strlen(initialFileName);
|
||||||
|
if (existingPathLen >= MAX_PATH)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < existingPathLen; i++)
|
||||||
|
baseFN[i] = static_cast<wchar_t>(initialFileName[i]);
|
||||||
|
baseFN[existingPathLen] = 0;
|
||||||
|
|
||||||
|
if (!ResolvePath(virtualDirectory, "", baseDir))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OPENFILENAMEW ofn;
|
||||||
|
memset(&ofn, 0, sizeof(ofn));
|
||||||
|
|
||||||
|
ofn.lStructSize = sizeof(ofn);
|
||||||
|
ofn.lpstrFilter = L"GlidePort File (*.gpf)\0*.gpf\0";
|
||||||
|
ofn.lpstrFile = baseFN;
|
||||||
|
ofn.lpstrDefExt = L"gpf";
|
||||||
|
ofn.nMaxFile = MAX_PATH;
|
||||||
|
ofn.lpstrInitialDir = baseDir;
|
||||||
|
ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT;
|
||||||
|
|
||||||
|
if (!GetSaveFileNameW(&ofn))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ofn.Flags & OFN_EXTENSIONDIFFERENT)
|
||||||
|
{
|
||||||
|
MessageBeep(MB_ICONERROR);
|
||||||
|
MessageBoxW(nullptr, L"Save file failed: Saved files must have the '.gpf' extension", L"Invalid file path", MB_OK);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t *fn = ofn.lpstrFile + ofn.nFileOffset;
|
||||||
|
size_t fnLengthWithoutExt = wcslen(fn);
|
||||||
|
if (ofn.nFileExtension - 1 > ofn.nFileOffset) // Off by 1 because extension doesn't include .
|
||||||
|
fnLengthWithoutExt = ofn.nFileExtension - ofn.nFileOffset - 1;
|
||||||
|
|
||||||
|
if (fnLengthWithoutExt >= pathCapacity)
|
||||||
|
{
|
||||||
|
wchar_t msg[256];
|
||||||
|
wsprintfW(msg, L"Save file failed: File name is too long. Limit is %i characters.", static_cast<int>(pathCapacity));
|
||||||
|
MessageBeep(MB_ICONERROR);
|
||||||
|
MessageBoxW(nullptr, msg, L"Invalid file path", MB_OK);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ofn.nFileOffset != wcslen(baseDir) || memcmp(ofn.lpstrFile, baseDir, ofn.nFileOffset * sizeof(wchar_t)))
|
||||||
|
{
|
||||||
|
wchar_t msg[256 + MAX_PATH];
|
||||||
|
wsprintfW(msg, L"Save file failed: File can't be saved here, it must be saved in %s", baseDir);
|
||||||
|
MessageBeep(MB_ICONERROR);
|
||||||
|
MessageBoxW(nullptr, msg, L"Invalid file path", MB_OK);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t *unsupportedCharMsg = L"File name contains unsupported characters.";
|
||||||
|
|
||||||
|
for (size_t i = 0; i < fnLengthWithoutExt; i++)
|
||||||
|
{
|
||||||
|
if (fn[i] < static_cast<wchar_t>(0) || fn[i] >= static_cast<wchar_t>(128))
|
||||||
|
{
|
||||||
|
MessageBeep(MB_ICONERROR);
|
||||||
|
MessageBoxW(nullptr, unsupportedCharMsg, L"Invalid file path", MB_OK);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
path[i] = static_cast<char>(fn[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidateFilePath(path, fnLengthWithoutExt))
|
||||||
|
{
|
||||||
|
MessageBeep(MB_ICONERROR);
|
||||||
|
MessageBoxW(nullptr, unsupportedCharMsg, L"Invalid file path", MB_OK);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outPathLength = fnLengthWithoutExt;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GpFileSystem_Win32::ValidateFilePath(const char *str, size_t length) const
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
const char c = str[i];
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (c == '_' || c == '.' || c == '\'')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (c == ' ' && i != 0 && i != length - 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (c >= 'a' && c <= 'z')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (c >= 'A' && c <= 'Z')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const wchar_t *GpFileSystem_Win32::GetBasePath() const
|
const wchar_t *GpFileSystem_Win32::GetBasePath() const
|
||||||
{
|
{
|
||||||
return m_executablePath;
|
return m_executablePath;
|
||||||
@@ -247,6 +409,9 @@ bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtua
|
|||||||
case PortabilityLayer::VirtualDirectories::kGameData:
|
case PortabilityLayer::VirtualDirectories::kGameData:
|
||||||
baseDir = m_housesDir.c_str();
|
baseDir = m_housesDir.c_str();
|
||||||
break;
|
break;
|
||||||
|
case PortabilityLayer::VirtualDirectories::kUserData:
|
||||||
|
baseDir = m_userHousesDir.c_str();
|
||||||
|
break;
|
||||||
case PortabilityLayer::VirtualDirectories::kPrefs:
|
case PortabilityLayer::VirtualDirectories::kPrefs:
|
||||||
baseDir = m_prefsDir.c_str();
|
baseDir = m_prefsDir.c_str();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -14,9 +14,14 @@ public:
|
|||||||
|
|
||||||
bool FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) override;
|
bool FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) override;
|
||||||
bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists) override;
|
bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists) override;
|
||||||
PortabilityLayer::IOStream *OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, bool create) override;
|
PortabilityLayer::IOStream *OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition) override;
|
||||||
|
bool DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) override;
|
||||||
PortabilityLayer::HostDirectoryCursor *ScanDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory) override;
|
PortabilityLayer::HostDirectoryCursor *ScanDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory) override;
|
||||||
|
|
||||||
|
bool PromptSaveFile(PortabilityLayer::VirtualDirectory_t dirID, char *path, size_t &outPathLength, size_t pathCapacity, const char *initialFileName) override;
|
||||||
|
|
||||||
|
bool ValidateFilePath(const char *path, size_t sz) const override;
|
||||||
|
|
||||||
const wchar_t *GetBasePath() const;
|
const wchar_t *GetBasePath() const;
|
||||||
|
|
||||||
static GpFileSystem_Win32 *GetInstance();
|
static GpFileSystem_Win32 *GetInstance();
|
||||||
@@ -28,6 +33,7 @@ private:
|
|||||||
std::wstring m_scoresDir;
|
std::wstring m_scoresDir;
|
||||||
std::wstring m_packagedDir;
|
std::wstring m_packagedDir;
|
||||||
std::wstring m_housesDir;
|
std::wstring m_housesDir;
|
||||||
|
std::wstring m_userHousesDir;
|
||||||
std::wstring m_resourcesDir;
|
std::wstring m_resourcesDir;
|
||||||
wchar_t m_executablePath[MAX_PATH];
|
wchar_t m_executablePath[MAX_PATH];
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ namespace PortabilityLayer
|
|||||||
PLError_t OpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, IOStream *&outRefNum) override;
|
PLError_t OpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, IOStream *&outRefNum) override;
|
||||||
bool ReadFileProperties(VirtualDirectory_t dirID, const PLPasStr &filename, MacFileProperties &properties) override;
|
bool ReadFileProperties(VirtualDirectory_t dirID, const PLPasStr &filename, MacFileProperties &properties) override;
|
||||||
|
|
||||||
PLError_t RawOpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, IOStream *&outStream) override;
|
PLError_t RawOpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, GpFileCreationDisposition_t creationDisposition, IOStream *&outStream) override;
|
||||||
PLError_t RawOpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, IOStream *&outStream) override;
|
PLError_t RawOpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, GpFileCreationDisposition_t creationDisposition, IOStream *&outStream) override;
|
||||||
|
|
||||||
|
bool PromptSaveFile(VirtualDirectory_t dirID, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName) override;
|
||||||
|
|
||||||
static FileManagerImpl *GetInstance();
|
static FileManagerImpl *GetInstance();
|
||||||
|
|
||||||
@@ -38,7 +40,7 @@ namespace PortabilityLayer
|
|||||||
typedef char ExtendedFileName_t[64 + 4];
|
typedef char ExtendedFileName_t[64 + 4];
|
||||||
|
|
||||||
PLError_t OpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext, EFilePermission permission, IOStream *&outRefNum);
|
PLError_t OpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext, EFilePermission permission, IOStream *&outRefNum);
|
||||||
PLError_t RawOpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext, EFilePermission permission, bool ignoreMeta, bool create, IOStream *&outStream);
|
PLError_t RawOpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext, EFilePermission permission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, IOStream *&outStream);
|
||||||
|
|
||||||
static bool ConstructFilename(ExtendedFileName_t& extFN, const PLPasStr &fn, const char *extension);
|
static bool ConstructFilename(ExtendedFileName_t& extFN, const PLPasStr &fn, const char *extension);
|
||||||
|
|
||||||
@@ -74,12 +76,28 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
bool FileManagerImpl::DeleteFile(VirtualDirectory_t dirID, const PLPasStr &filename)
|
bool FileManagerImpl::DeleteFile(VirtualDirectory_t dirID, const PLPasStr &filename)
|
||||||
{
|
{
|
||||||
ExtendedFileName_t extFN;
|
const size_t numExts = 3;
|
||||||
if (!ConstructFilename(extFN, filename, ".gpf"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// PL_NotYetImplemented_TODO("FileSystem")
|
const char *exts[numExts] = { ".gpr", ".gpd", ".gpf" };
|
||||||
return false;
|
const bool extMayNotExist[numExts] = { true, true, false };
|
||||||
|
|
||||||
|
for (int extIndex = 0; extIndex < numExts; extIndex++)
|
||||||
|
{
|
||||||
|
ExtendedFileName_t extFN;
|
||||||
|
if (!ConstructFilename(extFN, filename, exts[extIndex]))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
bool existed = false;
|
||||||
|
if (!PortabilityLayer::HostFileSystem::GetInstance()->DeleteFile(dirID, extFN, existed))
|
||||||
|
{
|
||||||
|
if (extMayNotExist[extIndex] && !existed)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PLError_t FileManagerImpl::CreateFile(VirtualDirectory_t dirID, const PLPasStr &filename, const MacFileProperties &mfp)
|
PLError_t FileManagerImpl::CreateFile(VirtualDirectory_t dirID, const PLPasStr &filename, const MacFileProperties &mfp)
|
||||||
@@ -92,7 +110,7 @@ namespace PortabilityLayer
|
|||||||
return PLErrors::kBadFileName;
|
return PLErrors::kBadFileName;
|
||||||
|
|
||||||
IOStream *stream = nullptr;
|
IOStream *stream = nullptr;
|
||||||
PLError_t err = RawOpenFileFork(dirID, filename, ".gpf", EFilePermission_Write, true, true, stream);
|
PLError_t err = RawOpenFileFork(dirID, filename, ".gpf", EFilePermission_Write, true, GpFileCreationDispositions::kCreateOrOverwrite, stream);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
@@ -130,7 +148,7 @@ namespace PortabilityLayer
|
|||||||
bool FileManagerImpl::ReadFileProperties(VirtualDirectory_t dirID, const PLPasStr &filename, MacFileProperties &properties)
|
bool FileManagerImpl::ReadFileProperties(VirtualDirectory_t dirID, const PLPasStr &filename, MacFileProperties &properties)
|
||||||
{
|
{
|
||||||
IOStream *stream = nullptr;
|
IOStream *stream = nullptr;
|
||||||
PLError_t err = RawOpenFileFork(dirID, filename, ".gpf", EFilePermission_Read, true, false, stream);
|
PLError_t err = RawOpenFileFork(dirID, filename, ".gpf", EFilePermission_Read, true, GpFileCreationDispositions::kOpenExisting, stream);
|
||||||
if (err)
|
if (err)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -144,14 +162,23 @@ namespace PortabilityLayer
|
|||||||
return readOk;
|
return readOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
PLError_t FileManagerImpl::RawOpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission permission, bool ignoreMeta, IOStream *&outStream)
|
PLError_t FileManagerImpl::RawOpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission permission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, IOStream *&outStream)
|
||||||
{
|
{
|
||||||
return RawOpenFileFork(dirID, filename, ".gpd", permission, ignoreMeta, false, outStream);
|
return RawOpenFileFork(dirID, filename, ".gpd", permission, ignoreMeta, createDisposition, outStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
PLError_t FileManagerImpl::RawOpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission permission, bool ignoreMeta, IOStream *&outStream)
|
PLError_t FileManagerImpl::RawOpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission permission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, IOStream *&outStream)
|
||||||
{
|
{
|
||||||
return RawOpenFileFork(dirID, filename, ".gpr", permission, ignoreMeta, false, outStream);
|
return RawOpenFileFork(dirID, filename, ".gpr", permission, ignoreMeta, createDisposition, outStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileManagerImpl::PromptSaveFile(VirtualDirectory_t dirID, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName)
|
||||||
|
{
|
||||||
|
ExtendedFileName_t extFN;
|
||||||
|
if (!ConstructFilename(extFN, initialFileName, ""))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return PortabilityLayer::HostFileSystem::GetInstance()->PromptSaveFile(dirID, path, outPathLength, pathCapacity, extFN);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileManagerImpl *FileManagerImpl::GetInstance()
|
FileManagerImpl *FileManagerImpl::GetInstance()
|
||||||
@@ -162,8 +189,9 @@ namespace PortabilityLayer
|
|||||||
PLError_t FileManagerImpl::OpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *extension, EFilePermission permission, IOStream *&outStream)
|
PLError_t FileManagerImpl::OpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *extension, EFilePermission permission, IOStream *&outStream)
|
||||||
{
|
{
|
||||||
bool isWriteAccess = (permission == EFilePermission_Any || permission == EFilePermission_ReadWrite || permission == EFilePermission_Write);
|
bool isWriteAccess = (permission == EFilePermission_Any || permission == EFilePermission_ReadWrite || permission == EFilePermission_Write);
|
||||||
|
GpFileCreationDisposition_t createDisposition = isWriteAccess ? GpFileCreationDispositions::kCreateOrOpen : GpFileCreationDispositions::kOpenExisting;
|
||||||
IOStream *stream = nullptr;
|
IOStream *stream = nullptr;
|
||||||
PLError_t openError = RawOpenFileFork(dirID, filename, extension, permission, false, isWriteAccess, stream);
|
PLError_t openError = RawOpenFileFork(dirID, filename, extension, permission, false, createDisposition, stream);
|
||||||
if (openError != PLErrors::kNone)
|
if (openError != PLErrors::kNone)
|
||||||
return openError;
|
return openError;
|
||||||
|
|
||||||
@@ -172,7 +200,7 @@ namespace PortabilityLayer
|
|||||||
return PLErrors::kNone;
|
return PLErrors::kNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
PLError_t FileManagerImpl::RawOpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext, EFilePermission permission, bool ignoreMeta, bool create, IOStream *&outStream)
|
PLError_t FileManagerImpl::RawOpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext, EFilePermission permission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, IOStream *&outStream)
|
||||||
{
|
{
|
||||||
ExtendedFileName_t gpfExtFN;
|
ExtendedFileName_t gpfExtFN;
|
||||||
ExtendedFileName_t extFN;
|
ExtendedFileName_t extFN;
|
||||||
@@ -192,27 +220,25 @@ namespace PortabilityLayer
|
|||||||
if (!ConstructFilename(extFN, filename, ext))
|
if (!ConstructFilename(extFN, filename, ext))
|
||||||
return PLErrors::kBadFileName;
|
return PLErrors::kBadFileName;
|
||||||
|
|
||||||
const bool needToCreate = create && !HostFileSystem::GetInstance()->FileExists(dirID, extFN);
|
|
||||||
|
|
||||||
IOStream *fstream = nullptr;
|
IOStream *fstream = nullptr;
|
||||||
switch (permission)
|
switch (permission)
|
||||||
{
|
{
|
||||||
case EFilePermission_Any:
|
case EFilePermission_Any:
|
||||||
fstream = HostFileSystem::GetInstance()->OpenFile(dirID, extFN, true, needToCreate);
|
fstream = HostFileSystem::GetInstance()->OpenFile(dirID, extFN, true, createDisposition);
|
||||||
if (fstream)
|
if (fstream)
|
||||||
permission = EFilePermission_ReadWrite;
|
permission = EFilePermission_ReadWrite;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
permission = EFilePermission_Read;
|
permission = EFilePermission_Read;
|
||||||
fstream = HostFileSystem::GetInstance()->OpenFile(dirID, extFN, false, needToCreate);
|
fstream = HostFileSystem::GetInstance()->OpenFile(dirID, extFN, false, createDisposition);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EFilePermission_Read:
|
case EFilePermission_Read:
|
||||||
fstream = HostFileSystem::GetInstance()->OpenFile(dirID, extFN, false, needToCreate);
|
fstream = HostFileSystem::GetInstance()->OpenFile(dirID, extFN, false, createDisposition);
|
||||||
break;
|
break;
|
||||||
case EFilePermission_ReadWrite:
|
case EFilePermission_ReadWrite:
|
||||||
case EFilePermission_Write:
|
case EFilePermission_Write:
|
||||||
fstream = HostFileSystem::GetInstance()->OpenFile(dirID, extFN, true, needToCreate);
|
fstream = HostFileSystem::GetInstance()->OpenFile(dirID, extFN, true, createDisposition);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,26 +259,8 @@ namespace PortabilityLayer
|
|||||||
memcpy(extFN, fn.Chars(), fnameSize);
|
memcpy(extFN, fn.Chars(), fnameSize);
|
||||||
memcpy(extFN + fnameSize, extension, strlen(extension) + 1);
|
memcpy(extFN + fnameSize, extension, strlen(extension) + 1);
|
||||||
|
|
||||||
for (size_t i = 0; i < fnameSize; i++)
|
if (!PortabilityLayer::HostFileSystem::GetInstance()->ValidateFilePath(extFN, fnameSize))
|
||||||
{
|
|
||||||
const char c = extFN[i];
|
|
||||||
if (c >= '0' && c <= '9')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (c == '_' || c == '.' || c == '\'')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (c == ' ' && i != 0 && i != fnameSize - 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (c >= 'a' && c <= 'z')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (c >= 'A' && c <= 'Z')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "FilePermission.h"
|
#include "FilePermission.h"
|
||||||
#include "CoreDefs.h"
|
#include "CoreDefs.h"
|
||||||
#include "FilePos.h"
|
#include "FilePos.h"
|
||||||
|
#include "GpFileCreationDisposition.h"
|
||||||
#include "PLErrorCodes.h"
|
#include "PLErrorCodes.h"
|
||||||
#include "VirtualDirectory.h"
|
#include "VirtualDirectory.h"
|
||||||
|
|
||||||
@@ -26,12 +27,15 @@ namespace PortabilityLayer
|
|||||||
virtual PLError_t CreateFile(VirtualDirectory_t dirID, const PLPasStr &filename, const MacFileProperties &mfp) = 0;
|
virtual PLError_t CreateFile(VirtualDirectory_t dirID, const PLPasStr &filename, const MacFileProperties &mfp) = 0;
|
||||||
virtual PLError_t CreateFileAtCurrentTime(VirtualDirectory_t dirID, const PLPasStr &filename, const ResTypeID &fileCreator, const ResTypeID &fileType) = 0;
|
virtual PLError_t CreateFileAtCurrentTime(VirtualDirectory_t dirID, const PLPasStr &filename, const ResTypeID &fileCreator, const ResTypeID &fileType) = 0;
|
||||||
|
|
||||||
|
// OpenFileData + OpenFileResources require that the file already exists (i.e. has a .gpf), but the fork may not
|
||||||
virtual PLError_t OpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, IOStream *&outStream) = 0;
|
virtual PLError_t OpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, IOStream *&outStream) = 0;
|
||||||
virtual PLError_t OpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, IOStream *&outStream) = 0;
|
virtual PLError_t OpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, IOStream *&outStream) = 0;
|
||||||
virtual bool ReadFileProperties(VirtualDirectory_t dirID, const PLPasStr &filename, MacFileProperties &properties) = 0;
|
virtual bool ReadFileProperties(VirtualDirectory_t dirID, const PLPasStr &filename, MacFileProperties &properties) = 0;
|
||||||
|
|
||||||
virtual PLError_t RawOpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, IOStream *&outStream) = 0;
|
virtual PLError_t RawOpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, IOStream *&outStream) = 0;
|
||||||
virtual PLError_t RawOpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, IOStream *&outStream) = 0;
|
virtual PLError_t RawOpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, IOStream *&outStream) = 0;
|
||||||
|
|
||||||
|
virtual bool PromptSaveFile(VirtualDirectory_t dirID, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName) = 0;
|
||||||
|
|
||||||
static FileManager *GetInstance();
|
static FileManager *GetInstance();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace PortabilityLayer
|
|||||||
{
|
{
|
||||||
void FontFamily::AddFont(int flags, const char *path, FontHacks fontHacks)
|
void FontFamily::AddFont(int flags, const char *path, FontHacks fontHacks)
|
||||||
{
|
{
|
||||||
PortabilityLayer::IOStream *sysFontStream = PortabilityLayer::HostFileSystem::GetInstance()->OpenFile(PortabilityLayer::VirtualDirectories::kFonts, path, false, false);
|
PortabilityLayer::IOStream *sysFontStream = PortabilityLayer::HostFileSystem::GetInstance()->OpenFile(PortabilityLayer::VirtualDirectories::kFonts, path, false, GpFileCreationDispositions::kOpenExisting);
|
||||||
if (!sysFontStream)
|
if (!sysFontStream)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "GpFileCreationDisposition.h"
|
||||||
#include "VirtualDirectory.h"
|
#include "VirtualDirectory.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace PortabilityLayer
|
namespace PortabilityLayer
|
||||||
{
|
{
|
||||||
class IOStream;
|
class IOStream;
|
||||||
@@ -12,9 +15,13 @@ namespace PortabilityLayer
|
|||||||
public:
|
public:
|
||||||
virtual bool FileExists(VirtualDirectory_t virtualDirectory, const char *path) = 0;
|
virtual bool FileExists(VirtualDirectory_t virtualDirectory, const char *path) = 0;
|
||||||
virtual bool FileLocked(VirtualDirectory_t virtualDirectory, const char *path, bool *exists) = 0;
|
virtual bool FileLocked(VirtualDirectory_t virtualDirectory, const char *path, bool *exists) = 0;
|
||||||
virtual IOStream *OpenFile(VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, bool create) = 0;
|
virtual IOStream *OpenFile(VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition) = 0;
|
||||||
|
virtual bool DeleteFile(VirtualDirectory_t virtualDirectory, const char *path, bool &existed) = 0;
|
||||||
virtual HostDirectoryCursor *ScanDirectory(VirtualDirectory_t virtualDirectory) = 0;
|
virtual HostDirectoryCursor *ScanDirectory(VirtualDirectory_t virtualDirectory) = 0;
|
||||||
|
|
||||||
|
virtual bool PromptSaveFile(VirtualDirectory_t virtualDirectory, char *path, size_t &outPathLength, size_t pathCapacity, const char *initialFileName) = 0;
|
||||||
|
virtual bool ValidateFilePath(const char *path, size_t pathLen) const = 0;
|
||||||
|
|
||||||
static HostFileSystem *GetInstance();
|
static HostFileSystem *GetInstance();
|
||||||
static void SetInstance(HostFileSystem *instance);
|
static void SetInstance(HostFileSystem *instance);
|
||||||
|
|
||||||
|
|||||||
@@ -366,6 +366,8 @@ namespace PortabilityLayer
|
|||||||
m_firstMenu = insertingMenu;
|
m_firstMenu = insertingMenu;
|
||||||
|
|
||||||
existingMenuPtr->prevMenu = insertingMenu;
|
existingMenuPtr->prevMenu = insertingMenu;
|
||||||
|
|
||||||
|
DrawMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManagerImpl::InsertMenuAfter(const THandle<Menu> &insertingMenu, const THandle<Menu> &existingMenu)
|
void MenuManagerImpl::InsertMenuAfter(const THandle<Menu> &insertingMenu, const THandle<Menu> &existingMenu)
|
||||||
@@ -384,6 +386,8 @@ namespace PortabilityLayer
|
|||||||
m_lastMenu = insertingMenu;
|
m_lastMenu = insertingMenu;
|
||||||
|
|
||||||
existingMenuPtr->nextMenu = insertingMenu;
|
existingMenuPtr->nextMenu = insertingMenu;
|
||||||
|
|
||||||
|
DrawMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManagerImpl::InsertMenuAtEnd(const THandle<Menu> &insertingMenu)
|
void MenuManagerImpl::InsertMenuAtEnd(const THandle<Menu> &insertingMenu)
|
||||||
@@ -399,6 +403,8 @@ namespace PortabilityLayer
|
|||||||
(*m_lastMenu)->nextMenu = insertingMenu;
|
(*m_lastMenu)->nextMenu = insertingMenu;
|
||||||
(*insertingMenu)->prevMenu = m_lastMenu;
|
(*insertingMenu)->prevMenu = m_lastMenu;
|
||||||
m_lastMenu = insertingMenu;
|
m_lastMenu = insertingMenu;
|
||||||
|
|
||||||
|
DrawMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManagerImpl::InsertMenuAtBeginning(const THandle<Menu> &insertingMenu)
|
void MenuManagerImpl::InsertMenuAtBeginning(const THandle<Menu> &insertingMenu)
|
||||||
@@ -414,12 +420,12 @@ namespace PortabilityLayer
|
|||||||
(*m_firstMenu)->prevMenu = insertingMenu;
|
(*m_firstMenu)->prevMenu = insertingMenu;
|
||||||
(*insertingMenu)->nextMenu = m_firstMenu;
|
(*insertingMenu)->nextMenu = m_firstMenu;
|
||||||
m_firstMenu = insertingMenu;
|
m_firstMenu = insertingMenu;
|
||||||
|
|
||||||
|
DrawMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManagerImpl::RemoveMenu(const THandle<Menu> &menu)
|
void MenuManagerImpl::RemoveMenu(const THandle<Menu> &menu)
|
||||||
{
|
{
|
||||||
DrawMenuBar();
|
|
||||||
|
|
||||||
Menu *menuPtr = *menu;
|
Menu *menuPtr = *menu;
|
||||||
if (menuPtr->stringBlobHandle)
|
if (menuPtr->stringBlobHandle)
|
||||||
PortabilityLayer::MemoryManager::GetInstance()->ReleaseHandle(menuPtr->stringBlobHandle);
|
PortabilityLayer::MemoryManager::GetInstance()->ReleaseHandle(menuPtr->stringBlobHandle);
|
||||||
@@ -446,6 +452,8 @@ namespace PortabilityLayer
|
|||||||
Menu *menu = *menuHandle;
|
Menu *menu = *menuHandle;
|
||||||
|
|
||||||
menu->enabled = enabled;
|
menu->enabled = enabled;
|
||||||
|
|
||||||
|
DrawMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManagerImpl::SetItemEnabled(const THandle<Menu> &menuHandle, unsigned int index, bool enabled)
|
void MenuManagerImpl::SetItemEnabled(const THandle<Menu> &menuHandle, unsigned int index, bool enabled)
|
||||||
@@ -456,6 +464,8 @@ namespace PortabilityLayer
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
menu->menuItems[index].enabled = enabled;
|
menu->menuItems[index].enabled = enabled;
|
||||||
|
|
||||||
|
DrawMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManagerImpl::SetItemChecked(const THandle<Menu> &menuHandle, unsigned int index, bool checked)
|
void MenuManagerImpl::SetItemChecked(const THandle<Menu> &menuHandle, unsigned int index, bool checked)
|
||||||
@@ -473,7 +483,6 @@ namespace PortabilityLayer
|
|||||||
return point.m_y >= 0 && static_cast<uint32_t>(point.m_y) < kMenuBarHeight;
|
return point.m_y >= 0 && static_cast<uint32_t>(point.m_y) < kMenuBarHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MenuManagerImpl::MenuSelect(const Vec2i &initialPoint, int16_t *outMenu, uint16_t *outItem)
|
void MenuManagerImpl::MenuSelect(const Vec2i &initialPoint, int16_t *outMenu, uint16_t *outItem)
|
||||||
{
|
{
|
||||||
RefreshMenuBarLayout();
|
RefreshMenuBarLayout();
|
||||||
|
|||||||
@@ -540,7 +540,7 @@ DirectoryFileListEntry *GetDirectoryFiles(PortabilityLayer::VirtualDirectory_t d
|
|||||||
if (!strcmp(&filename[fnLen - 4], ".gpf"))
|
if (!strcmp(&filename[fnLen - 4], ".gpf"))
|
||||||
{
|
{
|
||||||
const size_t dotPos = fnLen - 4;
|
const size_t dotPos = fnLen - 4;
|
||||||
PortabilityLayer::IOStream *stream = fs->OpenFile(dirID, filename, false, false);
|
PortabilityLayer::IOStream *stream = fs->OpenFile(dirID, filename, false, GpFileCreationDispositions::kOpenExisting);
|
||||||
if (!stream)
|
if (!stream)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
short OpenResFork(VirtualDirectory_t virtualDir, const PLPasStr &filename) override;
|
short OpenResFork(VirtualDirectory_t virtualDir, const PLPasStr &filename) override;
|
||||||
void CloseResFile(short ref) override;
|
void CloseResFile(short ref) override;
|
||||||
|
PLError_t CreateBlankResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) override;
|
||||||
|
|
||||||
THandle<void> GetResource(const ResTypeID &resType, int id) override;
|
THandle<void> GetResource(const ResTypeID &resType, int id) override;
|
||||||
|
|
||||||
@@ -161,7 +162,7 @@ namespace PortabilityLayer
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
IOStream *fStream = nullptr;
|
IOStream *fStream = nullptr;
|
||||||
if (FileManager::GetInstance()->RawOpenFileResources(virtualDir, filename, EFilePermission_Read, true, fStream) != PLErrors::kNone)
|
if (FileManager::GetInstance()->RawOpenFileResources(virtualDir, filename, EFilePermission_Read, true, GpFileCreationDispositions::kOpenExisting, fStream) != PLErrors::kNone)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ResourceFile *resFile = new ResourceFile();
|
ResourceFile *resFile = new ResourceFile();
|
||||||
@@ -224,6 +225,29 @@ namespace PortabilityLayer
|
|||||||
m_currentResFile = m_lastResFile;
|
m_currentResFile = m_lastResFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PLError_t ResourceManagerImpl::CreateBlankResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename)
|
||||||
|
{
|
||||||
|
const uint8_t blankResFileData[] = {
|
||||||
|
0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 30,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 30, 255, 255 };
|
||||||
|
|
||||||
|
PortabilityLayer::IOStream *stream = nullptr;
|
||||||
|
PLError_t error = FileManager::GetInstance()->RawOpenFileResources(virtualDir, filename, EFilePermission_Write, true, GpFileCreationDispositions::kCreateOrOverwrite, stream);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (stream->Write(blankResFileData, sizeof(blankResFileData)) != sizeof(blankResFileData))
|
||||||
|
{
|
||||||
|
stream->Close();
|
||||||
|
return PLErrors::kIOError;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->Close();
|
||||||
|
|
||||||
|
return PLErrors::kNone;
|
||||||
|
}
|
||||||
|
|
||||||
THandle<void> ResourceManagerImpl::GetResource(const ResTypeID &resType, int id)
|
THandle<void> ResourceManagerImpl::GetResource(const ResTypeID &resType, int id)
|
||||||
{
|
{
|
||||||
short searchIndex = m_currentResFile;
|
short searchIndex = m_currentResFile;
|
||||||
|
|||||||
@@ -45,12 +45,6 @@ int Count1Resources(UInt32 resType)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PLError_t HCreateResFile(PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &name)
|
|
||||||
{
|
|
||||||
PL_NotYetImplemented();
|
|
||||||
return PLErrors::kNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetResLoad(Boolean load)
|
void SetResLoad(Boolean load)
|
||||||
{
|
{
|
||||||
PortabilityLayer::ResourceManager::GetInstance()->SetResLoad(load != 0);
|
PortabilityLayer::ResourceManager::GetInstance()->SetResLoad(load != 0);
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ Handle Get1Resource(UInt32 resID, int index);
|
|||||||
Handle Get1IndResource(UInt32 resID, int index);
|
Handle Get1IndResource(UInt32 resID, int index);
|
||||||
int Count1Resources(UInt32 resType);
|
int Count1Resources(UInt32 resType);
|
||||||
|
|
||||||
PLError_t HCreateResFile(PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &name);
|
|
||||||
|
|
||||||
void SetResLoad(Boolean load); // Sets whether resources should be loaded when requested
|
void SetResLoad(Boolean load); // Sets whether resources should be loaded when requested
|
||||||
|
|
||||||
long GetMaxResourceSize(Handle res);
|
long GetMaxResourceSize(Handle res);
|
||||||
|
|||||||
@@ -1,396 +1,396 @@
|
|||||||
#include "ResourceFile.h"
|
#include "ResourceFile.h"
|
||||||
#include "BinarySearch.h"
|
#include "BinarySearch.h"
|
||||||
#include "ByteSwap.h"
|
#include "ByteSwap.h"
|
||||||
#include "MacFileMem.h"
|
#include "MacFileMem.h"
|
||||||
#include "MemoryManager.h"
|
#include "MemoryManager.h"
|
||||||
#include "MemReaderStream.h"
|
#include "MemReaderStream.h"
|
||||||
#include "ResourceCompiledRef.h"
|
#include "ResourceCompiledRef.h"
|
||||||
#include "ResourceCompiledTypeList.h"
|
#include "ResourceCompiledTypeList.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
namespace PortabilityLayer
|
namespace PortabilityLayer
|
||||||
{
|
{
|
||||||
ResourceFile::ResourceFile()
|
ResourceFile::ResourceFile()
|
||||||
: m_resDataBlob(nullptr)
|
: m_resDataBlob(nullptr)
|
||||||
, m_resDataBlobSize(0)
|
, m_resDataBlobSize(0)
|
||||||
, m_resNameBlob(nullptr)
|
, m_resNameBlob(nullptr)
|
||||||
, m_resNameBlobSize(0)
|
, m_resNameBlobSize(0)
|
||||||
, m_compiledRefBlob(nullptr)
|
, m_compiledRefBlob(nullptr)
|
||||||
, m_numResources(0)
|
, m_numResources(0)
|
||||||
, m_compiledTypeListBlob(nullptr)
|
, m_compiledTypeListBlob(nullptr)
|
||||||
, m_numResourceTypes(0)
|
, m_numResourceTypes(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceFile::~ResourceFile()
|
ResourceFile::~ResourceFile()
|
||||||
{
|
{
|
||||||
if (m_resNameBlob)
|
if (m_resNameBlob)
|
||||||
delete[] m_resNameBlob;
|
delete[] m_resNameBlob;
|
||||||
|
|
||||||
if (m_resDataBlob)
|
if (m_resDataBlob)
|
||||||
delete[] m_resDataBlob;
|
delete[] m_resDataBlob;
|
||||||
|
|
||||||
if (m_compiledRefBlob)
|
if (m_compiledRefBlob)
|
||||||
delete[] m_compiledRefBlob;
|
delete[] m_compiledRefBlob;
|
||||||
|
|
||||||
if (m_compiledTypeListBlob)
|
if (m_compiledTypeListBlob)
|
||||||
delete[] m_compiledTypeListBlob;
|
delete[] m_compiledTypeListBlob;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResourceFile::Load(IOStream *stream)
|
bool ResourceFile::Load(IOStream *stream)
|
||||||
{
|
{
|
||||||
struct ResourceHeader
|
struct ResourceHeader
|
||||||
{
|
{
|
||||||
uint32_t m_resDataOffset;
|
uint32_t m_resDataOffset;
|
||||||
uint32_t m_resMapOffset;
|
uint32_t m_resMapOffset;
|
||||||
uint32_t m_resDataSize;
|
uint32_t m_resDataSize;
|
||||||
uint32_t m_resMapSize;
|
uint32_t m_resMapSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
const UFilePos_t streamSize = stream->Size();
|
const UFilePos_t streamSize = stream->Size();
|
||||||
if (streamSize > UINT32_MAX)
|
if (streamSize > UINT32_MAX)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32_t resForkSize = static_cast<uint32_t>(streamSize);
|
uint32_t resForkSize = static_cast<uint32_t>(streamSize);
|
||||||
|
|
||||||
ResourceHeader resourceHeader;
|
ResourceHeader resourceHeader;
|
||||||
|
|
||||||
if (stream->Read(&resourceHeader, sizeof(ResourceHeader)) != sizeof(ResourceHeader))
|
if (stream->Read(&resourceHeader, sizeof(ResourceHeader)) != sizeof(ResourceHeader))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ByteSwap::BigUInt32(resourceHeader.m_resDataOffset);
|
ByteSwap::BigUInt32(resourceHeader.m_resDataOffset);
|
||||||
ByteSwap::BigUInt32(resourceHeader.m_resMapOffset);
|
ByteSwap::BigUInt32(resourceHeader.m_resMapOffset);
|
||||||
ByteSwap::BigUInt32(resourceHeader.m_resDataSize);
|
ByteSwap::BigUInt32(resourceHeader.m_resDataSize);
|
||||||
ByteSwap::BigUInt32(resourceHeader.m_resMapSize);
|
ByteSwap::BigUInt32(resourceHeader.m_resMapSize);
|
||||||
|
|
||||||
if (resourceHeader.m_resDataOffset > resForkSize)
|
if (resourceHeader.m_resDataOffset > resForkSize)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (resourceHeader.m_resMapOffset > resForkSize)
|
if (resourceHeader.m_resMapOffset > resForkSize)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (resForkSize - resourceHeader.m_resDataOffset < resourceHeader.m_resDataSize)
|
if (resForkSize - resourceHeader.m_resDataOffset < resourceHeader.m_resDataSize)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (resForkSize - resourceHeader.m_resMapOffset < resourceHeader.m_resMapSize)
|
if (resForkSize - resourceHeader.m_resMapOffset < resourceHeader.m_resMapSize)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!stream->SeekStart(resourceHeader.m_resDataOffset))
|
if (!stream->SeekStart(resourceHeader.m_resDataOffset))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_resDataBlob = new uint8_t[resourceHeader.m_resDataSize];
|
m_resDataBlob = new uint8_t[resourceHeader.m_resDataSize];
|
||||||
m_resDataBlobSize = resourceHeader.m_resDataSize;
|
m_resDataBlobSize = resourceHeader.m_resDataSize;
|
||||||
|
|
||||||
if (stream->Read(m_resDataBlob, resourceHeader.m_resDataSize) != resourceHeader.m_resDataSize)
|
if (stream->Read(m_resDataBlob, resourceHeader.m_resDataSize) != resourceHeader.m_resDataSize)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// The format of this is slightly different from the documented format:
|
// The format of this is slightly different from the documented format:
|
||||||
// The type list offset is the offset of the type COUNT, which takes up 2 bytes.
|
// The type list offset is the offset of the type COUNT, which takes up 2 bytes.
|
||||||
// Usually the offset is 28, even though the size of the resource map including the
|
// Usually the offset is 28, even though the size of the resource map including the
|
||||||
// count would appear to be 30.
|
// count would appear to be 30.
|
||||||
|
|
||||||
struct ResourceMap
|
struct ResourceMap
|
||||||
{
|
{
|
||||||
uint8_t m_reserved[16 + 4 + 2];
|
uint8_t m_reserved[16 + 4 + 2];
|
||||||
uint16_t m_attributes;
|
uint16_t m_attributes;
|
||||||
uint16_t m_typeListOffset;
|
uint16_t m_typeListOffset;
|
||||||
uint16_t m_nameListOffset;
|
uint16_t m_nameListOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceTypeListEntry
|
struct ResourceTypeListEntry
|
||||||
{
|
{
|
||||||
ResTypeID m_resType;
|
ResTypeID m_resType;
|
||||||
uint16_t m_numResourcesMinusOne;
|
uint16_t m_numResourcesMinusOne;
|
||||||
uint16_t m_refListOffset;
|
uint16_t m_refListOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceRefListEntry
|
struct ResourceRefListEntry
|
||||||
{
|
{
|
||||||
int16_t m_resID;
|
int16_t m_resID;
|
||||||
int16_t m_resourceNameOffset;
|
int16_t m_resourceNameOffset;
|
||||||
uint8_t m_attributes;
|
uint8_t m_attributes;
|
||||||
uint8_t m_packedResDataOffset[3];
|
uint8_t m_packedResDataOffset[3];
|
||||||
uint32_t m_reserved;
|
uint32_t m_reserved;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!stream->SeekStart(resourceHeader.m_resMapOffset))
|
if (!stream->SeekStart(resourceHeader.m_resMapOffset))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ResourceMap resourceMap;
|
ResourceMap resourceMap;
|
||||||
if (stream->Read(&resourceMap, sizeof(ResourceMap)) != sizeof(ResourceMap))
|
if (stream->Read(&resourceMap, sizeof(ResourceMap)) != sizeof(ResourceMap))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ByteSwap::BigUInt16(resourceMap.m_attributes);
|
ByteSwap::BigUInt16(resourceMap.m_attributes);
|
||||||
ByteSwap::BigUInt16(resourceMap.m_typeListOffset);
|
ByteSwap::BigUInt16(resourceMap.m_typeListOffset);
|
||||||
ByteSwap::BigUInt16(resourceMap.m_nameListOffset);
|
ByteSwap::BigUInt16(resourceMap.m_nameListOffset);
|
||||||
|
|
||||||
const size_t sizeFromStartOfResMap = resForkSize - resourceHeader.m_resMapOffset;
|
const size_t sizeFromStartOfResMap = resForkSize - resourceHeader.m_resMapOffset;
|
||||||
if (resourceMap.m_typeListOffset > sizeFromStartOfResMap)
|
if (resourceMap.m_typeListOffset > sizeFromStartOfResMap)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (resourceMap.m_nameListOffset > sizeFromStartOfResMap)
|
if (resourceMap.m_nameListOffset > sizeFromStartOfResMap)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const size_t typeListOffset = resourceHeader.m_resMapOffset + resourceMap.m_typeListOffset;
|
const size_t typeListOffset = resourceHeader.m_resMapOffset + resourceMap.m_typeListOffset;
|
||||||
const size_t sizeFromStartOfTypeList = resForkSize - typeListOffset;
|
const size_t sizeFromStartOfTypeList = resForkSize - typeListOffset;
|
||||||
|
|
||||||
// First pass: Count the number of references we need
|
// First pass: Count the number of references we need
|
||||||
if (!stream->SeekStart(typeListOffset))
|
if (!stream->SeekStart(typeListOffset))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint16_t numTypesMinusOne;
|
uint16_t numTypesMinusOne;
|
||||||
if (stream->Read(&numTypesMinusOne, 2) != 2)
|
if (stream->Read(&numTypesMinusOne, 2) != 2)
|
||||||
return false;
|
return false;
|
||||||
ByteSwap::BigUInt16(numTypesMinusOne);
|
ByteSwap::BigUInt16(numTypesMinusOne);
|
||||||
|
|
||||||
// numTypesMinusOne can sometimes be -1, in which case there are no resources
|
// numTypesMinusOne can sometimes be -1, in which case there are no resources
|
||||||
m_numResourceTypes = (numTypesMinusOne + 1) & 0xffff;
|
m_numResourceTypes = (numTypesMinusOne + 1) & 0xffff;
|
||||||
|
|
||||||
if (sizeFromStartOfTypeList < 2 || (sizeFromStartOfTypeList - 2) / 8 < m_numResourceTypes)
|
if (sizeFromStartOfTypeList < 2 || (sizeFromStartOfTypeList - 2) / 8 < m_numResourceTypes)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32_t numResourcesTotal = 0;
|
uint32_t numResourcesTotal = 0;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < m_numResourceTypes; i++)
|
for (uint32_t i = 0; i < m_numResourceTypes; i++)
|
||||||
{
|
{
|
||||||
ResourceTypeListEntry tlEntry;
|
ResourceTypeListEntry tlEntry;
|
||||||
if (stream->Read(&tlEntry, sizeof(ResourceTypeListEntry)) != sizeof(ResourceTypeListEntry))
|
if (stream->Read(&tlEntry, sizeof(ResourceTypeListEntry)) != sizeof(ResourceTypeListEntry))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ByteSwap::BigUInt16(tlEntry.m_numResourcesMinusOne);
|
ByteSwap::BigUInt16(tlEntry.m_numResourcesMinusOne);
|
||||||
|
|
||||||
const uint32_t numResourcesOfThisType = tlEntry.m_numResourcesMinusOne + 1;
|
const uint32_t numResourcesOfThisType = tlEntry.m_numResourcesMinusOne + 1;
|
||||||
|
|
||||||
if (UINT32_MAX - numResourcesTotal < numResourcesOfThisType)
|
if (UINT32_MAX - numResourcesTotal < numResourcesOfThisType)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
numResourcesTotal += numResourcesOfThisType;
|
numResourcesTotal += numResourcesOfThisType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numResourcesTotal > 0)
|
if (numResourcesTotal > 0)
|
||||||
m_compiledRefBlob = new ResourceCompiledRef[numResourcesTotal];
|
m_compiledRefBlob = new ResourceCompiledRef[numResourcesTotal];
|
||||||
|
|
||||||
m_numResources = numResourcesTotal;
|
m_numResources = numResourcesTotal;
|
||||||
|
|
||||||
// Second pass: Compile references
|
// Second pass: Compile references
|
||||||
ResourceCompiledRef *refToCompile = m_compiledRefBlob;
|
ResourceCompiledRef *refToCompile = m_compiledRefBlob;
|
||||||
|
|
||||||
if (m_numResourceTypes > 0)
|
if (m_numResourceTypes > 0)
|
||||||
m_compiledTypeListBlob = new ResourceCompiledTypeList[m_numResourceTypes];
|
m_compiledTypeListBlob = new ResourceCompiledTypeList[m_numResourceTypes];
|
||||||
|
|
||||||
for (uint32_t i = 0; i < m_numResourceTypes; i++)
|
for (uint32_t i = 0; i < m_numResourceTypes; i++)
|
||||||
{
|
{
|
||||||
if (!stream->SeekStart(typeListOffset + 2 + i * 8))
|
if (!stream->SeekStart(typeListOffset + 2 + i * 8))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ResourceTypeListEntry tlEntry;
|
ResourceTypeListEntry tlEntry;
|
||||||
if (stream->Read(&tlEntry, sizeof(ResourceTypeListEntry)) != sizeof(ResourceTypeListEntry))
|
if (stream->Read(&tlEntry, sizeof(ResourceTypeListEntry)) != sizeof(ResourceTypeListEntry))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ByteSwap::BigUInt16(tlEntry.m_numResourcesMinusOne);
|
ByteSwap::BigUInt16(tlEntry.m_numResourcesMinusOne);
|
||||||
ByteSwap::BigUInt16(tlEntry.m_refListOffset);
|
ByteSwap::BigUInt16(tlEntry.m_refListOffset);
|
||||||
|
|
||||||
ResourceCompiledTypeList &ctl = m_compiledTypeListBlob[i];
|
ResourceCompiledTypeList &ctl = m_compiledTypeListBlob[i];
|
||||||
ctl.m_resType = tlEntry.m_resType;
|
ctl.m_resType = tlEntry.m_resType;
|
||||||
ctl.m_firstRef = refToCompile;
|
ctl.m_firstRef = refToCompile;
|
||||||
ctl.m_numRefs = tlEntry.m_numResourcesMinusOne + 1;
|
ctl.m_numRefs = tlEntry.m_numResourcesMinusOne + 1;
|
||||||
|
|
||||||
if (sizeFromStartOfTypeList < tlEntry.m_refListOffset)
|
if (sizeFromStartOfTypeList < tlEntry.m_refListOffset)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Start reading the ref list
|
// Start reading the ref list
|
||||||
if (!stream->SeekStart(typeListOffset + tlEntry.m_refListOffset))
|
if (!stream->SeekStart(typeListOffset + tlEntry.m_refListOffset))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const uint32_t numResourcesOfThisType = tlEntry.m_numResourcesMinusOne + 1;
|
const uint32_t numResourcesOfThisType = tlEntry.m_numResourcesMinusOne + 1;
|
||||||
|
|
||||||
ResourceCompiledRef *firstRefOfThisType = refToCompile;
|
ResourceCompiledRef *firstRefOfThisType = refToCompile;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < numResourcesOfThisType; i++)
|
for (uint32_t i = 0; i < numResourcesOfThisType; i++)
|
||||||
{
|
{
|
||||||
ResourceRefListEntry refListEntry;
|
ResourceRefListEntry refListEntry;
|
||||||
if (stream->Read(&refListEntry, sizeof(ResourceRefListEntry)) != sizeof(ResourceRefListEntry))
|
if (stream->Read(&refListEntry, sizeof(ResourceRefListEntry)) != sizeof(ResourceRefListEntry))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ByteSwap::BigInt16(refListEntry.m_resID);
|
ByteSwap::BigInt16(refListEntry.m_resID);
|
||||||
ByteSwap::BigInt16(refListEntry.m_resourceNameOffset);
|
ByteSwap::BigInt16(refListEntry.m_resourceNameOffset);
|
||||||
|
|
||||||
const uint8_t *packedOffset = refListEntry.m_packedResDataOffset;
|
const uint8_t *packedOffset = refListEntry.m_packedResDataOffset;
|
||||||
|
|
||||||
const size_t dataSizeOffset = (packedOffset[0] << 16) + (packedOffset[1] << 8) + packedOffset[2];
|
const size_t dataSizeOffset = (packedOffset[0] << 16) + (packedOffset[1] << 8) + packedOffset[2];
|
||||||
|
|
||||||
if (dataSizeOffset > resourceHeader.m_resDataSize)
|
if (dataSizeOffset > resourceHeader.m_resDataSize)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Needs to be at least size 4 to decode the resource size
|
// Needs to be at least size 4 to decode the resource size
|
||||||
if (resourceHeader.m_resDataSize - dataSizeOffset < 4)
|
if (resourceHeader.m_resDataSize - dataSizeOffset < 4)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const size_t dataOffset = dataSizeOffset + 4;
|
const size_t dataOffset = dataSizeOffset + 4;
|
||||||
|
|
||||||
refToCompile->m_attributes = refListEntry.m_attributes;
|
refToCompile->m_attributes = refListEntry.m_attributes;
|
||||||
refToCompile->m_resData = m_resDataBlob + dataOffset;
|
refToCompile->m_resData = m_resDataBlob + dataOffset;
|
||||||
refToCompile->m_resID = refListEntry.m_resID;
|
refToCompile->m_resID = refListEntry.m_resID;
|
||||||
refToCompile->m_resNameOffset = refListEntry.m_resourceNameOffset;
|
refToCompile->m_resNameOffset = refListEntry.m_resourceNameOffset;
|
||||||
refToCompile->m_handle = nullptr;
|
refToCompile->m_handle = nullptr;
|
||||||
|
|
||||||
uint32_t resSize;
|
uint32_t resSize;
|
||||||
memcpy(&resSize, m_resDataBlob + dataSizeOffset, 4);
|
memcpy(&resSize, m_resDataBlob + dataSizeOffset, 4);
|
||||||
|
|
||||||
ByteSwap::BigUInt32(resSize);
|
ByteSwap::BigUInt32(resSize);
|
||||||
|
|
||||||
if (resSize > resourceHeader.m_resDataSize)
|
if (resSize > resourceHeader.m_resDataSize)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (resourceHeader.m_resDataSize - resSize < dataOffset)
|
if (resourceHeader.m_resDataSize - resSize < dataOffset)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
refToCompile++;
|
refToCompile++;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(firstRefOfThisType, refToCompile, CompiledRefSortPredicate);
|
std::sort(firstRefOfThisType, refToCompile, CompiledRefSortPredicate);
|
||||||
|
|
||||||
for (uint32_t i = 1; i < numResourcesOfThisType; i++)
|
for (uint32_t i = 1; i < numResourcesOfThisType; i++)
|
||||||
{
|
{
|
||||||
if (firstRefOfThisType[i - 1].m_resID == firstRefOfThisType[i].m_resID)
|
if (firstRefOfThisType[i - 1].m_resID == firstRefOfThisType[i].m_resID)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_numResources > 0)
|
if (m_numResources > 0)
|
||||||
{
|
{
|
||||||
bool anyNamed = false;
|
bool anyNamed = false;
|
||||||
|
|
||||||
size_t lastNameStart = 0;
|
size_t lastNameStart = 0;
|
||||||
for (size_t i = 0; i < m_numResources; i++)
|
for (size_t i = 0; i < m_numResources; i++)
|
||||||
{
|
{
|
||||||
const ResourceCompiledRef &ref = m_compiledRefBlob[i];
|
const ResourceCompiledRef &ref = m_compiledRefBlob[i];
|
||||||
if (ref.m_resNameOffset < 0)
|
if (ref.m_resNameOffset < 0)
|
||||||
{
|
{
|
||||||
if (ref.m_resNameOffset != -1)
|
if (ref.m_resNameOffset != -1)
|
||||||
return false; // Non-compliant
|
return false; // Non-compliant
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
anyNamed = true;
|
anyNamed = true;
|
||||||
|
|
||||||
const size_t candidateOffset = static_cast<size_t>(ref.m_resNameOffset);
|
const size_t candidateOffset = static_cast<size_t>(ref.m_resNameOffset);
|
||||||
if (candidateOffset > lastNameStart)
|
if (candidateOffset > lastNameStart)
|
||||||
lastNameStart = candidateOffset;
|
lastNameStart = candidateOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anyNamed)
|
if (anyNamed)
|
||||||
{
|
{
|
||||||
const size_t nameListCapacity = sizeFromStartOfResMap - resourceMap.m_nameListOffset;
|
const size_t nameListCapacity = sizeFromStartOfResMap - resourceMap.m_nameListOffset;
|
||||||
if (lastNameStart >= nameListCapacity)
|
if (lastNameStart >= nameListCapacity)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_resNameBlobSize = lastNameStart + 1 + 256;
|
m_resNameBlobSize = lastNameStart + 1 + 256;
|
||||||
m_resNameBlob = new uint8_t[m_resNameBlobSize];
|
m_resNameBlob = new uint8_t[m_resNameBlobSize];
|
||||||
memset(m_resNameBlob, 0, m_resNameBlobSize);
|
memset(m_resNameBlob, 0, m_resNameBlobSize);
|
||||||
|
|
||||||
if (!stream->SeekStart(resourceHeader.m_resMapOffset + resourceMap.m_nameListOffset))
|
if (!stream->SeekStart(resourceHeader.m_resMapOffset + resourceMap.m_nameListOffset))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (stream->Read(m_resNameBlob, lastNameStart + 1) != lastNameStart + 1)
|
if (stream->Read(m_resNameBlob, lastNameStart + 1) != lastNameStart + 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Figure out the length of the final string
|
// Figure out the length of the final string
|
||||||
const size_t lastStringLength = m_resNameBlob[lastNameStart];
|
const size_t lastStringLength = m_resNameBlob[lastNameStart];
|
||||||
if (lastStringLength > 0)
|
if (lastStringLength > 0)
|
||||||
{
|
{
|
||||||
if (stream->Read(m_resNameBlob + lastNameStart + 1, lastStringLength) != lastStringLength)
|
if (stream->Read(m_resNameBlob + lastNameStart + 1, lastStringLength) != lastStringLength)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(m_compiledTypeListBlob, m_compiledTypeListBlob + m_numResourceTypes, CompiledTypeListSortPredicate);
|
std::sort(m_compiledTypeListBlob, m_compiledTypeListBlob + m_numResourceTypes, CompiledTypeListSortPredicate);
|
||||||
|
|
||||||
for (uint32_t i = 1; i < m_numResourceTypes; i++)
|
for (uint32_t i = 1; i < m_numResourceTypes; i++)
|
||||||
{
|
{
|
||||||
if (m_compiledTypeListBlob[i - 1].m_resType == m_compiledTypeListBlob[i].m_resType)
|
if (m_compiledTypeListBlob[i - 1].m_resType == m_compiledTypeListBlob[i].m_resType)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResourceFile::CompiledRefSortPredicate(const ResourceCompiledRef &a, const ResourceCompiledRef &b)
|
bool ResourceFile::CompiledRefSortPredicate(const ResourceCompiledRef &a, const ResourceCompiledRef &b)
|
||||||
{
|
{
|
||||||
return a.m_resID < b.m_resID;
|
return a.m_resID < b.m_resID;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResourceFile::CompiledTypeListSortPredicate(const ResourceCompiledTypeList &a, const ResourceCompiledTypeList &b)
|
bool ResourceFile::CompiledTypeListSortPredicate(const ResourceCompiledTypeList &a, const ResourceCompiledTypeList &b)
|
||||||
{
|
{
|
||||||
return memcmp(&a.m_resType, &b.m_resType, sizeof(ResTypeID)) < 0;
|
return memcmp(&a.m_resType, &b.m_resType, sizeof(ResTypeID)) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ResourceFile::CompiledRefSearchPredicate(int resID, const ResourceCompiledRef &ref)
|
int ResourceFile::CompiledRefSearchPredicate(int resID, const ResourceCompiledRef &ref)
|
||||||
{
|
{
|
||||||
return resID - ref.m_resID;
|
return resID - ref.m_resID;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ResourceFile::CompiledTypeListSearchPredicate(const ResTypeID &resTypeID, const ResourceCompiledTypeList &typeList)
|
int ResourceFile::CompiledTypeListSearchPredicate(const ResTypeID &resTypeID, const ResourceCompiledTypeList &typeList)
|
||||||
{
|
{
|
||||||
return memcmp(&resTypeID, &typeList.m_resType, 4);
|
return memcmp(&resTypeID, &typeList.m_resType, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceFile::GetAllResourceTypeLists(ResourceCompiledTypeList *&outTypeLists, size_t &outCount) const
|
void ResourceFile::GetAllResourceTypeLists(ResourceCompiledTypeList *&outTypeLists, size_t &outCount) const
|
||||||
{
|
{
|
||||||
outTypeLists = m_compiledTypeListBlob;
|
outTypeLists = m_compiledTypeListBlob;
|
||||||
outCount = m_numResourceTypes;
|
outCount = m_numResourceTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ResourceCompiledTypeList *ResourceFile::GetResourceTypeList(const ResTypeID &resType)
|
const ResourceCompiledTypeList *ResourceFile::GetResourceTypeList(const ResTypeID &resType)
|
||||||
{
|
{
|
||||||
const ResourceCompiledTypeList *tlStart = m_compiledTypeListBlob;
|
const ResourceCompiledTypeList *tlStart = m_compiledTypeListBlob;
|
||||||
const ResourceCompiledTypeList *tlEnd = tlStart + m_numResourceTypes;
|
const ResourceCompiledTypeList *tlEnd = tlStart + m_numResourceTypes;
|
||||||
|
|
||||||
const ResourceCompiledTypeList *tl = BinarySearch(tlStart, tlEnd, resType, CompiledTypeListSearchPredicate);
|
const ResourceCompiledTypeList *tl = BinarySearch(tlStart, tlEnd, resType, CompiledTypeListSearchPredicate);
|
||||||
if (tl == tlEnd)
|
if (tl == tlEnd)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return tl;
|
return tl;
|
||||||
}
|
}
|
||||||
|
|
||||||
MMHandleBlock *ResourceFile::GetResource(const ResTypeID &resType, int id, bool load)
|
MMHandleBlock *ResourceFile::GetResource(const ResTypeID &resType, int id, bool load)
|
||||||
{
|
{
|
||||||
const ResourceCompiledTypeList *tl = GetResourceTypeList(resType);
|
const ResourceCompiledTypeList *tl = GetResourceTypeList(resType);
|
||||||
if (tl == nullptr)
|
if (tl == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
ResourceCompiledRef *refStart = tl->m_firstRef;
|
ResourceCompiledRef *refStart = tl->m_firstRef;
|
||||||
ResourceCompiledRef *refEnd = refStart + tl->m_numRefs;
|
ResourceCompiledRef *refEnd = refStart + tl->m_numRefs;
|
||||||
ResourceCompiledRef *ref = BinarySearch(refStart, refEnd, id, CompiledRefSearchPredicate);
|
ResourceCompiledRef *ref = BinarySearch(refStart, refEnd, id, CompiledRefSearchPredicate);
|
||||||
|
|
||||||
if (ref == refEnd)
|
if (ref == refEnd)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
MMHandleBlock *handle = nullptr;
|
MMHandleBlock *handle = nullptr;
|
||||||
if (ref->m_handle != nullptr)
|
if (ref->m_handle != nullptr)
|
||||||
handle = ref->m_handle;
|
handle = ref->m_handle;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
handle = MemoryManager::GetInstance()->AllocHandle(0);
|
handle = MemoryManager::GetInstance()->AllocHandle(0);
|
||||||
handle->m_rmSelfRef = ref;
|
handle->m_rmSelfRef = ref;
|
||||||
ref->m_handle = handle;
|
ref->m_handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handle->m_contents == nullptr && load)
|
if (handle->m_contents == nullptr && load)
|
||||||
{
|
{
|
||||||
const uint32_t resSize = ref->GetSize();
|
const uint32_t resSize = ref->GetSize();
|
||||||
if (resSize > 0)
|
if (resSize > 0)
|
||||||
{
|
{
|
||||||
void *contents = MemoryManager::GetInstance()->Alloc(resSize);
|
void *contents = MemoryManager::GetInstance()->Alloc(resSize);
|
||||||
handle->m_contents = contents;
|
handle->m_contents = contents;
|
||||||
handle->m_size = resSize;
|
handle->m_size = resSize;
|
||||||
memcpy(handle->m_contents, ref->m_resData, resSize);
|
memcpy(handle->m_contents, ref->m_resData, resSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "VirtualDirectory.h"
|
#include "VirtualDirectory.h"
|
||||||
|
#include "PLErrorCodes.h"
|
||||||
#include "PLHandle.h"
|
#include "PLHandle.h"
|
||||||
|
|
||||||
class PLPasStr;
|
class PLPasStr;
|
||||||
@@ -21,6 +22,7 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
virtual short OpenResFork(VirtualDirectory_t virtualDir, const PLPasStr &filename) = 0;
|
virtual short OpenResFork(VirtualDirectory_t virtualDir, const PLPasStr &filename) = 0;
|
||||||
virtual void CloseResFile(short ref) = 0;
|
virtual void CloseResFile(short ref) = 0;
|
||||||
|
virtual PLError_t CreateBlankResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) = 0;
|
||||||
|
|
||||||
virtual THandle<void> GetResource(const ResTypeID &resType, int id) = 0;
|
virtual THandle<void> GetResource(const ResTypeID &resType, int id) = 0;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user