Skip to content

Commit

Permalink
Fix up some file handling, add support for initializing a house file
Browse files Browse the repository at this point in the history
  • Loading branch information
elasota committed Jan 3, 2020
1 parent e00f506 commit 32116bc
Show file tree
Hide file tree
Showing 19 changed files with 708 additions and 475 deletions.
34 changes: 14 additions & 20 deletions GpApp/House.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
#include "PLPasStr.h"
#include "PLResources.h"
#include "PLSound.h"
#include "PLSysCalls.h"
#include "DialogUtils.h"
#include "Externs.h"
#include "FileManager.h"
#include "HostFileSystem.h"
#include "House.h"
#include "RectUtils.h"
#include "ResourceManager.h"


#define kGoToDialogID 1043
Expand Down Expand Up @@ -51,32 +54,23 @@ Boolean CreateNewHouse (void)
AEKeyword theKeyword;
DescType actualType;
Size actualSize;
NavReplyRecord theReply;
NavDialogOptions dialogOptions;
VFileSpec tempSpec;
VFileSpec theSpec;
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();

if (theReply.replacing)
{
if (fm->FileExists(theSpec.m_dir, theSpec.m_name))
{
CheckFileError(PLErrors::kFileNotFound, theSpec.m_name);
return (false);
}
theSpec.m_dir = PortabilityLayer::VirtualDirectories::kUserData;
PasStringCopy(PSTR("My House"), theSpec.m_name);

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))
{
CheckFileError(PLErrors::kAccessDenied, theSpec.m_name);
Expand All @@ -94,7 +88,7 @@ Boolean CreateNewHouse (void)
if (!CheckFileError(theErr, PSTR("New House")))
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)
YellowAlert(kYellowFailedResCreate, theErr);

Expand Down
5 changes: 3 additions & 2 deletions GpApp/SelectHouse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,8 +572,9 @@ void DoDirSearch (void)
for (i = 0; i < kMaxDirectories; i++)
theDirs[i] = PortabilityLayer::VirtualDirectories::kUnspecified;
currentDir = 0;
theDirs[currentDir] = PortabilityLayer::VirtualDirectories::kGameData;
numDirs = 1;
theDirs[0] = PortabilityLayer::VirtualDirectories::kGameData;
theDirs[1] = PortabilityLayer::VirtualDirectories::kUserData;
numDirs = 2;

PortabilityLayer::FileManager *fm = PortabilityLayer::FileManager::GetInstance();

Expand Down
15 changes: 15 additions & 0 deletions GpCommon/GpFileCreationDisposition.h
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 2 additions & 0 deletions GpCommon/GpWindows.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <Windows.h>

#undef CreateMutex
#undef DeleteFile


struct IGpFiber;
struct IGpColorCursor_Win32;
Expand Down
1 change: 1 addition & 0 deletions GpD3D/GpD3D.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
<ClInclude Include="..\GpCommon\EGpInputDriverType.h" />
<ClInclude Include="..\GpCommon\EGpStandardCursor.h" />
<ClInclude Include="..\GpCommon\GpDisplayDriverTickStatus.h" />
<ClInclude Include="..\GpCommon\GpFileCreationDisposition.h" />
<ClInclude Include="..\GpCommon\GpInputDriverProperties.h" />
<ClInclude Include="..\GpCommon\IGpColorCursor.h" />
<ClInclude Include="..\GpCommon\IGpAudioChannelCallbacks.h" />
Expand Down
3 changes: 3 additions & 0 deletions GpD3D/GpD3D.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@
<ClInclude Include="..\GpCommon\GpDisplayDriverTickStatus.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\GpCommon\GpFileCreationDisposition.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="GpD3D.rc">
Expand Down
171 changes: 168 additions & 3 deletions GpD3D/GpFileSystem_Win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <string>
#include <Shlwapi.h>
#include <ShlObj.h>
#include <commdlg.h>

#include <assert.h>

class GpDirectoryCursor_Win32 final : public PortabilityLayer::HostDirectoryCursor
Expand Down Expand Up @@ -112,12 +114,17 @@ GpFileSystem_Win32::GpFileSystem_Win32()
}

m_prefsDir.append(L"\\GlidePort");

m_userHousesDir = m_prefsDir + L"\\Houses";
m_scoresDir = m_prefsDir + L"\\Scores";

CreateDirectoryW(m_prefsDir.c_str(), nullptr);
CreateDirectoryW(m_scoresDir.c_str(), nullptr);
CreateDirectoryW(m_userHousesDir.c_str(), nullptr);

m_prefsDir.append(L"\\");
m_scoresDir.append(L"\\");
m_userHousesDir.append(L"\\");
}

DWORD modulePathSize = GetModuleFileNameW(nullptr, m_executablePath, MAX_PATH);
Expand Down Expand Up @@ -192,23 +199,66 @@ bool GpFileSystem_Win32::FileLocked(PortabilityLayer::VirtualDirectory_t virtual
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];

if (!ResolvePath(virtualDirectory, path, winPath))
return false;

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)
return false;

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)
{
wchar_t winPath[MAX_PATH + 2];
Expand All @@ -225,6 +275,118 @@ PortabilityLayer::HostDirectoryCursor *GpFileSystem_Win32::ScanDirectory(Portabi
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
{
return m_executablePath;
Expand All @@ -247,6 +409,9 @@ bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtua
case PortabilityLayer::VirtualDirectories::kGameData:
baseDir = m_housesDir.c_str();
break;
case PortabilityLayer::VirtualDirectories::kUserData:
baseDir = m_userHousesDir.c_str();
break;
case PortabilityLayer::VirtualDirectories::kPrefs:
baseDir = m_prefsDir.c_str();
break;
Expand Down
8 changes: 7 additions & 1 deletion GpD3D/GpFileSystem_Win32.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ class GpFileSystem_Win32 final : public PortabilityLayer::HostFileSystem

bool FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) 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;

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;

static GpFileSystem_Win32 *GetInstance();
Expand All @@ -28,6 +33,7 @@ class GpFileSystem_Win32 final : public PortabilityLayer::HostFileSystem
std::wstring m_scoresDir;
std::wstring m_packagedDir;
std::wstring m_housesDir;
std::wstring m_userHousesDir;
std::wstring m_resourcesDir;
wchar_t m_executablePath[MAX_PATH];

Expand Down
Loading

0 comments on commit 32116bc

Please sign in to comment.