Compare commits

...

35 Commits

Author SHA1 Message Date
elasota
c147e1100e Change MiniRez to use WindowsUnicodeToolShim 2021-03-11 22:28:01 -05:00
elasota
0a2e730d26 Change FTagData to use WindowsUnicodeToolShim 2021-03-11 22:22:30 -05:00
elasota
922cd0fd06 Add debug/release props to WindowsUnicodeToolShim 2021-03-11 22:22:14 -05:00
elasota
b8bf6be44f Change hqx2gp to use WindowsUnicodeToolShim 2021-03-11 22:18:07 -05:00
elasota
2897c4ffab Fix saved game deletion not working 2021-03-11 21:40:22 -05:00
elasota
35c174984b Bump version to 1.0.16 2021-03-11 21:36:06 -05:00
elasota
da16edea8d Fix broken AA table preloader, use load bar first time on desktop 2021-03-09 04:15:59 -05:00
elasota
2fe1ea2ee7 Fix objects being draggable when there is no room 2021-03-08 21:59:11 -05:00
elasota
0931f25f23 Fix wrong house sort order 2021-03-08 21:58:46 -05:00
elasota
7f4d782c0d Fix room count mismatch 2021-03-08 21:58:34 -05:00
elasota
f2cda23b0f Bump version to 1.0.15 2021-03-08 19:08:12 -05:00
elasota
6292705968 Fix insensitive compare not working properly with strings of different length 2021-03-08 19:07:31 -05:00
elasota
6931b3f505 EOL fix 2021-03-08 19:07:06 -05:00
elasota
d70a5b3bfc Add more optimizations 2021-03-07 05:45:24 -05:00
elasota
f0913d0d6a Add MergeGPF to installer project 2021-03-07 05:37:29 -05:00
elasota
80584eb781 Fix California or Bust! house on Android 2021-03-07 05:16:15 -05:00
elasota
fe4a8a55c6 Don't partially delete house when overwriting the current house 2021-03-07 05:08:01 -05:00
elasota
55ec6c516c Fix overwrite warnings not working 2021-03-07 05:07:23 -05:00
elasota
3917e1a370 File system refactor, bug fixes 2021-03-07 04:31:05 -05:00
elasota
6715bcb030 Fix music playing in the editor 2021-03-06 11:34:47 -05:00
elasota
c981a97a20 Fix fullscreen prefs not working in SDL. Add SDL GameController support. 2021-03-05 01:56:32 -05:00
elasota
a5562f96e0 Move portable things to AerofoilPortable. 2021-03-05 00:35:43 -05:00
elasota
d97bd8ad1c Move some C++11 types from Android to AerofoilSDL. 2021-03-01 01:06:56 -05:00
elasota
a43f32ab88 Fix trademark usage 2021-03-01 00:40:30 -05:00
elasota
e94d1511b1 Add 32-bit support to SDL version 2021-02-28 23:31:13 -05:00
elasota
95e4eb4e34 Bump version to 1.0.14, update copyright year 2021-02-28 23:01:53 -05:00
elasota
15bdb97c38 Add volume control to SDL audio driver. 2021-02-28 22:58:01 -05:00
elasota
e7246c1444 Reduce marquee speed 2021-02-20 15:26:44 -05:00
elasota
35b8e922d7 Fix Windows size minimum not working correctly 2021-02-20 15:16:47 -05:00
elasota
3090f70ee6 Bump version to 1.0.13 2021-02-20 15:16:31 -05:00
elasota
484e18a9af Fix resolution desync if resize occurs during the loading screen. 2021-02-20 15:11:31 -05:00
elasota
83d37a7c94 Add system clipboard support to Windows 2020-12-18 00:07:21 -05:00
elasota
fc043af3a1 Bump version to 1.0.12 2020-12-18 00:02:52 -05:00
elasota
2ebd3f2cf3 Whole-word selection in editboxes 2020-12-17 19:49:09 -05:00
elasota
ebab2ee188 Fix incorrect carat pulse timing 2020-12-17 19:48:57 -05:00
123 changed files with 4201 additions and 1613 deletions

View File

@@ -48,6 +48,7 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ReleasePackageInstaller", "
{7EFF1E21-C375-45EA-A069-4E2232C8A72B} = {7EFF1E21-C375-45EA-A069-4E2232C8A72B}
{B852D549-4020-4477-8BFB-E199FF78B047} = {B852D549-4020-4477-8BFB-E199FF78B047}
{2FF15659-5C72-48B8-B55B-3C658E4125B5} = {2FF15659-5C72-48B8-B55B-3C658E4125B5}
{36DAF5FA-6ADB-4F20-9810-1610DE0AE653} = {36DAF5FA-6ADB-4F20-9810-1610DE0AE653}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsUnicodeToolShim", "WindowsUnicodeToolShim\WindowsUnicodeToolShim.vcxproj", "{15009625-1120-405E-8BBA-69A16CD6713D}"
@@ -60,6 +61,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GpFontHandler_FreeType2", "
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AerofoilSDL", "AerofoilSDL\AerofoilSDL.vcxproj", "{33542FF0-0473-4802-BC79-3B8261790F65}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MergeGPF", "MergeGPF\MergeGPF.vcxproj", "{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -168,6 +171,10 @@ Global
{33542FF0-0473-4802-BC79-3B8261790F65}.Debug|x64.Build.0 = Debug|x64
{33542FF0-0473-4802-BC79-3B8261790F65}.Release|x64.ActiveCfg = Release|x64
{33542FF0-0473-4802-BC79-3B8261790F65}.Release|x64.Build.0 = Release|x64
{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Debug|x64.ActiveCfg = Debug|x64
{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Debug|x64.Build.0 = Debug|x64
{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Release|x64.ActiveCfg = Release|x64
{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -99,24 +99,6 @@ bool GpFileStream_Win32::SeekEnd(GpUFilePos_t loc)
return SetFilePointerEx(m_handle, li, nullptr, FILE_END) != 0;
}
bool GpFileStream_Win32::Truncate(GpUFilePos_t loc)
{
if (!m_writeable)
return false;
GpUFilePos_t oldPos = Tell();
if (!SeekStart(loc))
return false;
if (!SetEndOfFile(m_handle))
return false;
if (!SeekStart(oldPos))
return false;
return true;
}
GpUFilePos_t GpFileStream_Win32::Size() const
{
LARGE_INTEGER fsize;

View File

@@ -17,7 +17,6 @@ public:
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
bool Truncate(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;

View File

@@ -191,24 +191,24 @@ bool GpFileSystem_Win32::FileExists(PortabilityLayer::VirtualDirectory_t virtual
return PathFileExistsW(winPath) != 0;
}
bool GpFileSystem_Win32::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists)
bool GpFileSystem_Win32::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists)
{
wchar_t winPath[MAX_PATH + 1];
if (!ResolvePath(virtualDirectory, &path, 1, winPath))
{
*exists = false;
exists = false;
return false;
}
DWORD attribs = GetFileAttributesW(winPath);
if (attribs == INVALID_FILE_ATTRIBUTES)
{
*exists = false;
exists = false;
return false;
}
*exists = true;
exists = true;
return (attribs & FILE_ATTRIBUTE_READONLY) != 0;
}
@@ -319,11 +319,6 @@ bool GpFileSystem_Win32::ValidateFilePathUnicodeChar(uint32_t c) const
return false;
}
bool GpFileSystem_Win32::IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const
{
return false;
}
void GpFileSystem_Win32::SetMainThreadRelay(IGpThreadRelay *relay)
{
(void)relay;
@@ -341,7 +336,7 @@ bool GpFileSystem_Win32::ValidateFilePath(const char *str, size_t length) const
if (c >= '0' && c <= '9')
continue;
if (c == '_' || c == '.' || c == '\'')
if (c == '_' || c == '.' || c == '\'' || c == '!')
continue;
if (c == ' ' && i != 0 && i != length - 1)

View File

@@ -13,7 +13,7 @@ public:
GpFileSystem_Win32();
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;
GpIOStream *OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) override;
bool DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) override;
IGpDirectoryCursor *ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) override;
@@ -21,8 +21,6 @@ public:
bool ValidateFilePath(const char *path, size_t sz) const override;
bool ValidateFilePathUnicodeChar(uint32_t ch) const override;
bool IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const override;
void SetMainThreadRelay(IGpThreadRelay *relay) override;
void SetDelayCallback(DelayCallback_t delayCallback) override;

View File

@@ -1,14 +1,117 @@
#include "GpSystemServices_Win32.h"
#include "GpMutex_Win32.h"
#include "GpThreadEvent_Win32.h"
#include "GpWindows.h"
#include "IGpClipboardContents.h"
#include "UTF16.h"
#include "UTF8.h"
#include <assert.h>
#include <vector>
#pragma push_macro("CreateMutex")
#ifdef CreateMutex
#undef CreateMutex
#endif
extern GpWindowsGlobals g_gpWindowsGlobals;
namespace GpSystemServices_Win32_Private
{
class RefCountedClipboard
{
public:
RefCountedClipboard();
protected:
virtual ~RefCountedClipboard();
void AddRef();
void DecRef();
unsigned int m_refCount;
};
class TextClipboard : public RefCountedClipboard, public IGpClipboardContentsText
{
public:
TextClipboard(const uint8_t *utf8Text, size_t utf8Size);
~TextClipboard() override;
GpClipboardContentsType_t GetContentsType() const override;
void Destroy() override;
IGpClipboardContents *Clone() const override;
const uint8_t *GetBytes() const override;
size_t GetSize() const override;
private:
std::vector<uint8_t> m_utf8Text;
};
RefCountedClipboard::RefCountedClipboard()
: m_refCount(1)
{
}
RefCountedClipboard::~RefCountedClipboard()
{
}
void RefCountedClipboard::AddRef()
{
m_refCount++;
}
void RefCountedClipboard::DecRef()
{
unsigned int rc = --m_refCount;
if (rc == 0)
delete this;
}
TextClipboard::TextClipboard(const uint8_t *utf8Text, size_t utf8Size)
{
m_utf8Text.resize(utf8Size);
if (utf8Size > 0)
memcpy(&m_utf8Text[0], utf8Text, utf8Size);
}
TextClipboard::~TextClipboard()
{
}
GpClipboardContentsType_t TextClipboard::GetContentsType() const
{
return GpClipboardContentsTypes::kText;
}
void TextClipboard::Destroy()
{
this->DecRef();
}
IGpClipboardContents *TextClipboard::Clone() const
{
const_cast<TextClipboard*>(this)->AddRef();
return const_cast<TextClipboard*>(this);
}
const uint8_t *TextClipboard::GetBytes() const
{
if (m_utf8Text.size() == 0)
return nullptr;
return &m_utf8Text[0];
}
size_t TextClipboard::GetSize() const
{
return m_utf8Text.size();
}
}
struct GpSystemServices_Win32_ThreadStartParams
{
GpSystemServices_Win32::ThreadFunc_t m_threadFunc;
@@ -34,6 +137,10 @@ GpSystemServices_Win32::GpSystemServices_Win32()
{
}
GpSystemServices_Win32::~GpSystemServices_Win32()
{
}
int64_t GpSystemServices_Win32::GetTime() const
{
SYSTEMTIME epochStart;
@@ -149,6 +256,11 @@ bool GpSystemServices_Win32::IsFullscreenPreferred() const
return !m_isTouchscreenSimulation;
}
bool GpSystemServices_Win32::IsFullscreenOnStartup() const
{
return false;
}
unsigned int GpSystemServices_Win32::GetCPUCount() const
{
SYSTEM_INFO sysInfo;
@@ -171,6 +283,89 @@ bool GpSystemServices_Win32::AreFontResourcesSeekable() const
return true;
}
IGpClipboardContents *GpSystemServices_Win32::GetClipboardContents() const
{
IGpClipboardContents *cbObject = nullptr;
if (IsClipboardFormatAvailable(CF_UNICODETEXT))
{
if (OpenClipboard(g_gpWindowsGlobals.m_hwnd))
{
HGLOBAL textHandle = GetClipboardData(CF_UNICODETEXT);
if (textHandle)
{
const wchar_t *str = static_cast<const wchar_t*>(GlobalLock(textHandle));
if (str)
{
if (str[0] == 0)
cbObject = new GpSystemServices_Win32_Private::TextClipboard(nullptr, 0);
else
{
int bytesRequired = WideCharToMultiByte(CP_UTF8, 0, str, -1, nullptr, 0, nullptr, nullptr);
if (bytesRequired > 0)
{
std::vector<char> decodedText;
decodedText.resize(bytesRequired);
WideCharToMultiByte(CP_UTF8, 0, str, -1, &decodedText[0], bytesRequired, nullptr, nullptr);
cbObject = new GpSystemServices_Win32_Private::TextClipboard(reinterpret_cast<const uint8_t*>(&decodedText[0]), decodedText.size() - 1);
}
}
GlobalUnlock(textHandle);
}
}
CloseClipboard();
}
}
return cbObject;
}
void GpSystemServices_Win32::SetClipboardContents(IGpClipboardContents *contents)
{
if (!contents)
return;
if (contents->GetContentsType() == GpClipboardContentsTypes::kText)
{
IGpClipboardContentsText *textContents = static_cast<IGpClipboardContentsText*>(contents);
if (OpenClipboard(g_gpWindowsGlobals.m_hwnd))
{
if (EmptyClipboard())
{
int wcharsRequired = MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<const char*>(textContents->GetBytes()), textContents->GetSize(), nullptr, 0);
std::vector<wchar_t> wideChars;
if (wcharsRequired)
{
wideChars.resize(wcharsRequired + 1);
MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<const char*>(textContents->GetBytes()), textContents->GetSize(), &wideChars[0], wcharsRequired);
}
else
wideChars.resize(1);
wideChars[wideChars.size() - 1] = static_cast<wchar_t>(0);
HGLOBAL textObject = GlobalAlloc(GMEM_MOVEABLE, wideChars.size() * sizeof(wchar_t));
if (textObject)
{
wchar_t *buffer = static_cast<wchar_t*>(GlobalLock(textObject));
memcpy(buffer, &wideChars[0], wideChars.size() * sizeof(wchar_t));
GlobalUnlock(textObject);
SetClipboardData(CF_UNICODETEXT, textObject);
}
}
CloseClipboard();
}
}
}
void GpSystemServices_Win32::SetTouchscreenSimulation(bool isTouchscreenSimulation)
{

View File

@@ -19,6 +19,7 @@ class GpSystemServices_Win32 final : public IGpSystemServices
{
public:
GpSystemServices_Win32();
~GpSystemServices_Win32();
int64_t GetTime() const override;
void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const override;
@@ -32,10 +33,13 @@ public:
bool IsUsingMouseAsTouch() const override;
bool IsTextInputObstructive() const override;
bool IsFullscreenPreferred() const override;
bool IsFullscreenOnStartup() const override;
unsigned int GetCPUCount() const override;
void SetTextInputEnabled(bool isEnabled) override;
bool IsTextInputEnabled() const override;
bool AreFontResourcesSeekable() const override;
IGpClipboardContents *GetClipboardContents() const override;
void SetClipboardContents(IGpClipboardContents *contents) override;
void SetTouchscreenSimulation(bool isTouchscreenSimulation);

View File

@@ -15,8 +15,8 @@ android {
}
minSdkVersion 16
targetSdkVersion 29
versionCode 7
versionName "1.0.11"
versionCode 12
versionName "1.0.16"
externalNativeBuild {
ndkBuild {
arguments "APP_PLATFORM=android-16"
@@ -66,7 +66,7 @@ android {
}
aaptOptions {
noCompress 'gpa'
noCompress 'gpf'
}
}

View File

@@ -0,0 +1 @@
../../../AerofoilPortable

View File

@@ -9,6 +9,7 @@ SDL_PATH := ../SDL
LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include \
$(LOCAL_PATH)/../GpShell \
$(LOCAL_PATH)/../GpCommon \
$(LOCAL_PATH)/../AerofoilPortable \
$(LOCAL_PATH)/../AerofoilSDL \
$(LOCAL_PATH)/../Common \
$(LOCAL_PATH)/../PortabilityLayer
@@ -23,7 +24,7 @@ LOCAL_SRC_FILES := \
LOCAL_SHARED_LIBRARIES := SDL2
LOCAL_STATIC_LIBRARIES := GpShell GpFontHandler_FreeType2 AerofoilSDL GpApp
LOCAL_STATIC_LIBRARIES := GpShell GpFontHandler_FreeType2 AerofoilPortable AerofoilSDL GpApp
LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog

View File

@@ -42,7 +42,6 @@ public:
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
bool Truncate(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;
@@ -70,7 +69,6 @@ public:
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
bool Truncate(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;
@@ -96,7 +94,6 @@ public:
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
bool Truncate(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;
@@ -168,11 +165,6 @@ bool GpFileStream_PFD::SeekEnd(GpUFilePos_t loc)
return lseek64(m_fd, loc, SEEK_END) >= 0;
}
bool GpFileStream_PFD::Truncate(GpUFilePos_t loc)
{
return ftruncate64(m_fd, static_cast<off64_t>(loc)) >= 0;
}
GpUFilePos_t GpFileStream_PFD::Size() const
{
struct stat64 s;
@@ -251,11 +243,6 @@ bool GpFileStream_SDLRWops::SeekEnd(GpUFilePos_t loc)
return m_rw->seek(m_rw, -static_cast<Sint64>(loc), RW_SEEK_END) >= 0;
}
bool GpFileStream_SDLRWops::Truncate(GpUFilePos_t loc)
{
return false;
}
GpUFilePos_t GpFileStream_SDLRWops::Size() const
{
return m_rw->size(m_rw);
@@ -346,12 +333,6 @@ bool GpFileStream_Android_File::SeekEnd(GpUFilePos_t loc)
return lseek64(m_fd, -static_cast<off64_t>(loc), SEEK_END) >= 0;
}
bool GpFileStream_Android_File::Truncate(GpUFilePos_t loc)
{
fflush(m_f);
return ftruncate64(m_fd, static_cast<off64_t>(loc)) >= 0;
}
GpUFilePos_t GpFileStream_Android_File::Size() const
{
fflush(m_f);
@@ -552,26 +533,26 @@ bool GpFileSystem_Android::FileExists(PortabilityLayer::VirtualDirectory_t virtu
return stat(resolvedPath.c_str(), &s) == 0;
}
bool GpFileSystem_Android::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists)
bool GpFileSystem_Android::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists)
{
std::string resolvedPath;
bool isAsset;
if (!ResolvePath(virtualDirectory, &path, 1, resolvedPath, isAsset))
{
if (exists)
*exists = false;
exists = false;
return false;
}
if (isAsset)
{
if (exists)
*exists = this->FileExists(virtualDirectory, path);
exists = this->FileExists(virtualDirectory, path);
return true;
}
int permissions = access(resolvedPath.c_str(), W_OK | F_OK);
*exists = ((permissions & F_OK) != 0);
exists = ((permissions & F_OK) != 0);
return ((permissions & W_OK) != 0);
}
@@ -733,7 +714,7 @@ bool GpFileSystem_Android::ValidateFilePath(const char *path, size_t length) con
if (c >= '0' && c <= '9')
continue;
if (c == '_' || c == '.' || c == '\'')
if (c == '_' || c == '.' || c == '\'' || c == '!')
continue;
if (c == ' ' && i != 0 && i != length - 1)
@@ -771,11 +752,6 @@ bool GpFileSystem_Android::ValidateFilePathUnicodeChar(uint32_t c) const
return false;
}
bool GpFileSystem_Android::IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const
{
return false;
}
void GpFileSystem_Android::SetMainThreadRelay(IGpThreadRelay *relay)
{
m_relay = relay;

View File

@@ -19,7 +19,7 @@ public:
void ShutdownJNI();
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;
GpIOStream *OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) override;
bool DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) override;
IGpDirectoryCursor *ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) override;
@@ -27,8 +27,6 @@ public:
bool ValidateFilePath(const char *path, size_t pathLen) const override;
bool ValidateFilePathUnicodeChar(uint32_t ch) const override;
bool IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const override;
void SetMainThreadRelay(IGpThreadRelay *relay) override;
void SetDelayCallback(DelayCallback_t delayCallback) override;

View File

@@ -8,6 +8,7 @@
#include "GpFileSystem_Android.h"
#include "GpFontHandlerFactory.h"
#include "GpInputDriverFactory.h"
#include "GpInputDriver_SDL_Gamepad.h"
#include "GpAppInterface.h"
#include "GpSystemServices_Android.h"
#include "GpVOSEvent.h"
@@ -116,3 +117,8 @@ int main(int argc, char* argv[])
return returnCode;
}
IGpInputDriverSDLGamepad *IGpInputDriverSDLGamepad::GetInstance()
{
return nullptr;
}

View File

@@ -1,11 +1,9 @@
#include "GpSystemServices_Android.h"
#include "IGpMutex.h"
#include "IGpThreadEvent.h"
#include "SDL.h"
#include <time.h>
#include <mutex>
#include <condition_variable>
#include <unistd.h>
struct GpSystemServices_Android_ThreadStartParams
@@ -28,175 +26,11 @@ static int SDLCALL StaticStartThread(void *lpThreadParameter)
return threadFunc(threadContext);
}
template<class TMutex>
class GpMutex_Cpp11 final : public IGpMutex
{
public:
GpMutex_Cpp11();
~GpMutex_Cpp11();
void Destroy() override;
void Lock() override;
void Unlock() override;
private:
TMutex m_mutex;
};
template<class TMutex>
GpMutex_Cpp11<TMutex>::GpMutex_Cpp11()
{
}
template<class TMutex>
GpMutex_Cpp11<TMutex>::~GpMutex_Cpp11()
{
}
template<class TMutex>
void GpMutex_Cpp11<TMutex>::Destroy()
{
this->~GpMutex_Cpp11();
free(this);
}
template<class TMutex>
void GpMutex_Cpp11<TMutex>::Lock()
{
m_mutex.lock();
}
template<class TMutex>
void GpMutex_Cpp11<TMutex>::Unlock()
{
m_mutex.unlock();
}
typedef GpMutex_Cpp11<std::mutex> GpMutex_Cpp11_Vanilla;
typedef GpMutex_Cpp11<std::recursive_mutex> GpMutex_Cpp11_Recursive;
class GpThreadEvent_Cpp11 final : public IGpThreadEvent
{
public:
GpThreadEvent_Cpp11(bool autoReset, bool startSignaled);
~GpThreadEvent_Cpp11();
void Wait() override;
bool WaitTimed(uint32_t msec) override;
void Signal() override;
void Destroy() override;
private:
std::mutex m_mutex;
std::condition_variable m_cvar;
bool m_flag;
bool m_autoReset;
};
GpThreadEvent_Cpp11::GpThreadEvent_Cpp11(bool autoReset, bool startSignaled)
: m_flag(startSignaled)
, m_autoReset(autoReset)
{
}
GpThreadEvent_Cpp11::~GpThreadEvent_Cpp11()
{
}
void GpThreadEvent_Cpp11::Wait()
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_autoReset)
{
m_cvar.wait(lock,[&]()->bool{
if (m_flag)
{
m_flag = false;
return true;
}
else
return false;
});
}
else
m_cvar.wait(lock,[&]()->bool{ return m_flag; });
}
bool GpThreadEvent_Cpp11::WaitTimed(uint32_t msec)
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_autoReset)
{
if (!m_cvar.wait_for(lock, std::chrono::milliseconds(msec), [&]()->bool{
if (m_flag)
{
m_flag = false;
return true;
}
else
return false;
}))
return false;
}
else
{
if (!m_cvar.wait_for(lock, std::chrono::milliseconds(msec), [&]()->bool{ return m_flag; }))
return false;
}
return true;
}
void GpThreadEvent_Cpp11::Signal()
{
m_mutex.lock();
m_flag = true;
m_mutex.unlock();
if (m_autoReset)
m_cvar.notify_one();
else
m_cvar.notify_all();
}
void GpThreadEvent_Cpp11::Destroy()
{
this->~GpThreadEvent_Cpp11();
free(this);
}
GpSystemServices_Android::GpSystemServices_Android()
: m_textInputEnabled(false)
{
}
int64_t GpSystemServices_Android::GetTime() const
{
time_t t = time(nullptr);
return static_cast<int64_t>(t) - 2082844800;
}
void GpSystemServices_Android::GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const
{
time_t t = time(nullptr);
tm *tmObject = localtime(&t);
year = static_cast<unsigned int>(tmObject->tm_year);
month = static_cast<unsigned int>(tmObject->tm_mon + 1);
hour = static_cast<unsigned int>(tmObject->tm_hour);
minute = static_cast<unsigned int>(tmObject->tm_min);
second = static_cast<unsigned int>(tmObject->tm_sec);
}
IGpMutex *GpSystemServices_Android::CreateMutex()
{
GpMutex_Cpp11_Vanilla *mutex = static_cast<GpMutex_Cpp11_Vanilla*>(malloc(sizeof(GpMutex_Cpp11_Vanilla)));
if (!mutex)
return nullptr;
return new (mutex) GpMutex_Cpp11_Vanilla();
}
void *GpSystemServices_Android::CreateThread(ThreadFunc_t threadFunc, void *context)
{
IGpThreadEvent *evt = CreateThreadEvent(true, false);
@@ -221,31 +55,6 @@ void *GpSystemServices_Android::CreateThread(ThreadFunc_t threadFunc, void *cont
return thread;
}
IGpMutex *GpSystemServices_Android::CreateRecursiveMutex()
{
GpMutex_Cpp11_Recursive *mutex = static_cast<GpMutex_Cpp11_Recursive*>(malloc(sizeof(GpMutex_Cpp11_Recursive)));
if (!mutex)
return nullptr;
return new (mutex) GpMutex_Cpp11_Recursive();
}
IGpThreadEvent *GpSystemServices_Android::CreateThreadEvent(bool autoReset, bool startSignaled)
{
GpThreadEvent_Cpp11 *evt = static_cast<GpThreadEvent_Cpp11*>(malloc(sizeof(GpThreadEvent_Cpp11)));
if (!evt)
return nullptr;
return new (evt) GpThreadEvent_Cpp11(autoReset, startSignaled);
}
uint64_t GpSystemServices_Android::GetFreeMemoryCosmetic() const
{
long pages = sysconf(_SC_AVPHYS_PAGES);
long pageSize = sysconf(_SC_PAGE_SIZE);
return pages * pageSize;
}
void GpSystemServices_Android::Beep() const
{
}
@@ -270,6 +79,11 @@ bool GpSystemServices_Android::IsFullscreenPreferred() const
return true;
}
bool GpSystemServices_Android::IsFullscreenOnStartup() const
{
return true;
}
unsigned int GpSystemServices_Android::GetCPUCount() const
{
return SDL_GetCPUCount();
@@ -290,6 +104,15 @@ bool GpSystemServices_Android::AreFontResourcesSeekable() const
return false;
}
IGpClipboardContents *GpSystemServices_Android::GetClipboardContents() const
{
return nullptr;
}
void GpSystemServices_Android::SetClipboardContents(IGpClipboardContents *contents)
{
}
GpSystemServices_Android *GpSystemServices_Android::GetInstance()
{
return &ms_instance;

View File

@@ -1,29 +1,26 @@
#pragma once
#include "IGpSystemServices.h"
#include "GpSystemServices_POSIX.h"
#include "GpCoreDefs.h"
class GpSystemServices_Android final : public IGpSystemServices
class GpSystemServices_Android final : public GpSystemServices_POSIX
{
public:
GpSystemServices_Android();
int64_t GetTime() const override;
void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const override;
IGpMutex *CreateMutex() override;
IGpMutex *CreateRecursiveMutex() override;
void *CreateThread(ThreadFunc_t threadFunc, void *context) override;
IGpThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) override;
uint64_t GetFreeMemoryCosmetic() const override;
void Beep() const override;
bool IsTouchscreen() const override;
bool IsUsingMouseAsTouch() const override;
bool IsTextInputObstructive() const override;
bool IsFullscreenPreferred() const override;
bool IsFullscreenOnStartup() const override;
unsigned int GetCPUCount() const override;
void SetTextInputEnabled(bool isEnabled) override;
bool IsTextInputEnabled() const override;
bool AreFontResourcesSeekable() const override;
IGpClipboardContents *GetClipboardContents() const override;
void SetClipboardContents(IGpClipboardContents *contents) override;
void FlushTextInputEnabled();

View File

@@ -7,6 +7,6 @@ mkdir Packaged
cd Packaged
rmdir /S /Q Houses
mkdir Houses
copy ..\..\..\..\..\..\Packaged\*.gpa .\
copy ..\..\..\..\..\..\Packaged\*.gpf .\
copy ..\..\..\..\..\..\Packaged\Houses\* Houses\
cd ..

View File

@@ -5,6 +5,7 @@ call remove_symlinks.bat
mklink /D app\jni\AerofoilSDL ..\..\..\AerofoilSDL
mklink /D app\jni\AerofoilPortable ..\..\..\AerofoilPortable
mklink /D app\jni\Common ..\..\..\Common
mklink /D app\jni\SDL2 ..\..\..\SDL2-2.0.12
mklink /D app\jni\GpApp ..\..\..\GpApp

View File

@@ -2,6 +2,7 @@
@cd /d "%~dp0"
rmdir app\jni\AerofoilSDL
rmdir app\jni\AerofoilPortable
rmdir app\jni\Common
rmdir app\jni\SDL2
rmdir app\jni\GpShell

View File

@@ -0,0 +1,20 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := AerofoilPortable
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/../GpCommon \
$(LOCAL_PATH)/../GpShell \
$(LOCAL_PATH)/../Common \
$(LOCAL_PATH)/../PortabilityLayer
LOCAL_CFLAGS := -DGP_DEBUG_CONFIG=0
# Add your application source files here...
LOCAL_SRC_FILES := \
GpThreadEvent_Cpp11.cpp \
GpSystemServices_POSIX.cpp
include $(BUILD_STATIC_LIBRARY)

View File

@@ -0,0 +1,66 @@
#pragma once
#include "IGpMutex.h"
#include <mutex>
template<class TMutex>
class GpMutex_Cpp11 final : public IGpMutex
{
public:
~GpMutex_Cpp11();
void Destroy() override;
static GpMutex_Cpp11<TMutex> *Create();
void Lock() override;
void Unlock() override;
private:
GpMutex_Cpp11();
TMutex m_mutex;
};
#include <stdlib.h>
template<class TMutex>
GpMutex_Cpp11<TMutex>::GpMutex_Cpp11()
{
}
template<class TMutex>
GpMutex_Cpp11<TMutex>::~GpMutex_Cpp11()
{
}
template<class TMutex>
void GpMutex_Cpp11<TMutex>::Destroy()
{
this->~GpMutex_Cpp11();
free(this);
}
template<class TMutex>
GpMutex_Cpp11<TMutex> *GpMutex_Cpp11<TMutex>::Create()
{
GpMutex_Cpp11<TMutex> *mutex = static_cast<GpMutex_Cpp11<TMutex>*>(malloc(sizeof(GpMutex_Cpp11<TMutex>)));
if (!mutex)
return nullptr;
return new (mutex) GpMutex_Cpp11<TMutex>();
}
template<class TMutex>
void GpMutex_Cpp11<TMutex>::Lock()
{
m_mutex.lock();
}
template<class TMutex>
void GpMutex_Cpp11<TMutex>::Unlock()
{
m_mutex.unlock();
}
typedef GpMutex_Cpp11<std::mutex> GpMutex_Cpp11_NonRecursive;
typedef GpMutex_Cpp11<std::recursive_mutex> GpMutex_Cpp11_Recursive;

View File

@@ -0,0 +1,50 @@
#include "GpSystemServices_POSIX.h"
#include "GpMutex_Cpp11.h"
#include "GpThreadEvent_Cpp11.h"
#include <time.h>
#include <unistd.h>
GpSystemServices_POSIX::GpSystemServices_POSIX()
{
}
int64_t GpSystemServices_POSIX::GetTime() const
{
time_t t = time(nullptr);
return static_cast<int64_t>(t) - 2082844800;
}
void GpSystemServices_POSIX::GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const
{
time_t t = time(nullptr);
tm *tmObject = localtime(&t);
year = static_cast<unsigned int>(tmObject->tm_year);
month = static_cast<unsigned int>(tmObject->tm_mon + 1);
hour = static_cast<unsigned int>(tmObject->tm_hour);
minute = static_cast<unsigned int>(tmObject->tm_min);
second = static_cast<unsigned int>(tmObject->tm_sec);
}
IGpMutex *GpSystemServices_POSIX::CreateMutex()
{
return GpMutex_Cpp11_NonRecursive::Create();
}
IGpMutex *GpSystemServices_POSIX::CreateRecursiveMutex()
{
return GpMutex_Cpp11_Recursive::Create();
}
IGpThreadEvent *GpSystemServices_POSIX::CreateThreadEvent(bool autoReset, bool startSignaled)
{
return GpThreadEvent_Cpp11::Create(autoReset, startSignaled);
}
uint64_t GpSystemServices_POSIX::GetFreeMemoryCosmetic() const
{
long pages = sysconf(_SC_AVPHYS_PAGES);
long pageSize = sysconf(_SC_PAGE_SIZE);
return pages * pageSize;
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "IGpSystemServices.h"
#include "GpCoreDefs.h"
class GpSystemServices_POSIX : public IGpSystemServices
{
public:
GpSystemServices_POSIX();
int64_t GetTime() const override;
void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const override;
IGpMutex *CreateMutex() override;
IGpMutex *CreateRecursiveMutex() override;
IGpThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) override;
uint64_t GetFreeMemoryCosmetic() const override;
};

View File

@@ -0,0 +1,82 @@
#include "GpThreadEvent_Cpp11.h"
GpThreadEvent_Cpp11::GpThreadEvent_Cpp11(bool autoReset, bool startSignaled)
: m_flag(startSignaled)
, m_autoReset(autoReset)
{
}
GpThreadEvent_Cpp11::~GpThreadEvent_Cpp11()
{
}
void GpThreadEvent_Cpp11::Wait()
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_autoReset)
{
m_cvar.wait(lock,[&]()->bool{
if (m_flag)
{
m_flag = false;
return true;
}
else
return false;
});
}
else
m_cvar.wait(lock,[&]()->bool{ return m_flag; });
}
bool GpThreadEvent_Cpp11::WaitTimed(uint32_t msec)
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_autoReset)
{
if (!m_cvar.wait_for(lock, std::chrono::milliseconds(msec), [&]()->bool{
if (m_flag)
{
m_flag = false;
return true;
}
else
return false;
}))
return false;
}
else
{
if (!m_cvar.wait_for(lock, std::chrono::milliseconds(msec), [&]()->bool{ return m_flag; }))
return false;
}
return true;
}
void GpThreadEvent_Cpp11::Signal()
{
m_mutex.lock();
m_flag = true;
m_mutex.unlock();
if (m_autoReset)
m_cvar.notify_one();
else
m_cvar.notify_all();
}
void GpThreadEvent_Cpp11::Destroy()
{
this->~GpThreadEvent_Cpp11();
free(this);
}
GpThreadEvent_Cpp11 *GpThreadEvent_Cpp11::Create(bool autoReset, bool startSignaled)
{
GpThreadEvent_Cpp11 *evt = static_cast<GpThreadEvent_Cpp11*>(malloc(sizeof(GpThreadEvent_Cpp11)));
if (!evt)
return nullptr;
return new (evt) GpThreadEvent_Cpp11(autoReset, startSignaled);
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include "IGpThreadEvent.h"
#include <mutex>
#include <condition_variable>
class GpThreadEvent_Cpp11 final : public IGpThreadEvent
{
public:
~GpThreadEvent_Cpp11();
void Wait() override;
bool WaitTimed(uint32_t msec) override;
void Signal() override;
void Destroy() override;
static GpThreadEvent_Cpp11 *Create(bool autoReset, bool startSignaled);
private:
GpThreadEvent_Cpp11(bool autoReset, bool startSignaled);
GpThreadEvent_Cpp11() = delete;
std::mutex m_mutex;
std::condition_variable m_cvar;
bool m_flag;
bool m_autoReset;
};

View File

@@ -95,8 +95,10 @@
<ClCompile Include="GpDisplayDriver_SDL_GL2.cpp" />
<ClCompile Include="GpFiberStarter_SDL.cpp" />
<ClCompile Include="GpFiber_SDL.cpp" />
<ClCompile Include="GpInputDriver_SDL_Gamepad.cpp" />
<ClCompile Include="GpMain_SDL_Win32.cpp" />
<ClCompile Include="ShaderCode\CopyQuadP.cpp" />
<ClCompile Include="ShaderCode\DrawQuad32P.cpp" />
<ClCompile Include="ShaderCode\DrawQuadPaletteP.cpp" />
<ClCompile Include="ShaderCode\DrawQuadV.cpp" />
<ClCompile Include="ShaderCode\ScaleQuadP.cpp" />
@@ -111,15 +113,13 @@
<ProjectReference Include="..\GpFontHandler_FreeType2\GpFontHandler_FreeType2.vcxproj">
<Project>{4b564030-8985-4975-91e1-e1b2c16ae2a1}</Project>
</ProjectReference>
<ProjectReference Include="..\GpInputDriver_XInput\GpInputDriver_XInput.vcxproj">
<Project>{17b96f07-ef92-47cd-95a5-8e6ee38ab564}</Project>
</ProjectReference>
<ProjectReference Include="..\GpShell\GpShell.vcxproj">
<Project>{10cf9b5f-61d0-4b5b-89f4-810b58fc053d}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClInclude Include="GpFiber_SDL.h" />
<ClInclude Include="GpInputDriver_SDL_Gamepad.h" />
<ClInclude Include="ShaderCode\DrawQuadPixelConstants.h" />
<ClInclude Include="ShaderCode\Functions.h" />
</ItemGroup>

View File

@@ -66,6 +66,12 @@
<ClCompile Include="ShaderCode\CopyQuadP.cpp">
<Filter>Source Files\ShaderCode</Filter>
</ClCompile>
<ClCompile Include="ShaderCode\DrawQuad32P.cpp">
<Filter>Source Files\ShaderCode</Filter>
</ClCompile>
<ClCompile Include="GpInputDriver_SDL_Gamepad.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ShaderCode\Functions.h">
@@ -77,5 +83,8 @@
<ClInclude Include="GpFiber_SDL.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GpInputDriver_SDL_Gamepad.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -23,6 +23,7 @@ LOCAL_SRC_FILES := \
GpFiberStarter_SDL.cpp \
ShaderCode/CopyQuadP.cpp \
ShaderCode/DrawQuadPaletteP.cpp \
ShaderCode/DrawQuad32P.cpp \
ShaderCode/DrawQuadV.cpp \
ShaderCode/ScaleQuadP.cpp

View File

@@ -136,6 +136,7 @@ private:
static const size_t kMaxChannels = 16;
static const size_t kMixChunkSize = 256;
static const int16_t kMaxAudioVolumeScale = 25;
GpAudioChannel_SDL2 *m_channels[kMaxChannels];
size_t m_numChannels;
@@ -144,6 +145,8 @@ private:
GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) int16_t m_mixChunk[kMixChunkSize];
size_t m_mixChunkReadOffset;
int16_t m_audioVolumeScale;
};
GpAudioChannelBufferChain_SDL2::GpAudioChannelBufferChain_SDL2()
@@ -353,6 +356,7 @@ GpAudioDriver_SDL2::GpAudioDriver_SDL2(const GpAudioDriverProperties &properties
, m_numChannels(0)
, m_sdlAudioRunning(false)
, m_mixChunkReadOffset(kMixChunkSize)
, m_audioVolumeScale(kMaxAudioVolumeScale)
{
for (size_t i = 0; i < kMaxChannels; i++)
@@ -395,6 +399,9 @@ IGpAudioChannel *GpAudioDriver_SDL2::CreateChannel()
void GpAudioDriver_SDL2::SetMasterVolume(uint32_t vol, uint32_t maxVolume)
{
double scale = vol * static_cast<uint64_t>(kMaxAudioVolumeScale) / maxVolume;
m_audioVolumeScale = static_cast<int16_t>(scale);
}
void GpAudioDriver_SDL2::Shutdown()
@@ -527,6 +534,8 @@ void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, siz
bool noAudio = true;
const int16_t audioVolumeScale = m_audioVolumeScale;
for (size_t i = 0; i < numChannels; i++)
{
channels[i]->Consume(audioMixBuffer, kMixChunkSize);
@@ -535,12 +544,12 @@ void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, siz
{
noAudio = false;
for (size_t j = 0; j < kMixChunkSize; j++)
m_mixChunk[j] = (audioMixBuffer[j] - 0x80) * 25;
m_mixChunk[j] = (static_cast<int16_t>(audioMixBuffer[j]) - 0x80) * audioVolumeScale;
}
else
{
for (size_t j = 0; j < kMixChunkSize; j++)
m_mixChunk[j] += (audioMixBuffer[j] - 0x80) * 25;
m_mixChunk[j] += (static_cast<int16_t>(audioMixBuffer[j]) - 0x80) * audioVolumeScale;
}
}

View File

@@ -6,6 +6,7 @@
#include "GpDisplayDriverProperties.h"
#include "GpVOSEvent.h"
#include "GpRingBuffer.h"
#include "GpInputDriver_SDL_Gamepad.h"
#include "IGpCursor.h"
#include "IGpDisplayDriverSurface.h"
#include "IGpLogDriver.h"
@@ -39,6 +40,13 @@ class GpDisplayDriver_SDL_GL2;
static GpDisplayDriverSurfaceEffects gs_defaultEffects;
static const char *kPrefsIdentifier = "GpDisplayDriverSDL_GL2";
static uint32_t kPrefsVersion = 1;
struct GpDisplayDriver_SDL_GL2_Prefs
{
bool m_isFullScreen;
};
namespace DeleteMe
{
@@ -108,17 +116,13 @@ namespace GpBinarizedShaders
extern const char *g_drawQuadPalettePF_GL2;
extern const char *g_drawQuadPalettePNF_GL2;
extern const char *g_drawQuadRGBPF_GL2;
extern const char *g_drawQuadRGBPNF_GL2;
extern const char *g_drawQuad15BitPF_GL2;
extern const char *g_drawQuad15BitPNF_GL2;
extern const char *g_drawQuad32PF_GL2;
extern const char *g_drawQuad32PNF_GL2;
extern const char *g_drawQuadPaletteICCPF_GL2;
extern const char *g_drawQuadPaletteICCPNF_GL2;
extern const char *g_drawQuadRGBICCPF_GL2;
extern const char *g_drawQuadRGBICCPNF_GL2;
extern const char *g_drawQuad15BitICCPF_GL2;
extern const char *g_drawQuad15BitICCPNF_GL2;
extern const char *g_drawQuad32ICCPF_GL2;
extern const char *g_drawQuad32ICCPNF_GL2;
extern const char *g_copyQuadP_GL2;
extern const char *g_scaleQuadP_GL2;
@@ -849,12 +853,16 @@ private:
DrawQuadProgram m_drawQuadPaletteNoFlickerProgram;
DrawQuadProgram m_drawQuadPaletteFlickerProgram;
DrawQuadProgram m_drawQuadRGBProgram;
DrawQuadProgram m_drawQuad15BitProgram;
DrawQuadProgram m_drawQuad15NoFlickerProgram;
DrawQuadProgram m_drawQuad15FlickerProgram;
DrawQuadProgram m_drawQuad32NoFlickerProgram;
DrawQuadProgram m_drawQuad32FlickerProgram;
DrawQuadProgram m_drawQuadPaletteICCNoFlickerProgram;
DrawQuadProgram m_drawQuadPaletteICCFlickerProgram;
DrawQuadProgram m_drawQuadRGBICCProgram;
DrawQuadProgram m_drawQuad15BitICCProgram;
DrawQuadProgram m_drawQuad15ICCNoFlickerProgram;
DrawQuadProgram m_drawQuad15ICCFlickerProgram;
DrawQuadProgram m_drawQuad32ICCNoFlickerProgram;
DrawQuadProgram m_drawQuad32ICCFlickerProgram;
};
InstancedResources m_res;
@@ -1219,7 +1227,7 @@ GpDisplayDriver_SDL_GL2::GpDisplayDriver_SDL_GL2(const GpDisplayDriverProperties
m_bgColor[3] = 1.f;
// Stupid hack to detect mobile...
m_isFullScreenDesired = m_properties.m_systemServices->IsFullscreenPreferred();
m_isFullScreenDesired = m_properties.m_systemServices->IsFullscreenOnStartup();
const intmax_t periodNum = std::chrono::high_resolution_clock::period::num;
const intmax_t periodDen = std::chrono::high_resolution_clock::period::den;
@@ -1842,7 +1850,7 @@ void GpDisplayDriver_SDL_GL2::Run()
m_vosFiber = new GpFiber_SDL(nullptr, m_vosEvent);
uint32_t windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
if (m_isFullScreenDesired)
if (m_properties.m_systemServices->IsFullscreenOnStartup())
{
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
m_isFullScreen = true;
@@ -1909,19 +1917,35 @@ void GpDisplayDriver_SDL_GL2::Run()
SDL_Event msg;
if (SDL_PollEvent(&msg) != 0)
{
if (msg.type == SDL_MOUSEMOTION)
switch (msg.type)
{
if (!m_mouseIsInClientArea)
m_mouseIsInClientArea = true;
}
//else if (msg.type == SDL_MOUSELEAVE) // Does SDL support this??
case SDL_MOUSEMOTION:
{
if (!m_mouseIsInClientArea)
m_mouseIsInClientArea = true;
}
break;
//case SDL_MOUSELEAVE: // Does SDL support this??
// m_mouseIsInClientArea = false;
else if (msg.type == SDL_RENDER_DEVICE_RESET || msg.type == SDL_RENDER_TARGETS_RESET)
{
if (logger)
logger->Printf(IGpLogDriver::Category_Information, "Triggering GL context reset due to device loss (Type: %i)", static_cast<int>(msg.type));
// break;
case SDL_RENDER_DEVICE_RESET:
case SDL_RENDER_TARGETS_RESET:
{
if (logger)
logger->Printf(IGpLogDriver::Category_Information, "Triggering GL context reset due to device loss (Type: %i)", static_cast<int>(msg.type));
m_contextLost = true;
m_contextLost = true;
}
break;
case SDL_CONTROLLERAXISMOTION:
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
case SDL_CONTROLLERDEVICEADDED:
case SDL_CONTROLLERDEVICEREMOVED:
case SDL_CONTROLLERDEVICEREMAPPED:
if (IGpInputDriverSDLGamepad *gamepadDriver = IGpInputDriverSDLGamepad::GetInstance())
gamepadDriver->ProcessSDLEvent(msg);
break;
}
TranslateSDLMessage(&msg, m_properties.m_eventQueue, m_pixelScaleX, m_pixelScaleY, obstructiveTextInput);
@@ -2117,7 +2141,20 @@ void GpDisplayDriver_SDL_GL2::DrawSurface(IGpDisplayDriverSurface *surface, int3
}
else if (pixelFormat == GpPixelFormats::kRGB32)
{
return;
if (m_useICCProfile)
{
if (effects->m_flicker)
program = &m_res.m_drawQuad32ICCFlickerProgram;
else
program = &m_res.m_drawQuad32ICCNoFlickerProgram;
}
else
{
if (effects->m_flicker)
program = &m_res.m_drawQuad32FlickerProgram;
else
program = &m_res.m_drawQuad32NoFlickerProgram;
}
}
else
{
@@ -2345,11 +2382,19 @@ bool GpDisplayDriver_SDL_GL2::SupportsSizedFormats() const
void GpDisplayDriver_SDL_GL2::ApplyPrefs(const void *identifier, size_t identifierSize, const void *contents, size_t contentsSize, uint32_t version)
{
if (version == kPrefsVersion && identifierSize == strlen(kPrefsIdentifier) && !memcmp(identifier, kPrefsIdentifier, identifierSize))
{
const GpDisplayDriver_SDL_GL2_Prefs *prefs = static_cast<const GpDisplayDriver_SDL_GL2_Prefs *>(contents);
m_isFullScreenDesired = prefs->m_isFullScreen;
}
}
bool GpDisplayDriver_SDL_GL2::SavePrefs(void *context, WritePrefsFunc_t writeFunc)
bool GpDisplayDriver_SDL_GL2::SavePrefs(void *context, IGpPrefsHandler::WritePrefsFunc_t writeFunc)
{
return true;
GpDisplayDriver_SDL_GL2_Prefs prefs;
prefs.m_isFullScreen = m_isFullScreenDesired;
return writeFunc(context, kPrefsIdentifier, strlen(kPrefsIdentifier), &prefs, sizeof(prefs), kPrefsVersion);
}
void GpDisplayDriver_SDL_GL2::UnlinkSurface(GpDisplayDriverSurface_GL2 *surface, GpDisplayDriverSurface_GL2 *prev, GpDisplayDriverSurface_GL2 *next)
@@ -2496,23 +2541,29 @@ bool GpDisplayDriver_SDL_GL2::InitResources(uint32_t physicalWidth, uint32_t phy
}
GpComPtr<GpGLShader<GL_VERTEX_SHADER>> drawQuadVertexShader = CreateShader<GL_VERTEX_SHADER>(GpBinarizedShaders::g_drawQuadV_GL2);
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteFlickerPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPalettePF_GL2);
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteNoFlickerPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPalettePNF_GL2);
//m_drawQuadRGBPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadRGBP_GL2);
//m_drawQuad15BitPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad15BitP_GL2);
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuad32FlickerPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad32PF_GL2);
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuad32NoFlickerPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad32PNF_GL2);
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteICCFPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPaletteICCPF_GL2);
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuadPaletteICCNFPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadPaletteICCPNF_GL2);
//m_drawQuadRGBICCPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuadRGBICCP_GL2);
//m_drawQuad15BitICCPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad15BitICCP_GL2);
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuad32ICCFPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad32ICCPF_GL2);
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> drawQuad32ICCNFPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_drawQuad32ICCPNF_GL2);
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> scaleQuadPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_scaleQuadP_GL2);
GpComPtr<GpGLShader<GL_FRAGMENT_SHADER>> copyQuadPixelShader = CreateShader<GL_FRAGMENT_SHADER>(GpBinarizedShaders::g_copyQuadP_GL2);
if (!m_res.m_drawQuadPaletteFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteFlickerPixelShader)
|| !m_res.m_drawQuadPaletteNoFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteFlickerPixelShader)
//|| !m_drawQuadRGBProgram.Link(this, drawQuadVertexShader, drawQuadRGBPixelShader)
//|| !m_drawQuad15BitProgram.Link(this, drawQuadVertexShader, drawQuad15BitPixelShader)
|| !m_res.m_drawQuadPaletteNoFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteNoFlickerPixelShader)
|| !m_res.m_drawQuad32FlickerProgram.Link(this, drawQuadVertexShader, drawQuad32FlickerPixelShader)
|| !m_res.m_drawQuad32NoFlickerProgram.Link(this, drawQuadVertexShader, drawQuad32NoFlickerPixelShader)
|| !m_res.m_drawQuadPaletteICCFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteICCFPixelShader)
|| !m_res.m_drawQuadPaletteICCNoFlickerProgram.Link(this, drawQuadVertexShader, drawQuadPaletteICCNFPixelShader)
|| !m_res.m_drawQuad32ICCFlickerProgram.Link(this, drawQuadVertexShader, drawQuad32ICCFPixelShader)
|| !m_res.m_drawQuad32ICCNoFlickerProgram.Link(this, drawQuadVertexShader, drawQuad32ICCNFPixelShader)
//|| !m_drawQuadRGBICCProgram.Link(this, drawQuadVertexShader, drawQuadRGBICCPixelShader)
//|| !m_drawQuad15BitICCProgram.Link(this, drawQuadVertexShader, drawQuad15BitICCPixelShader)
|| !m_res.m_scaleQuadProgram.Link(this, drawQuadVertexShader, scaleQuadPixelShader)

View File

@@ -0,0 +1,309 @@
#include "GpInputDriver_SDL_Gamepad.h"
#include "GpVOSEvent.h"
#include "GpWindows.h"
#include "IGpVOSEventQueue.h"
#include "SDL_events.h"
#include "SDL_joystick.h"
#include "SDL_gamecontroller.h"
#include <stdlib.h>
#include <algorithm>
#include <new>
#include <vector>
class GpInputDriverSDLGamepad final : public IGpInputDriverSDLGamepad
{
public:
void ProcessInput() override;
void Shutdown() override;
IGpPrefsHandler *GetPrefsHandler() const override;
static GpInputDriverSDLGamepad *Create(const GpInputDriverProperties &props);
void ProcessSDLEvent(const SDL_Event &evt) override;
private:
static const int kMaxPlayers = 2;
GpInputDriverSDLGamepad(const GpInputDriverProperties &props);
~GpInputDriverSDLGamepad();
bool FindJoystickPlayer(uint8_t &playerNum, SDL_JoystickID joystickID);
void HandleDeviceAdded(SDL_JoystickID joystickID);
void HandleDeviceRemoved(SDL_JoystickID joystickID);
std::vector<GpVOSEvent> m_pendingEvents;
GpInputDriverProperties m_properties;
SDL_JoystickID m_playerJoystickIDs[kMaxPlayers];
SDL_GameController *m_playerControllers[kMaxPlayers];
bool m_playerButtonDown[kMaxPlayers][GpGamepadButtons::kCount];
};
static GpInputDriverSDLGamepad *gs_instance = nullptr;
IGpInputDriverSDLGamepad *IGpInputDriverSDLGamepad::GetInstance()
{
return gs_instance;
}
void GpInputDriverSDLGamepad::ProcessInput()
{
for (size_t i = 0; i < m_pendingEvents.size(); i++)
{
if (GpVOSEvent *evt = m_properties.m_eventQueue->QueueEvent())
*evt = m_pendingEvents[i];
}
m_pendingEvents.clear();
}
void GpInputDriverSDLGamepad::Shutdown()
{
this->~GpInputDriverSDLGamepad();
free(this);
gs_instance = nullptr;
}
IGpPrefsHandler *GpInputDriverSDLGamepad::GetPrefsHandler() const
{
return nullptr;
}
GpInputDriverSDLGamepad *GpInputDriverSDLGamepad::Create(const GpInputDriverProperties &props)
{
void *storage = malloc(sizeof(GpInputDriverSDLGamepad));
if (!storage)
return nullptr;
GpInputDriverSDLGamepad *driver = new (storage) GpInputDriverSDLGamepad(props);
gs_instance = driver;
return driver;
}
GpInputDriverSDLGamepad::GpInputDriverSDLGamepad(const GpInputDriverProperties &props)
: m_properties(props)
{
for (int i = 0; i < kMaxPlayers; i++)
{
m_playerJoystickIDs[i] = -1;
m_playerControllers[i] = nullptr;
for (int j = 0; j < GpGamepadButtons::kCount; j++)
m_playerButtonDown[i][j] = false;
}
}
GpInputDriverSDLGamepad::~GpInputDriverSDLGamepad()
{
for (int i = 0; i < kMaxPlayers; i++)
{
if (m_playerControllers[i])
SDL_GameControllerClose(m_playerControllers[i]);
}
}
bool GpInputDriverSDLGamepad::FindJoystickPlayer(uint8_t &playerNum, SDL_JoystickID joystickID)
{
for (int i = 0; i < kMaxPlayers; i++)
{
if (m_playerJoystickIDs[i] == joystickID)
{
playerNum = static_cast<uint8_t>(i);
return true;
}
}
return false;
}
void GpInputDriverSDLGamepad::HandleDeviceAdded(SDL_JoystickID joystickID)
{
for (int i = 0; i < kMaxPlayers; i++)
{
if (m_playerJoystickIDs[i] == -1)
{
SDL_GameController *controller = SDL_GameControllerOpen(joystickID);
if (controller)
{
m_playerJoystickIDs[i] = joystickID;
m_playerControllers[i] = controller;
}
return;
}
}
}
void GpInputDriverSDLGamepad::HandleDeviceRemoved(SDL_JoystickID joystickID)
{
int playerNum = 0;
bool foundPlayer = false;
for (int i = 0; i < kMaxPlayers; i++)
{
if (m_playerJoystickIDs[i] == joystickID)
{
SDL_GameControllerClose(m_playerControllers[i]);
m_playerJoystickIDs[i] = -1;
m_playerControllers[i] = nullptr;
playerNum = i;
foundPlayer = true;
}
}
if (!foundPlayer)
return;
for (int axis = 0; axis < GpGamepadAxes::kCount; axis++)
{
GpVOSEvent evt;
evt.m_eventType = GpVOSEventTypes::kGamepadInput;
evt.m_event.m_gamepadInputEvent.m_eventType = GpGamepadInputEventTypes::kAnalogAxisChanged;
evt.m_event.m_gamepadInputEvent.m_event.m_analogAxisEvent.m_axis = static_cast<GpGamepadAxis_t>(axis);
evt.m_event.m_gamepadInputEvent.m_event.m_analogAxisEvent.m_player = playerNum;
evt.m_event.m_gamepadInputEvent.m_event.m_analogAxisEvent.m_state = 0;
m_pendingEvents.push_back(evt);
}
for (int button = 0; button < GpGamepadButtons::kCount; button++)
{
if (m_playerButtonDown[playerNum][button])
{
m_playerButtonDown[playerNum][button] = false;
GpVOSEvent evt;
evt.m_eventType = GpVOSEventTypes::kKeyboardInput;
evt.m_event.m_keyboardInputEvent.m_eventType = GpKeyboardInputEventTypes::kUp;
evt.m_event.m_keyboardInputEvent.m_keyIDSubset = GpKeyIDSubsets::kGamepadButton;
evt.m_event.m_keyboardInputEvent.m_key.m_gamepadKey.m_player = playerNum;
evt.m_event.m_keyboardInputEvent.m_key.m_gamepadKey.m_button = static_cast<GpGamepadButton_t>(button);
evt.m_event.m_keyboardInputEvent.m_repeatCount = 0;
m_pendingEvents.push_back(evt);
}
}
}
void GpInputDriverSDLGamepad::ProcessSDLEvent(const SDL_Event &msg)
{
IGpVOSEventQueue *evtQueue = m_properties.m_eventQueue;
switch (msg.type)
{
case SDL_CONTROLLERAXISMOTION:
{
const SDL_ControllerAxisEvent &axisMsg = msg.caxis;
GpGamepadAxis_t axis = GpGamepadAxes::kCount;
uint8_t playerNumber = 0;
if (!FindJoystickPlayer(playerNumber, axisMsg.which))
break;
if (axisMsg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT)
axis = GpGamepadAxes::kLeftTrigger;
else if (axisMsg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
axis = GpGamepadAxes::kRightTrigger;
else if (axisMsg.axis == SDL_CONTROLLER_AXIS_LEFTX)
axis = GpGamepadAxes::kLeftStickX;
else if (axisMsg.axis == SDL_CONTROLLER_AXIS_LEFTY)
axis = GpGamepadAxes::kLeftStickY;
else if (axisMsg.axis == SDL_CONTROLLER_AXIS_RIGHTX)
axis = GpGamepadAxes::kRightStickX;
else if (axisMsg.axis == SDL_CONTROLLER_AXIS_RIGHTY)
axis = GpGamepadAxes::kRightStickY;
else
break;
GpVOSEvent evt;
evt.m_eventType = GpVOSEventTypes::kGamepadInput;
evt.m_event.m_gamepadInputEvent.m_eventType = GpGamepadInputEventTypes::kAnalogAxisChanged;
evt.m_event.m_gamepadInputEvent.m_event.m_analogAxisEvent.m_axis = axis;
evt.m_event.m_gamepadInputEvent.m_event.m_analogAxisEvent.m_player = playerNumber;
evt.m_event.m_gamepadInputEvent.m_event.m_analogAxisEvent.m_state = std::max<int16_t>(-32767, axisMsg.value);
m_pendingEvents.push_back(evt);
}
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
{
const bool isDown = (msg.type == SDL_CONTROLLERBUTTONDOWN);
const SDL_ControllerButtonEvent &buttonMsg = msg.cbutton;
GpGamepadButton_t gpButton = GpGamepadButtons::kCount;
uint8_t playerNumber = 0;
if (!FindJoystickPlayer(playerNumber, buttonMsg.which))
break;
if (buttonMsg.button == SDL_CONTROLLER_BUTTON_A)
gpButton = GpGamepadButtons::kFaceRight;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_B)
gpButton = GpGamepadButtons::kFaceDown;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_X)
gpButton = GpGamepadButtons::kFaceUp;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_Y)
gpButton = GpGamepadButtons::kFaceLeft;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_START)
gpButton = GpGamepadButtons::kMisc1;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_BACK)
gpButton = GpGamepadButtons::kMisc2;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_LEFTSTICK)
gpButton = GpGamepadButtons::kLeftStick;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK)
gpButton = GpGamepadButtons::kRightStick;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
gpButton = GpGamepadButtons::kLeftBumper;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
gpButton = GpGamepadButtons::kRightBumper;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
gpButton = GpGamepadButtons::kDPadUp;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
gpButton = GpGamepadButtons::kDPadDown;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
gpButton = GpGamepadButtons::kDPadLeft;
else if (buttonMsg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
gpButton = GpGamepadButtons::kDPadRight;
else
break;
m_playerButtonDown[playerNumber][gpButton] = isDown;
GpVOSEvent evt;
evt.m_eventType = GpVOSEventTypes::kKeyboardInput;
evt.m_event.m_keyboardInputEvent.m_eventType = (isDown ? GpKeyboardInputEventTypes::kDown : GpKeyboardInputEventTypes::kUp);
evt.m_event.m_keyboardInputEvent.m_keyIDSubset = GpKeyIDSubsets::kGamepadButton;
evt.m_event.m_keyboardInputEvent.m_key.m_gamepadKey.m_player = playerNumber;
evt.m_event.m_keyboardInputEvent.m_key.m_gamepadKey.m_button = gpButton;
evt.m_event.m_keyboardInputEvent.m_repeatCount = 0;
m_pendingEvents.push_back(evt);
}
break;
case SDL_CONTROLLERDEVICEADDED:
HandleDeviceAdded(msg.cdevice.which);
break;
case SDL_CONTROLLERDEVICEREMOVED:
HandleDeviceRemoved(msg.cdevice.which);
break;
case SDL_CONTROLLERDEVICEREMAPPED:
// Not really sure what to do here, just re-add it
HandleDeviceRemoved(msg.cdevice.which);
HandleDeviceAdded(msg.cdevice.which);
break;
}
}
IGpInputDriver *GpDriver_CreateInputDriver_SDL2_Gamepad(const GpInputDriverProperties &properties)
{
return GpInputDriverSDLGamepad::Create(properties);
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "IGpInputDriver.h"
#include "GpInputDriverProperties.h"
#include "GpVOSEvent.h"
union SDL_Event;
struct IGpInputDriverSDLGamepad : public IGpInputDriver
{
virtual void ProcessSDLEvent(const SDL_Event &evt) = 0;
static IGpInputDriverSDLGamepad *GetInstance();
};

View File

@@ -29,16 +29,16 @@
GpWindowsGlobals g_gpWindowsGlobals;
extern "C" __declspec(dllimport) IGpInputDriver *GpDriver_CreateInputDriver_XInput(const GpInputDriverProperties &properties);
extern "C" __declspec(dllimport) IGpFontHandler *GpDriver_CreateFontHandler_FreeType2(const GpFontHandlerProperties &properties);
IGpDisplayDriver *GpDriver_CreateDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties);
IGpAudioDriver *GpDriver_CreateAudioDriver_SDL(const GpAudioDriverProperties &properties);
IGpInputDriver *GpDriver_CreateInputDriver_SDL2_Gamepad(const GpInputDriverProperties &properties);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0)
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0)
return -1;
LPWSTR cmdLine = GetCommandLineW();
@@ -79,7 +79,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
EGpInputDriverType inputDrivers[] =
{
EGpInputDriverType_XInput
EGpInputDriverType_SDL2_Gamepad
};
g_gpGlobalConfig.m_inputDriverTypes = inputDrivers;
@@ -91,7 +91,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_SDL_GL2, GpDriver_CreateDisplayDriver_SDL_GL2);
GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_SDL2, GpDriver_CreateAudioDriver_SDL);
GpInputDriverFactory::RegisterInputDriverFactory(EGpInputDriverType_XInput, GpDriver_CreateInputDriver_XInput);
GpInputDriverFactory::RegisterInputDriverFactory(EGpInputDriverType_SDL2_Gamepad, GpDriver_CreateInputDriver_SDL2_Gamepad);
GpFontHandlerFactory::RegisterFontHandlerFactory(EGpFontHandlerType_FreeType2, GpDriver_CreateFontHandler_FreeType2);
if (logger)

View File

@@ -0,0 +1,35 @@
#include "Functions.h"
#include "DrawQuadPixelConstants.h"
#define GP_GL_SHADER_CODE_DRAWQUAD32P_GLSL \
"\n"\
"varying vec4 texCoord;\n"\
"uniform sampler2D surfaceTexture;\n"\
"uniform sampler2D paletteTexture;\n"\
"\n"\
"vec3 SamplePixel(vec2 tc)\n"\
"{\n"\
" return texture2D(surfaceTexture, tc).rgb;\n"\
"}\n"\
"\n"\
"void main()\n"\
"{\n"\
" vec4 resultColor = vec4(SamplePixel(texCoord.xy), 1.0);\n"\
" resultColor *= constants_Modulation;\n"\
"#ifdef ENABLE_FLICKER\n"\
" resultColor = ApplyFlicker(constants_FlickerAxis, texCoord.zw, constants_FlickerStartThreshold, constants_FlickerEndThreshold, resultColor * constants_Modulation);\n"\
" if (resultColor.a < 1.0)\n"\
" discard;\n"\
"#endif\n"\
" resultColor = ApplyDesaturation(constants_Desaturation, resultColor);\n"\
"\n"\
" gl_FragColor = vec4(ApplyColorSpaceTransform(resultColor.rgb), resultColor.a);\n"\
"}\n"
namespace GpBinarizedShaders
{
const char *g_drawQuad32PF_GL2 = "#define ENABLE_FLICKER\n" GP_GL_SHADER_CODE_MEDIUM_PRECISION_PREFIX GP_GL_SHADER_CODE_DRAWQUADPIXELCONSTANTS_H GP_GL_SHADER_CODE_FUNCTIONS_H GP_GL_SHADER_CODE_DRAWQUAD32P_GLSL;
const char *g_drawQuad32PNF_GL2 = GP_GL_SHADER_CODE_MEDIUM_PRECISION_PREFIX GP_GL_SHADER_CODE_DRAWQUADPIXELCONSTANTS_H GP_GL_SHADER_CODE_FUNCTIONS_H GP_GL_SHADER_CODE_DRAWQUAD32P_GLSL;
const char *g_drawQuad32ICCPF_GL2 = "#define USE_ICC_PROFILE\n" "#define ENABLE_FLICKER\n" GP_GL_SHADER_CODE_MEDIUM_PRECISION_PREFIX GP_GL_SHADER_CODE_DRAWQUADPIXELCONSTANTS_H GP_GL_SHADER_CODE_FUNCTIONS_H GP_GL_SHADER_CODE_DRAWQUAD32P_GLSL;
const char *g_drawQuad32ICCPNF_GL2 = "#define USE_ICC_PROFILE\n" GP_GL_SHADER_CODE_MEDIUM_PRECISION_PREFIX GP_GL_SHADER_CODE_DRAWQUADPIXELCONSTANTS_H GP_GL_SHADER_CODE_FUNCTIONS_H GP_GL_SHADER_CODE_DRAWQUAD32P_GLSL;
}

View File

@@ -18,7 +18,7 @@
"enabled" : false
},
{
"name" : "Aerofoil ©2019-2020 Eric Lasota",
"name" : "Aerofoil ©2019-2021 Eric Lasota",
"itemType" : "Label",
"pos" : [ 16, 21 ],
"size" : [ 406, 20 ],

View File

@@ -2,14 +2,17 @@ rmdir /S /Q Packaged
mkdir Packaged
mkdir Packaged\Houses
mkdir Packaged\WinCursors
x64\Release\MiniRez.exe "GliderProData\Glider PRO.r" Packaged\ApplicationResources.gpr
x64\Release\gpr2gpa.exe "Packaged\ApplicationResources.gpr" "DefaultTimestamp.timestamp" "Packaged\ApplicationResources.gpa" "ApplicationResourcePatches\manifest.json"
x64\Release\FTagData.exe "DefaultTimestamp.timestamp" "Packaged\ApplicationResources.gpf" data ozm5 0 0 locked
x64\Release\MergeGPF.exe "Packaged\ApplicationResources.gpf"
x64\Release\ConvertColorCursors.exe
attrib -R Packaged\ApplicationResources.gpf
attrib -R Packaged\Houses\*
x64\Release\hqx2gp.exe "GliderProData\Houses\Art Museum.binhex" "DefaultTimestamp.timestamp" "Packaged\Houses\Art Museum"
@@ -70,11 +73,52 @@ x64\Release\FTagData.exe "DefaultTimestamp.timestamp" "Packaged\Houses\SpacePods
x64\Release\FTagData.exe "DefaultTimestamp.timestamp" "Packaged\Houses\Teddy World.mov.gpf" MooV ozm5 0 0 locked
x64\Release\FTagData.exe "DefaultTimestamp.timestamp" "Packaged\Houses\Titanic.mov.gpf" MooV ozm5 0 0 locked
del /Q Packaged\Houses\*.gpr
del /Q Packaged\ApplicationResources.gpr
copy /Y GliderProData\ConvertedMovies\*.mov.gpa Packaged\Houses\
x64\Release\MergeGPF.exe "Packaged\Houses\Art Museum.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\California or Bust!.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Castle o' the Air.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\CD Demo House.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Davis Station.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Demo House.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Fun House.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Grand Prix.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\ImagineHouse PRO II.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\In The Mirror.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Land of Illusion.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Leviathan.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Metropolis.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Nemo's Market.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Rainbow's End.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Slumberland.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\SpacePods.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Teddy World.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\The Asylum Pro.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Titanic.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Art Museum.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Castle o' the Air.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\CD Demo House.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Davis Station.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Demo House.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Grand Prix.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\ImagineHouse PRO II.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Land of Illusion.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Leviathan.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Nemo's Market.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Rainbow's End.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Slumberland.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\SpacePods.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Teddy World.mov.gpf"
x64\Release\MergeGPF.exe "Packaged\Houses\Titanic.mov.gpf"
del /Q Packaged\Houses\*.gpr
del /Q Packaged\Houses\*.gpa
del /Q Packaged\Houses\*.gpd
del /Q Packaged\ApplicationResources.gpr
del /Q Packaged\ApplicationResources.gpa
attrib +R Packaged\Houses\*
attrib +R Packaged\ApplicationResources.gpf
pause

View File

@@ -2,8 +2,12 @@
#include <string>
#include <Windows.h>
#include "MacFileInfo.h"
#include "CFileStream.h"
#include "CombinedTimestamp.h"
int main(int argc, const char **argv)
#include "WindowsUnicodeToolShim.h"
int toolMain(int argc, const char **argv)
{
if (argc < 7)
{
@@ -26,22 +30,18 @@ int main(int argc, const char **argv)
return -3;
}
FILE *tsF = nullptr;
errno_t ferr = fopen_s(&tsF, timestampPath.c_str(), "rb");
FILE *tsF = fopen_utf8(timestampPath.c_str(), "rb");
int64_t timestamp = 0;
PortabilityLayer::CombinedTimestamp ts;
if (!ferr)
if (tsF)
{
uint8_t encodedTimestamp[8];
if (fread(encodedTimestamp, 1, 8, tsF) != 8)
if (fread(&ts, 1, sizeof(ts), tsF) != sizeof(ts))
{
fprintf(stderr, "Error reading timestamp file");
return -1;
}
for (int i = 0; i < 8; i++)
timestamp |= static_cast<int64_t>(encodedTimestamp[i]) << (i * 8);
fclose(tsF);
}
@@ -52,7 +52,7 @@ int main(int argc, const char **argv)
mfp.m_yPos = atoi(argv[6]);
mfp.m_finderFlags = 0;
mfp.m_protected = 0;
mfp.m_modifiedDate = mfp.m_creationDate = timestamp;
mfp.m_modifiedTimeMacEpoch = mfp.m_createdTimeMacEpoch = timestamp;
for (int i = 7; i < argc; i++)
{
@@ -78,12 +78,14 @@ int main(int argc, const char **argv)
PortabilityLayer::MacFilePropertiesSerialized mps;
mps.Serialize(mfp);
FILE *file = nullptr;
errno_t err = fopen_s(&file, outPath.c_str(), "wb");
if (!err)
FILE *file = fopen_utf8(outPath.c_str(), "wb");
if (file)
{
fwrite(mps.m_data, PortabilityLayer::MacFilePropertiesSerialized::kSize, 1, file);
fclose(file);
PortabilityLayer::CFileStream stream(file);
mps.WriteAsPackage(stream, ts);
stream.Close();
}
return 0;

View File

@@ -41,6 +41,7 @@
<Import Project="..\GpCommon.props" />
<Import Project="..\Common.props" />
<Import Project="..\Debug.props" />
<Import Project="..\WindowsUnicodeToolShim.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
@@ -48,6 +49,7 @@
<Import Project="..\GpCommon.props" />
<Import Project="..\Common.props" />
<Import Project="..\Release.props" />
<Import Project="..\WindowsUnicodeToolShim.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
@@ -77,6 +79,9 @@
<ProjectReference Include="..\PortabilityLayer\PortabilityLayer.vcxproj">
<Project>{6ec62b0f-9353-40a4-a510-3788f1368b33}</Project>
</ProjectReference>
<ProjectReference Include="..\WindowsUnicodeToolShim\WindowsUnicodeToolShim.vcxproj">
<Project>{15009625-1120-405e-8bba-69a16cd6713d}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="FTagData.cpp" />

View File

@@ -48,6 +48,9 @@ Boolean CheckFileError (short resultCode, const PLPasStr &fileName)
case PLErrors::kOutOfMemory:
stringIndex = 7;
break;
case PLErrors::kFileIsBusy:
stringIndex = 12;
break;
default:
stringIndex = 1;
break;

View File

@@ -121,10 +121,10 @@ void ShiftWholeHouse (SInt16);
void DoHouseInfo (void); // --- HouseInfo.c
Boolean OpenHouse (void); // --- HouseIO.c
Boolean OpenHouse (Boolean load); // --- HouseIO.c
Boolean OpenSpecificHouse (const VFileSpec &);
Boolean SaveHouseAs (void);
Boolean ReadHouse (void);
Boolean ReadHouse (GpIOStream *houseStream);
Boolean WriteHouse (Boolean);
Boolean CloseHouse (void);
void OpenHouseResFork (void);

View File

@@ -50,7 +50,7 @@ void GetHighScoreName (short);
void UpdateBannerDialog (Dialog *);
int16_t BannerFilter(void *context, Dialog *dialog, const TimeTaggedVOSEvent *evt);
void GetHighScoreBanner (void);
Boolean OpenHighScoresFile (const VFileSpec &spec, GpIOStream *&outStream);
Boolean OpenHighScoresFile (const VFileSpec &spec, GpIOStream *&outStream, Boolean write);
Str31 highBanner;
@@ -686,23 +686,18 @@ void GetHighScoreBanner (void)
//-------------------------------------------------------------- OpenHighScoresFile
Boolean OpenHighScoresFile (const VFileSpec &scoreSpec, GpIOStream *&scoresStream)
Boolean OpenHighScoresFile (const VFileSpec &spec, GpIOStream *&outStream, Boolean write)
{
PLError_t theErr;
PortabilityLayer::FileManager *fm = PortabilityLayer::FileManager::GetInstance();
theErr = fm->OpenFileData(scoreSpec.m_dir, scoreSpec.m_name, PortabilityLayer::EFilePermission_Any, scoresStream);
PLError_t theErr = fm->OpenNonCompositeFile(spec.m_dir, spec.m_name, ".dat", write ? PortabilityLayer::EFilePermission_Write : PortabilityLayer::EFilePermission_Read, write ? GpFileCreationDispositions::kCreateOrOverwrite : GpFileCreationDispositions::kOpenExisting, outStream);
if (theErr == PLErrors::kFileNotFound)
{
theErr = fm->CreateFileAtCurrentTime(scoreSpec.m_dir, scoreSpec.m_name, 'ozm5', 'gliS');
if (!CheckFileError(theErr, PSTR("New High Scores File")))
return (false);
theErr = fm->OpenFileData(scoreSpec.m_dir, scoreSpec.m_name, PortabilityLayer::EFilePermission_Any, scoresStream);
if (!CheckFileError(theErr, PSTR("High Score")))
return (false);
outStream = nil;
return (true);
}
else if (!CheckFileError(theErr, PSTR("High Score")))
if (!CheckFileError(theErr, PSTR("High Score")))
return (false);
return (true);
@@ -721,18 +716,11 @@ Boolean WriteScoresToDisk (void)
GpIOStream *scoresStream = nil;
scoreSpec = MakeVFileSpec(PortabilityLayer::VirtualDirectories::kHighScores, thisHouseName);
if (!OpenHighScoresFile(scoreSpec, scoresStream))
if (!OpenHighScoresFile(scoreSpec, scoresStream, true))
{
SysBeep(1);
return (false);
}
if (!scoresStream->SeekStart(0))
{
CheckFileError(PLErrors::kIOError, PSTR("High Scores File"));
scoresStream->Close();
return(false);
}
byteCount = sizeof(scoresType);
theScores = &((*thisHouse)->highScores);
@@ -744,13 +732,6 @@ Boolean WriteScoresToDisk (void)
return(false);
}
if (!scoresStream->Truncate(byteCount))
{
CheckFileError(PLErrors::kIOError, PSTR("High Scores File"));
scoresStream->Close();
return(false);
}
scoresStream->Close();
return (true);
@@ -777,15 +758,22 @@ Boolean ReadScoresFromDisk (void)
short volRefNum;
char wasState;
GpIOStream *scoresStream = nil;
VFileSpec scoreSpec = MakeVFileSpec(PortabilityLayer::VirtualDirectories::kHighScores, thisHouseName);
if (!OpenHighScoresFile(scoreSpec, scoresStream))
if (!OpenHighScoresFile(scoreSpec, scoresStream, false))
{
SysBeep(1);
return (false);
}
if (!scoresStream)
return (false);
byteCount = scoresStream->Size();
if (byteCount != sizeof(scoresType))
{
scoresStream->Close();
return (false);
}
theScores = &((*thisHouse)->highScores);

View File

@@ -16,7 +16,9 @@
#include "FileManager.h"
#include "FontFamily.h"
#include "House.h"
#include "MacFileInfo.h"
#include "PLStandardColors.h"
#include "PLStringCompare.h"
#include "PLTimeTaggedVOSEvent.h"
#include "RectUtils.h"
#include "ResourceManager.h"
@@ -31,6 +33,7 @@ void UpdateGoToDialog (Dialog *);
int16_t GoToFilter (void *context, Dialog *dial, const TimeTaggedVOSEvent *evt);
extern PortabilityLayer::IResourceArchive *houseResFork;
extern PortabilityLayer::CompositeFile *houseCFile;
houseHand thisHouse;
@@ -66,6 +69,13 @@ static void FBUI_FreeFileDetails(void *fileDetails)
{
}
static bool FBUI_FilterFile(PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &filename)
{
PortabilityLayer::CompositeFile *cfile = PortabilityLayer::FileManager::GetInstance()->OpenCompositeFile(dirID, filename);
return PortabilityLayer::ResTypeIDCodec::Decode(cfile->GetProperties().m_fileType) == 'gliH';
}
static PortabilityLayer::FileBrowserUI_DetailsCallbackAPI GetHouseDetailsAPI()
{
PortabilityLayer::FileBrowserUI_DetailsCallbackAPI api;
@@ -74,6 +84,7 @@ static PortabilityLayer::FileBrowserUI_DetailsCallbackAPI GetHouseDetailsAPI()
api.m_drawFileDetailsCallback = FBUI_DrawFileDetails;
api.m_loadFileDetailsCallback = FBUI_LoadFileDetails;
api.m_freeFileDetailsCallback = FBUI_FreeFileDetails;
api.m_filterFileCallback = FBUI_FilterFile;
return api;
}
@@ -98,7 +109,7 @@ Boolean CreateNewHouse (void)
char savePath[sizeof(theSpec.m_name) + 1];
size_t savePathLength = 0;
if (!fm->PromptSaveFile(theSpec.m_dir, 'gliH', savePath, savePathLength, sizeof(theSpec.m_name), PSTR("My House"), PSTR("Create House"), GetHouseDetailsAPI()))
if (!fm->PromptSaveFile(theSpec.m_dir, ".gpf", savePath, savePathLength, sizeof(theSpec.m_name), PSTR("My House"), PSTR("Create House"), true, GetHouseDetailsAPI()))
return false;
assert(savePathLength < sizeof(theSpec.m_name) - 1);
@@ -106,9 +117,16 @@ Boolean CreateNewHouse (void)
theSpec.m_name[0] = static_cast<uint8_t>(savePathLength);
memcpy(theSpec.m_name + 1, savePath, savePathLength);
if (fm->FileExists(theSpec.m_dir, theSpec.m_name))
// Don't try to overwrite the current house
if (houseCFile && theSpec.m_dir == houseCFile->GetDirectory() && !StrCmp::CompareCaseInsensitive(theSpec.m_name, houseCFile->GetFileName()))
{
if (!fm->DeleteFile(theSpec.m_dir, theSpec.m_name))
CheckFileError(PLErrors::kFileIsBusy, theSpec.m_name);
return (false);
}
if (fm->CompositeFileExists(theSpec.m_dir, theSpec.m_name))
{
if (!fm->DeleteCompositeFile(theSpec.m_dir, theSpec.m_name))
{
CheckFileError(PLErrors::kAccessDenied, theSpec.m_name);
return (false);
@@ -133,7 +151,7 @@ Boolean CreateNewHouse (void)
AddExtraHouse(theSpec);
BuildHouseList();
InitCursor();
if (!OpenHouse())
if (!OpenHouse(false))
return (false);
WriteOutPrefs();
@@ -155,7 +173,7 @@ Boolean InitializeEmptyHouse (void)
if (thisHouse != nil)
thisHouse.Dispose();
thisHouse = NewHandle(sizeof(houseType)).StaticCast<houseType>();
thisHouse = NewHandle(sizeof(houseType) - sizeof(roomType)).StaticCast<houseType>();
if (thisHouse == nil)
{

View File

@@ -28,20 +28,18 @@
#define kDiscardChanges 2
void LoopMovie (void);
void OpenHouseMovie (void);
void CloseHouseMovie (void);
Boolean IsFileReadOnly (const VFileSpec &);
AnimationPlayer theMovie;
Rect movieRect;
PortabilityLayer::IResourceArchive *houseResFork;
short wasHouseVersion;
GpIOStream *houseStream;
Boolean houseOpen, fileDirty;
Boolean changeLockStateOfHouse, saveHouseLocked, houseIsReadOnly;
Boolean hasMovie, tvInRoom;
PortabilityLayer::CompositeFile *houseCFile;
extern VFileSpec *theHousesSpecs;
@@ -59,9 +57,7 @@ void OpenHouseMovie (void)
{
#ifdef COMPILEQT
VFileSpec theSpec;
VFileInfo finderInfo;
Handle spaceSaver;
PLError_t theErr;
short movieRefNum;
Boolean dataRefWasChanged;
@@ -69,19 +65,20 @@ void OpenHouseMovie (void)
{
theSpec = theHousesSpecs[thisHouseIndex];
PasStringConcat(theSpec.m_name, PSTR(".mov"));
theErr = FSpGetFInfo(theSpec, finderInfo);
if (theErr != PLErrors::kNone)
return;
AnimationPackage *anim = AnimationPackage::Create();
if (!anim)
return;
if (!anim->Load(theSpec.m_dir, theSpec.m_name))
PLError_t theErr = anim->Load(theSpec.m_dir, theSpec.m_name);
if (theErr != PLErrors::kNone)
{
anim->Destroy();
YellowAlert(kYellowQTMovieNotLoaded, theErr);
if (theErr != PLErrors::kFileNotFound)
YellowAlert(kYellowQTMovieNotLoaded, theErr);
return;
}
@@ -116,7 +113,7 @@ void CloseHouseMovie (void)
//-------------------------------------------------------------- OpenHouse
// Opens a house (whatever current selection is). Returns true if all went well.
Boolean OpenHouse (void)
Boolean OpenHouse (Boolean read)
{
PLError_t theErr;
@@ -132,12 +129,21 @@ Boolean OpenHouse (void)
if (!StrCmp::EqualCaseInsensitive(theHousesSpecs[thisHouseIndex].name, "\pDemo House"))
return (false);
#endif
houseIsReadOnly = IsFileReadOnly(theHousesSpecs[thisHouseIndex]);
theErr = PortabilityLayer::FileManager::GetInstance()->OpenFileData(theHousesSpecs[thisHouseIndex].m_dir, theHousesSpecs[thisHouseIndex].m_name, PortabilityLayer::EFilePermission_Any, houseStream);
if (!CheckFileError(theErr, thisHouseName))
houseCFile = PortabilityLayer::FileManager::GetInstance()->OpenCompositeFile(theHousesSpecs[thisHouseIndex].m_dir, theHousesSpecs[thisHouseIndex].m_name);
if (!houseCFile)
return (false);
houseIsReadOnly = houseCFile->IsDataReadOnly();
GpIOStream *houseStream = nil;
theErr = houseCFile->OpenData(PortabilityLayer::EFilePermission_Any, GpFileCreationDispositions::kCreateOrOpen, houseStream);
if (!CheckFileError(theErr, thisHouseName))
{
houseCFile->Close();
houseCFile = nil;
return (false);
}
houseOpen = true;
OpenHouseResFork();
@@ -148,6 +154,16 @@ Boolean OpenHouse (void)
tvInRoom = false;
tvWithMovieNumber = -1;
OpenHouseMovie();
if (read)
{
Boolean readOK = ReadHouse(houseStream);
houseStream->Close();
return readOK;
}
houseStream->Close();
return (true);
}
@@ -173,9 +189,7 @@ Boolean OpenSpecificHouse (const VFileSpec &specs)
{
thisHouseIndex = i;
PasStringCopy(theHousesSpecs[thisHouseIndex].m_name, thisHouseName);
if (OpenHouse())
itOpened = ReadHouse();
else
if (!OpenHouse(true))
itOpened = false;
break;
}
@@ -527,7 +541,7 @@ bool ByteSwapHouse(housePtr house, size_t sizeInBytes, bool isSwappedAfter)
return true;
}
Boolean ReadHouse (void)
Boolean ReadHouse (GpIOStream *houseStream)
{
long byteCount;
PLError_t theErr;
@@ -653,19 +667,27 @@ Boolean WriteHouse (Boolean checkIt)
UInt32 timeStamp;
long byteCount;
PLError_t theErr;
if ((housesFound < 1) || (thisHouseIndex == -1))
return(false);
if (!houseOpen)
{
YellowAlert(kYellowUnaccounted, 4);
return (false);
}
if (!houseStream->SeekStart(0))
if (!houseCFile)
{
CheckFileError(PLErrors::kIOError, thisHouseName);
return(false);
YellowAlert(kYellowUnaccounted, 4);
return (false);
}
GpIOStream *houseStream = nil;
theErr = houseCFile->OpenData(PortabilityLayer::EFilePermission_Write, GpFileCreationDispositions::kCreateOrOpen, houseStream);
if (theErr != PLErrors::kNone)
return (false);
CopyThisRoomToRoom();
if (checkIt)
@@ -699,6 +721,7 @@ Boolean WriteHouse (Boolean checkIt)
{
CheckFileError(PLErrors::kIOError, thisHouseName);
ByteSwapHouse(*thisHouse, static_cast<size_t>(byteCount), false);
houseStream->Close();
return(false);
}
@@ -706,16 +729,13 @@ Boolean WriteHouse (Boolean checkIt)
{
CheckFileError(PLErrors::kIOError, thisHouseName);
ByteSwapHouse(*thisHouse, static_cast<size_t>(byteCount), false);
houseStream->Close();
return(false);
}
ByteSwapHouse(*thisHouse, static_cast<size_t>(byteCount), false);
if (!houseStream->Truncate(byteCount))
{
CheckFileError(PLErrors::kIOError, thisHouseName);
return(false);
}
houseStream->Close();
if (changeLockStateOfHouse)
{
@@ -749,7 +769,11 @@ Boolean CloseHouse (void)
CloseHouseResFork();
CloseHouseMovie();
houseStream->Close();
if (houseCFile)
{
houseCFile->Close();
houseCFile = nil;
}
houseOpen = false;
@@ -764,7 +788,7 @@ void OpenHouseResFork (void)
PortabilityLayer::ResourceManager *rm = PortabilityLayer::ResourceManager::GetInstance();
if (houseResFork == nullptr)
{
houseResFork = rm->LoadResFile(theHousesSpecs[thisHouseIndex].m_dir, theHousesSpecs[thisHouseIndex].m_name);
houseResFork = rm->LoadResFile(houseCFile);
if (!houseResFork)
YellowAlert(kYellowFailedResOpen, PLErrors::kResourceError);
}
@@ -817,8 +841,7 @@ Boolean QuerySaveChanges (void)
if (!quitting)
{
whoCares = CloseHouse();
if (OpenHouse())
whoCares = ReadHouse();
OpenHouse(true);
}
UpdateMenus(false);
return (true);
@@ -850,17 +873,6 @@ void YellowAlert (short whichAlert, short identifier)
whoCares = PortabilityLayer::DialogManager::GetInstance()->DisplayAlert(kYellowAlert, &substitutions);
}
//-------------------------------------------------------------- IsFileReadOnly
Boolean IsFileReadOnly (const VFileSpec &spec)
{
// Kind of annoying, but itch.io doesn't preserve read-only flags and there doesn't seem to be any way around that.
if (spec.m_dir == PortabilityLayer::VirtualDirectories::kApplicationData || spec.m_dir == PortabilityLayer::VirtualDirectories::kGameData)
return true;
return PortabilityLayer::FileManager::GetInstance()->FileLocked(spec.m_dir, spec.m_name);
}
//-------------------------------------------------------------- LoadHousePicture
THandle<void> LoadHouseResource(const PortabilityLayer::ResTypeID &resTypeID, int16_t resID)

View File

@@ -616,7 +616,7 @@ void ValidateNumberOfRooms (void)
reportsRooms = (long)(*thisHouse)->nRooms;
countedRooms = (GetHandleSize(thisHouse.StaticCast<void>()) -
sizeof(houseType)) / sizeof(roomType);
sizeof(houseType)) / sizeof(roomType) + 1;
if (reportsRooms != countedRooms)
{
(*thisHouse)->nRooms = (short)countedRooms;

View File

@@ -8,6 +8,7 @@
#include "WindowDef.h"
#include "BitmapImage.h"
#include "FileManager.h"
#include "Externs.h"
#include "Environ.h"
#include "FontFamily.h"
@@ -55,6 +56,7 @@ DrawSurface *loadScreenRingSurface;
void ReadInPrefs (void);
void WriteOutPrefs (void);
void HandleSplashResolutionChange (void);
int main(int argc, const char **argv);
@@ -69,7 +71,7 @@ extern Str15 leftName, rightName, batteryName, bandName;
extern Str15 highName;
//extern long encryptedNumber;
extern short maxFiles, numNeighbors, willMaxFiles;
extern GpIOStream *houseStream;
extern PortabilityLayer::CompositeFile *houseCFile;
extern short isEditH, isEditV, isMapH, isMapV;
extern short isToolsH, isToolsV, isCoordH, isCoordV;
extern short isLinkH, isLinkV, toolMode, mapLeftRoom, mapTopRoom;
@@ -224,12 +226,12 @@ void ReadInPrefs (void)
isEditV = 41;
isMapH = 3;
// isMapV = qd.screenBits.bounds.bottom - 100;
isMapV = 100;
isMapV = 385;
mapRoomsWide = 15;
mapRoomsHigh = 4;
// isToolsH = qd.screenBits.bounds.right - 120;
isToolsH = 100;
isToolsV = 35;
isToolsH = 525;
isToolsV = 41;
isLinkH = 50;
isLinkV = 80;
// isCoordH = qd.screenBits.bounds.right - 55;
@@ -360,8 +362,6 @@ void WriteOutPrefs (void)
SysBeep(1);
modulePrefs.Dispose();
UnivSetSoundVolume(wasVolume, thisMac.hasSM3);
}
void StepLoadScreenRing()
@@ -567,8 +567,7 @@ void StepLoadScreen(int steps, bool insertDelay)
void InitLoadingWindow()
{
// Only phones are slow enough for this to matter
if (!thisMac.isTouchscreen)
if (!thisMac.isTouchscreen && isPrefsLoaded)
return;
if (isPrefsLoaded)
@@ -781,6 +780,10 @@ void PreloadAATables()
PortabilityLayer::RGBAColor::Create(0, 255, 255, 255),
PortabilityLayer::RGBAColor::Create(0, 0, 255, 255),
PortabilityLayer::RGBAColor::Create(204, 102, 51, 255),
PortabilityLayer::RGBAColor::Create(102, 102, 102, 255),
PortabilityLayer::RGBAColor::Create(51, 51, 102, 255),
PortabilityLayer::RGBAColor::Create(255, 255, 51, 255),
PortabilityLayer::RGBAColor::Create(0, 0, 0, 255),
};
const size_t numPalettePreloads = sizeof(preloadColors) / sizeof(preloadColors[0]);
@@ -814,7 +817,7 @@ void PreloadAATables()
if (!toneAlreadyQueued)
{
PreloadAATableSpec &spec = specs[i + numTonePreloads];
PreloadAATableSpec &spec = specs[numPalettePreloads + numTonePreloads];
numTonePreloads++;
spec.m_color = PortabilityLayer::RGBAColor::Create(tone, tone, tone, 255);
@@ -911,7 +914,7 @@ void ShowInitialLaunchDisclaimer()
"on the 2016 release of the game\xd5s source code and assets.",
"",
"Glider PRO, a sequel to the original Glider, was released in 1994",
"for the Apple Macintosh, and is widely recognized as one of",
"for Apple Macintosh computers, and is widely recognized as one of",
"of the most iconic Macintosh-exclusive games of the 1990\xd5s.",
"",
"I hope that by adapting it to be playable on modern systems, more",
@@ -1137,17 +1140,28 @@ int gpAppMain()
loadScreenRingSurface = nullptr;
}
bool resolutionChanged = false;
if (!isPrefsLoaded)
{
WriteOutPrefs();
FlushResolutionChange();
if (thisMac.isResolutionDirty)
{
resolutionChanged = true;
FlushResolutionChange();
}
ShowInitialLaunchDisclaimer();
}
FlushResolutionChange();
if (thisMac.isResolutionDirty)
resolutionChanged = true;
OpenMainWindow();
if (resolutionChanged)
HandleSplashResolutionChange();
else
OpenMainWindow();
if (isDoColorFade)
PortabilityLayer::WindowManager::GetInstance()->SetWindowDesaturation(mainWindow, 1.0);
@@ -1155,8 +1169,7 @@ int gpAppMain()
InitSound(); SpinCursor(2);
InitMusic(); SpinCursor(2);
BuildHouseList();
if (OpenHouse())
whoCares = ReadHouse();
OpenHouse(true);
PlayPrioritySound(kBirdSound, kBirdPriority);
DelayTicks(6);
@@ -1189,7 +1202,8 @@ int gpAppMain()
if (!CloseHouse())
{
CloseHouseResFork();
houseStream->Close();
if (houseCFile)
houseCFile->Close();
houseOpen = false;
}
}

View File

@@ -43,16 +43,23 @@ void DoMarquee (void)
{
if ((!theMarquee.active) || (theMarquee.paused))
return;
DrawSurface *surface = mainWindow->GetDrawSurface();
const uint8_t *pattern = theMarquee.pats[theMarquee.index];
DrawMarquee(surface, pattern);
theMarquee.index++;
if (theMarquee.index >= kNumMarqueePats)
theMarquee.index = 0;
pattern = theMarquee.pats[theMarquee.index];
DrawMarquee(surface, pattern);
theMarquee.step++;
if (theMarquee.step >= theMarquee.interval)
{
DrawSurface *surface = mainWindow->GetDrawSurface();
const uint8_t *pattern = theMarquee.pats[theMarquee.index];
DrawMarquee(surface, pattern);
theMarquee.index++;
if (theMarquee.index >= kNumMarqueePats)
theMarquee.index = 0;
pattern = theMarquee.pats[theMarquee.index];
DrawMarquee(surface, pattern);
theMarquee.step = 0;
}
}
//-------------------------------------------------------------- StartMarquee
@@ -515,6 +522,8 @@ void InitMarquee (void)
theMarquee.active = false;
theMarquee.paused = false;
theMarquee.handled = false;
theMarquee.step = 0;
theMarquee.interval = 2;
gliderMarqueeUp = false;
}

View File

@@ -15,7 +15,7 @@ typedef struct
{
Pattern pats[kNumMarqueePats];
Rect bounds, handle;
short index, direction, dist;
short index, direction, dist, step, interval;
Boolean active, paused, handled;
} marquee;

View File

@@ -426,6 +426,7 @@ void DoOptionsMenu (short theItem)
}
OpenMainWindow();
RedrawSplashScreen();
incrementModeTime = TickCount() + kIdleSplashTicks;
}
else if (theMode == kSplashMode) // switching to edit mode

View File

@@ -1963,6 +1963,12 @@ void GetThisRoomsObjRects (void)
if ((noRoomAtAll) || (!houseUnlocked))
{
// clear object handles so they're not draggable
QSetRect(&leftStartGliderDest, 0, 0, 0, 0);
QSetRect(&rightStartGliderDest, 0, 0, 0, 0);
for (i = 0; i < kMaxRoomObs; i++)
QSetRect(&roomObjectRects[i], 0, 0, 0, 0);
return;
}
else

View File

@@ -278,12 +278,11 @@ void DoDemoGame (void)
whoCares = CloseHouse();
thisHouseIndex = demoHouseIndex;
PasStringCopy(theHousesSpecs[thisHouseIndex].m_name, thisHouseName);
if (OpenHouse())
if (OpenHouse(true))
{
if (thisMac.isTouchscreen)
DismissMainMenuUI();
whoCares = ReadHouse();
demoGoing = true;
NewGame(kNewGameMode);
@@ -293,8 +292,7 @@ void DoDemoGame (void)
whoCares = CloseHouse();
thisHouseIndex = wasHouseIndex;
PasStringCopy(theHousesSpecs[thisHouseIndex].m_name, thisHouseName);
if (OpenHouse())
whoCares = ReadHouse();
OpenHouse(true);
incrementModeTime = TickCount() + kIdleSplashTicks;
RedrawSplashScreen();
}

View File

@@ -22,7 +22,7 @@
#define kPrefCreatorType 'ozm5'
#define kPrefFileType 'gliP'
#define kPrefFileName PSTR("Glider Prefs v2")
#define kPrefFileName PSTR("Glider Prefs")
#define kDefaultPrefFName PSTR("Preferences")
#define kPrefsStringsID 160
#define kNewPrefsAlertID 160
@@ -67,16 +67,8 @@ Boolean WritePrefs (const prefsInfo *thePrefs, short versionNow, THandle<moduleP
PasStringCopy(kPrefFileName, fileName);
VFileSpec theSpecs = MakeVFileSpec(PortabilityLayer::VirtualDirectories::kPrefs, fileName);
if (!fm->FileExists(PortabilityLayer::VirtualDirectories::kPrefs, fileName))
{
theErr = fm->CreateFileAtCurrentTime(theSpecs.m_dir, theSpecs.m_name, kPrefCreatorType, kPrefFileType);
if (theErr != PLErrors::kNone)
{
CheckFileError(theErr, PSTR("Preferences"));
return(false);
}
}
theErr = fm->OpenFileData(theSpecs.m_dir, theSpecs.m_name, PortabilityLayer::EFilePermission_Write, fileStream);
theErr = fm->OpenNonCompositeFile(theSpecs.m_dir, theSpecs.m_name, ".dat", PortabilityLayer::EFilePermission_Write, GpFileCreationDispositions::kCreateOrOverwrite, fileStream);
if (theErr != PLErrors::kNone)
{
CheckFileError(theErr, PSTR("Preferences"));
@@ -201,10 +193,10 @@ PLError_t ReadPrefs (prefsInfo *thePrefs, short versionNeed, Boolean *isOldVersi
theSpecs = MakeVFileSpec(PortabilityLayer::VirtualDirectory_t::kPrefs, fileName);
if (!PortabilityLayer::FileManager::GetInstance()->FileExists(theSpecs.m_dir, theSpecs.m_name))
return PLErrors::kFileNotFound;
theErr = fm->OpenNonCompositeFile(theSpecs.m_dir, theSpecs.m_name, ".dat", PortabilityLayer::EFilePermission_Read, GpFileCreationDispositions::kOpenExisting, fileStream);
if (theErr == PLErrors::kFileNotFound)
return theErr;
theErr = fm->OpenFileData(theSpecs.m_dir, theSpecs.m_name, PortabilityLayer::EFilePermission_Read, fileStream);
if (theErr != PLErrors::kNone)
{
CheckFileError(theErr, PSTR("Preferences"));
@@ -326,7 +318,7 @@ Boolean DeletePrefs ()
theSpecs = MakeVFileSpec(PortabilityLayer::VirtualDirectories::kPrefs, fileName);
return PortabilityLayer::FileManager::GetInstance()->DeleteFile(theSpecs.m_dir, theSpecs.m_name);
return PortabilityLayer::FileManager::GetInstance()->DeleteNonCompositeFile(theSpecs.m_dir, theSpecs.m_name, ".dat");
}
//-------------------------------------------------------------- RunFunctionOnAllPrefsHandlers

View File

@@ -73,7 +73,7 @@ static void FBUI_DrawFileDetails(DrawSurface *surface, const Point &basePoint, c
static void *FBUI_LoadFileDetails(PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &filename)
{
GpIOStream *stream = nullptr;
if (PortabilityLayer::FileManager::GetInstance()->OpenFileData(dirID, filename, PortabilityLayer::EFilePermission_Read, stream) != PLErrors::kNone)
if (PortabilityLayer::FileManager::GetInstance()->OpenNonCompositeFile(dirID, filename, ".sav", PortabilityLayer::EFilePermission_Read, GpFileCreationDispositions::kOpenExisting, stream) != PLErrors::kNone)
return nullptr;
const size_t kPrefixSize = sizeof(game2Type) - sizeof(savedRoom);
@@ -102,6 +102,11 @@ static void FBUI_FreeFileDetails(void *fileDetails)
PortabilityLayer::MemoryManager::GetInstance()->Release(fileDetails);
}
static bool FBUI_FilterFile(PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &filename)
{
return true;
}
static PortabilityLayer::FileBrowserUI_DetailsCallbackAPI GetSavedGameDetailsAPI()
{
PortabilityLayer::FileBrowserUI_DetailsCallbackAPI api;
@@ -110,6 +115,7 @@ static PortabilityLayer::FileBrowserUI_DetailsCallbackAPI GetSavedGameDetailsAPI
api.m_drawFileDetailsCallback = FBUI_DrawFileDetails;
api.m_loadFileDetailsCallback = FBUI_LoadFileDetails;
api.m_freeFileDetailsCallback = FBUI_FreeFileDetails;
api.m_filterFileCallback = FBUI_FilterFile;
return api;
}
@@ -163,7 +169,7 @@ Boolean SaveGame2 (void)
char savePath[sizeof(spec.m_name) + 1];
size_t savePathLength = 0;
if (!fm->PromptSaveFile(spec.m_dir, 'gliG', savePath, savePathLength, sizeof(spec.m_name), PLPasStr(gameNameStr), PSTR("Save Game"), GetSavedGameDetailsAPI()))
if (!fm->PromptSaveFile(spec.m_dir, ".sav", savePath, savePathLength, sizeof(spec.m_name), PLPasStr(gameNameStr), PSTR("Save Game"), false, GetSavedGameDetailsAPI()))
{
mm->Release(savedGame);
return false;
@@ -173,15 +179,6 @@ Boolean SaveGame2 (void)
spec.m_name[0] = static_cast<uint8_t>(savePathLength);
memcpy(spec.m_name + 1, savePath, savePathLength);
if (fm->FileExists(spec.m_dir, PLPasStr(spec.m_name)))
{
if (!fm->DeleteFile(spec.m_dir, spec.m_name))
{
CheckFileError(PLErrors::kAccessDenied, PSTR("Saved Game"));
return false;
}
}
thisHousePtr = *thisHouse;
@@ -216,20 +213,15 @@ Boolean SaveGame2 (void)
destRoom->objects[i] = srcRoom->objects[i];
}
PLError_t theErr = fm->CreateFileAtCurrentTime(spec.m_dir, spec.m_name, 'ozm5', 'gliG');
PLError_t theErr = fm->OpenNonCompositeFile(spec.m_dir, spec.m_name, ".sav", PortabilityLayer::EFilePermission_Write, GpFileCreationDispositions::kCreateOrOverwrite, gameStream);
if (CheckFileError(theErr, PSTR("Saved Game")))
{
theErr = fm->OpenFileData(spec.m_dir, spec.m_name, PortabilityLayer::EFilePermission_Write, gameStream);
if (CheckFileError(theErr, PSTR("Saved Game")))
if (gameStream->Write(savedGame, byteCount) != byteCount)
{
if (gameStream->Write(savedGame, byteCount) != byteCount)
{
CheckFileError(PLErrors::kIOError, PSTR("Saved Game"));
}
gameStream->Close();
CheckFileError(PLErrors::kIOError, PSTR("Saved Game"));
}
gameStream->Close();
}
mm->Release(savedGame);
@@ -274,7 +266,7 @@ Boolean OpenSavedGame (void)
char savePath[sizeof(spec.m_name) + 1];
size_t savePathLength = 0;
if (!fm->PromptOpenFile(spec.m_dir, 'gliG', savePath, savePathLength, sizeof(spec.m_name), PSTR("Open Saved Game"), GetSavedGameDetailsAPI()))
if (!fm->PromptOpenFile(spec.m_dir, ".sav", savePath, savePathLength, sizeof(spec.m_name), PSTR("Open Saved Game"), false, GetSavedGameDetailsAPI()))
return false;
assert(savePathLength < sizeof(spec.m_name) - 1);
@@ -282,15 +274,8 @@ Boolean OpenSavedGame (void)
spec.m_name[0] = static_cast<uint8_t>(savePathLength);
memcpy(spec.m_name + 1, savePath, savePathLength);
PortabilityLayer::MacFileProperties props;
if (!fm->ReadFileProperties(spec.m_dir, spec.m_name, props))
return false;
if (memcmp(props.m_fileType, "gliG", 4))
return false;
GpIOStream *gameStream = nullptr;
PLError_t theErr = fm->OpenFileData(spec.m_dir, spec.m_name, PortabilityLayer::EFilePermission_Read, gameStream);
PLError_t theErr = fm->OpenNonCompositeFile(spec.m_dir, spec.m_name, ".sav", PortabilityLayer::EFilePermission_Read, GpFileCreationDispositions::kOpenExisting, gameStream);
if (!CheckFileError(theErr, PSTR("Saved Game")))
return(false);

View File

@@ -101,18 +101,26 @@ void UpdateLoadDialog (Dialog *theDialog)
if (SectRect(&dialogRect, &tempRect, &dummyRect))
{
PortabilityLayer::IResourceArchive *resFile = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(theHousesSpecs[i].m_dir, theHousesSpecs[i].m_name);
if (resFile != nullptr)
PortabilityLayer::CompositeFile *cfile = PortabilityLayer::FileManager::GetInstance()->OpenCompositeFile(theHousesSpecs[i].m_dir, theHousesSpecs[i].m_name);
bool haveHouseIcon = false;
GpIOStream *resStream = nil;
if (cfile)
{
if (!LargeIconPlot(surface, resFile, -16455, tempRect))
PortabilityLayer::IResourceArchive *resFile = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(cfile);
if (resFile != nullptr)
{
LoadDialogPICT(theDialog, kLoadIconFirstItem + i - housePage,
kDefaultHousePict8);
if (LargeIconPlot(surface, resFile, -16455, tempRect))
haveHouseIcon = true;
resFile->Destroy();
}
resFile->Destroy();
cfile->Close();
}
else
if (!haveHouseIcon)
LoadDialogPICT(theDialog, kLoadIconFirstItem + i - housePage,
kDefaultHousePict8);
}
@@ -411,11 +419,8 @@ void DoLoadHouse (void)
whoCares = CloseHouse();
PasStringCopy(theHousesSpecs[thisHouseIndex].m_name,
thisHouseName);
if (OpenHouse())
{
whoCares = ReadHouse();
if (OpenHouse(true))
houseNameDirty = true;
}
}
leaving = true;
}
@@ -452,11 +457,8 @@ void DoLoadHouse (void)
whoCares = CloseHouse();
PasStringCopy(theHousesSpecs[thisHouseIndex].m_name,
thisHouseName);
if (OpenHouse())
{
whoCares = ReadHouse();
if (OpenHouse(true))
houseNameDirty = true;
}
}
leaving = true;
}
@@ -489,11 +491,8 @@ void DoLoadHouse (void)
whoCares = CloseHouse();
PasStringCopy(theHousesSpecs[thisHouseIndex].m_name,
thisHouseName);
if (OpenHouse())
{
whoCares = ReadHouse();
if (OpenHouse(true))
houseNameDirty = true;
}
}
leaving = true;
}
@@ -596,7 +595,7 @@ void DoDirSearch (void)
{
theHousesSpecs[housesFound] = MakeVFileSpec(theDirs[currentDir], f->name);
if (fm->FileExists(theDirs[currentDir], f->name))
if (fm->CompositeFileExists(theDirs[currentDir], f->name))
housesFound++;
}
}

View File

@@ -637,7 +637,7 @@ void HandleSoundMusicChange (short newVolume, Boolean sayIt)
isSoundOn = (newVolume != 0);
if (wasIdle)
if (wasIdle && theMode != kEditMode)
{
if (newVolume == 0)
StopTheMusic();
@@ -773,7 +773,7 @@ void DoSoundPrefs (void)
case kCancelButton:
UnivSetSoundVolume(wasLoudness, thisMac.hasSM3);
HandleSoundMusicChange(wasLoudness, false);
if (isPlayMusicIdle != wasIdle)
if (isPlayMusicIdle != wasIdle && theMode != kEditMode)
{
if (isPlayMusicIdle)
{
@@ -831,7 +831,7 @@ void DoSoundPrefs (void)
case kIdleMusicItem:
wasIdle = !wasIdle;
SetDialogItemValue(prefDlg, kIdleMusicItem, (short)wasIdle);
if (wasIdle)
if (wasIdle && theMode != kEditMode)
{
UnivGetSoundVolume(&tempVolume, thisMac.hasSM3);
if (tempVolume != 0)

View File

@@ -96,41 +96,6 @@ SortableEntry SortableEntry::Create(const char *zipLocation, PortabilityLayer::V
return entry;
}
static void ConvertToMSDOSTimestamp(const PortabilityLayer::CombinedTimestamp &ts, uint16_t &msdosDate, uint16_t &msdosTime)
{
int32_t yearsSince1980 = ts.GetLocalYear() - 1980;
uint8_t month = ts.m_localMonth;
uint8_t day = ts.m_localDay;
uint8_t hour = ts.m_localHour;
uint8_t minute = ts.m_localMinute;
uint8_t second = ts.m_localSecond;
if (yearsSince1980 < 0)
{
// Time machine
yearsSince1980 = 0;
second = 0;
minute = 0;
hour = 0;
day = 1;
month = 1;
}
else if (yearsSince1980 > 127)
{
// I was promised flying cars, but it's 2107 and you're still flying paper airplanes...
yearsSince1980 = 127;
second = 59;
minute = 59;
hour = 23;
day = 31;
month = 12;
}
msdosTime = (second / 2) | (minute << 5) | (hour << 11);
msdosDate = day | (month << 5) | (yearsSince1980 << 9);
}
static void InitSourceExportWindow(SourceExportState *state)
{
static const int kLoadScreenHeight = 32;
@@ -403,7 +368,7 @@ static bool RepackDirectory(SourceExportState &state, GpIOStream *outStream, std
uint16_t dosDate = 0;
uint16_t dosTime = 0;
ConvertToMSDOSTimestamp(state.m_ts, dosDate, dosTime);
state.m_ts.GetAsMSDOSTimestamp(dosDate, dosTime);
IGpDirectoryCursor *dirCursor = PLDrivers::GetFileSystem()->ScanDirectory(virtualDir);
if (!dirCursor)
@@ -610,7 +575,7 @@ static bool AddZipDirectory(GpIOStream *stream, std::vector<PortabilityLayer::Zi
uint16_t dosDate = 0;
uint16_t dosTime = 0;
ConvertToMSDOSTimestamp(ts, dosDate, dosTime);
ts.GetAsMSDOSTimestamp(dosDate, dosTime);
GpUFilePos_t localHeaderPos = stream->Tell();

View File

@@ -70,9 +70,9 @@ short WhichStringFirst (StringPtr p1, StringPtr p2)
{
if (!foundIt)
{
if (p1[0] < p2[0]) // shortest string wins
if (p1[0] > p2[0]) // shortest string wins
greater = 1;
else if (p1[0] > p2[0])
else if (p1[0] < p2[0])
greater = 2;
}
foundIt = true;

View File

@@ -3,6 +3,7 @@
enum EGpInputDriverType
{
EGpInputDriverType_XInput,
EGpInputDriverType_SDL2_Gamepad,
EGpInputDriverType_Count,
};

View File

@@ -2,8 +2,8 @@
#define GP_BUILD_VERSION_MAJOR 1
#define GP_BUILD_VERSION_MINOR 0
#define GP_BUILD_VERSION_UPDATE 11
#define GP_BUILD_VERSION_UPDATE 16
#define GP_APPLICATION_VERSION_STRING "1.0.11"
#define GP_APPLICATION_COPYRIGHT_STRING "2019-2020 Eric Lasota"
#define GP_APPLICATION_VERSION_STRING "1.0.16"
#define GP_APPLICATION_COPYRIGHT_STRING "2019-2021 Eric Lasota"
#define GP_APPLICATION_WEBSITE_STRING "https://github.com/elasota/Aerofoil"

View File

@@ -0,0 +1,11 @@
#pragma once
namespace GpClipboardContentsTypes
{
enum GpClipboardContentsType
{
kText,
};
}
typedef GpClipboardContentsTypes::GpClipboardContentsType GpClipboardContentsType_t;

View File

@@ -13,9 +13,23 @@ public:
virtual bool SeekStart(GpUFilePos_t loc) = 0;
virtual bool SeekCurrent(GpFilePos_t loc) = 0;
virtual bool SeekEnd(GpUFilePos_t loc) = 0;
virtual bool Truncate(GpUFilePos_t loc) = 0;
virtual GpUFilePos_t Size() const = 0;
virtual GpUFilePos_t Tell() const = 0;
virtual void Close() = 0;
virtual void Flush() = 0;
bool ReadExact(void *bytesOut, size_t size);
bool WriteExact(const void *bytesOut, size_t size);
};
inline bool GpIOStream::ReadExact(void *bytesOut, size_t size)
{
const size_t nRead = this->Read(bytesOut, size);
return nRead == size;
}
inline bool GpIOStream::WriteExact(const void *bytes, size_t size)
{
const size_t nWritten = this->Write(bytes, size);
return nWritten == size;
}

View File

@@ -0,0 +1,16 @@
#include "GpClipboardContentsType.h"
#include <stdint.h>
struct IGpClipboardContents
{
virtual GpClipboardContentsType_t GetContentsType() const = 0;
virtual void Destroy() = 0;
virtual IGpClipboardContents *Clone() const = 0;
};
struct IGpClipboardContentsText : public IGpClipboardContents
{
virtual const uint8_t *GetBytes() const = 0;
virtual size_t GetSize() const = 0; // In bytes
};

View File

@@ -15,7 +15,7 @@ public:
typedef void(*DelayCallback_t)(uint32_t ticks);
virtual bool FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) = 0;
virtual bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists) = 0;
virtual bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists) = 0;
virtual GpIOStream *OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) = 0;
virtual bool DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) = 0;
virtual IGpDirectoryCursor *ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) = 0;
@@ -23,7 +23,6 @@ public:
virtual bool ValidateFilePath(const char *path, size_t pathLen) const = 0;
virtual bool ValidateFilePathUnicodeChar(uint32_t ch) const = 0;
virtual bool IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const = 0;
virtual void SetMainThreadRelay(IGpThreadRelay *relay) = 0;
virtual void SetDelayCallback(DelayCallback_t delayCallback) = 0;

View File

@@ -12,6 +12,7 @@
struct IGpMutex;
struct IGpThreadEvent;
struct IGpClipboardContents;
struct IGpSystemServices
{
@@ -29,9 +30,12 @@ public:
virtual bool IsTouchscreen() const = 0;
virtual bool IsUsingMouseAsTouch() const = 0;
virtual bool IsFullscreenPreferred() const = 0;
virtual bool IsFullscreenOnStartup() const = 0;
virtual bool IsTextInputObstructive() const = 0;
virtual unsigned int GetCPUCount() const = 0;
virtual void SetTextInputEnabled(bool isEnabled) = 0;
virtual bool IsTextInputEnabled() const = 0;
virtual bool AreFontResourcesSeekable() const = 0;
virtual IGpClipboardContents *GetClipboardContents() const = 0;
virtual void SetClipboardContents(IGpClipboardContents *contents) = 0;
};

View File

@@ -1202,6 +1202,12 @@ void GpDisplayDriverD3D11::Run()
float pixelScaleX = 1.0f;
float pixelScaleY = 1.0f;
if (desiredWidth < 640)
desiredWidth = 640;
if (desiredHeight < 480)
desiredHeight = 480;
if (m_properties.m_adjustRequestedResolutionFunc(m_properties.m_adjustRequestedResolutionFuncContext, desiredWidth, desiredHeight, virtualWidth, virtualHeight, pixelScaleX, pixelScaleY))
{
bool resizedOK = ResizeD3DWindow(m_osGlobals->m_hwnd, m_windowWidthPhysical, m_windowHeightPhysical, desiredWidth, desiredHeight, windowStyle, menus, logger);

View File

@@ -154,6 +154,7 @@ void GpInputDriverXInput::ProcessButtonStateChange(DWORD prevState, DWORD newSta
evt->m_event.m_keyboardInputEvent.m_keyIDSubset = GpKeyIDSubsets::kGamepadButton;
evt->m_event.m_keyboardInputEvent.m_key.m_gamepadKey.m_button = gamepadButton;
evt->m_event.m_keyboardInputEvent.m_key.m_gamepadKey.m_player = playerNum;
evt->m_event.m_keyboardInputEvent.m_repeatCount = 0;
}
}

View File

@@ -51,7 +51,7 @@ int main(int argc, const char **argv)
SystemTimeToTzSpecificLocalTime(&tz, &utcST, &localST);
PortabilityLayer::CombinedTimestamp ts;
ts.SetUTCTime(timeDelta);
ts.SetMacEpochTime(timeDelta);
ts.SetLocalYear(localST.wYear);
ts.m_localMonth = localST.wMonth;

388
MergeGPF/MergeGPF.cpp Normal file
View File

@@ -0,0 +1,388 @@
#include "WindowsUnicodeToolShim.h"
#include "CFileStream.h"
#include "CombinedTimestamp.h"
#include "DeflateCodec.h"
#include "MacFileInfo.h"
#include "ZipFile.h"
#include <stdio.h>
#include <string>
#include <algorithm>
int toolMain(int argc, const char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Usage: MergeGPF <file.gpf>");
return -1;
}
std::string metaFileName = argv[1];
if (metaFileName.length() < 5)
{
fprintf(stderr, "Usage: MergeGPF <file.gpf>");
return -1;
}
std::string fnameBase = metaFileName.substr(0, metaFileName.length() - 4);
std::string resName = fnameBase + ".gpa";
std::string dataName = fnameBase + ".gpd";
PortabilityLayer::MacFilePropertiesSerialized mfps;
PortabilityLayer::ZipFileLocalHeader metaLH;
{
FILE *metaF = fopen_utf8(argv[1], "rb");
PortabilityLayer::CFileStream metaStream(metaF);
if (!mfps.ReadFromPackage(metaStream))
{
fprintf(stderr, "Error reading metadata");
return -1;
}
metaStream.SeekStart(0);
metaStream.Read(&metaLH, sizeof(metaLH));
metaStream.Close();
}
PortabilityLayer::MacFileProperties mfp;
mfps.Deserialize(mfp);
mfps.Serialize(mfp);
size_t insertedMetaFSize = sizeof(metaLH) + strlen(PortabilityLayer::MacFilePropertiesSerialized::GetPackagedName()) + sizeof(mfps.m_data);
size_t insertedDataSize = 0;
FILE *mergedF = fopen_utf8(argv[1], "wb");
if (!mergedF)
{
fprintf(stderr, "Error reopening metadata file");
return -1;
}
PortabilityLayer::CFileStream mergedStream(mergedF);
uint32_t numFiles = 0;
uint32_t cdirSize = 0;
const char *metaPackagedName = PortabilityLayer::MacFilePropertiesSerialized::GetPackagedName();
const char *dataPackagedName = "!data";
PortabilityLayer::ZipCentralDirectoryFileHeader metaCDir;
PortabilityLayer::ZipCentralDirectoryFileHeader dataCDir;
bool hasData = false;
// Write metadata
{
GpUFilePos_t metaLHStart = mergedStream.Tell();
PortabilityLayer::ZipFileLocalHeader metaCopyLH;
metaCopyLH.m_signature = PortabilityLayer::ZipFileLocalHeader::kSignature;
metaCopyLH.m_versionRequired = PortabilityLayer::ZipConstants::kStoredRequiredVersion;
metaCopyLH.m_flags = 0;
metaCopyLH.m_method = PortabilityLayer::ZipConstants::kStoredMethod;
metaCopyLH.m_modificationTime = metaLH.m_modificationTime;
metaCopyLH.m_modificationDate = metaLH.m_modificationDate;
metaCopyLH.m_crc = PortabilityLayer::DeflateContext::CRC32(0, mfps.m_data, sizeof(mfps.m_data));
metaCopyLH.m_compressedSize = sizeof(mfps.m_data);
metaCopyLH.m_uncompressedSize = sizeof(mfps.m_data);
metaCopyLH.m_fileNameLength = strlen(metaPackagedName);
metaCopyLH.m_extraFieldLength = 0;
if (!mergedStream.WriteExact(&metaCopyLH, sizeof(metaCopyLH)) || !mergedStream.WriteExact(metaPackagedName, strlen(metaPackagedName)) || !mergedStream.WriteExact(mfps.m_data, sizeof(mfps.m_data)))
{
fprintf(stderr, "Error writing metadata");
return -1;
}
numFiles++;
cdirSize += sizeof(PortabilityLayer::ZipCentralDirectoryFileHeader) + strlen(metaPackagedName);
metaCDir.m_signature = PortabilityLayer::ZipCentralDirectoryFileHeader::kSignature;
metaCDir.m_versionCreated = PortabilityLayer::ZipConstants::kCompressedRequiredVersion;
metaCDir.m_versionRequired = PortabilityLayer::ZipConstants::kStoredRequiredVersion;
metaCDir.m_flags = 0;
metaCDir.m_method = PortabilityLayer::ZipConstants::kStoredMethod;
metaCDir.m_modificationTime = metaLH.m_modificationTime;
metaCDir.m_modificationDate = metaLH.m_modificationDate;
metaCDir.m_crc = metaLH.m_crc;
metaCDir.m_compressedSize = metaLH.m_compressedSize;
metaCDir.m_uncompressedSize = metaLH.m_uncompressedSize;
metaCDir.m_fileNameLength = metaLH.m_fileNameLength;
metaCDir.m_extraFieldLength = metaLH.m_extraFieldLength;
metaCDir.m_commentLength = 0;
metaCDir.m_diskNumber = 0;
metaCDir.m_internalAttributes = 0;
metaCDir.m_externalAttributes = PortabilityLayer::ZipConstants::kArchivedAttributes;
metaCDir.m_localHeaderOffset = metaLHStart;
}
FILE *dataF = fopen_utf8(dataName.c_str(), "rb");
if (dataF)
{
GpUFilePos_t dataLHStart = mergedStream.Tell();
PortabilityLayer::ZipFileLocalHeader dataLH;
dataLH.m_signature = PortabilityLayer::ZipFileLocalHeader::kSignature;
dataLH.m_versionRequired = PortabilityLayer::ZipConstants::kCompressedRequiredVersion;
dataLH.m_flags = 0;
dataLH.m_method = PortabilityLayer::ZipConstants::kDeflatedMethod;
dataLH.m_modificationTime = metaLH.m_modificationTime;
dataLH.m_modificationDate = metaLH.m_modificationDate;
dataLH.m_crc = 0;
dataLH.m_compressedSize = 0;
dataLH.m_uncompressedSize = 0;
dataLH.m_fileNameLength = strlen(dataPackagedName);
dataLH.m_extraFieldLength = 0;
if (!mergedStream.WriteExact(&dataLH, sizeof(dataLH)) || !mergedStream.WriteExact(dataPackagedName, strlen(dataPackagedName)))
{
fprintf(stderr, "Error compressing data");
return -1;
}
PortabilityLayer::DeflateContext *ctx = PortabilityLayer::DeflateContext::Create(&mergedStream, 9);
uint8_t compressBuffer[1024];
uint32_t crc = 0;
size_t uncompressedSize = 0;
GpUFilePos_t compressedDataStart = mergedStream.Tell();
for (;;)
{
size_t dataRead = fread(compressBuffer, 1, sizeof(compressBuffer), dataF);
if (dataRead == 0)
break;
uncompressedSize += dataRead;
if (!ctx->Append(compressBuffer, dataRead))
{
fprintf(stderr, "Error compressing data");
return -1;
}
crc = PortabilityLayer::DeflateContext::CRC32(crc, compressBuffer, dataRead);
}
if (!ctx->Flush())
{
fprintf(stderr, "Error compressing data");
return -1;
}
ctx->Destroy();
GpUFilePos_t compressedDataEnd = mergedStream.Tell();
dataLH.m_crc = crc;
dataLH.m_compressedSize = (compressedDataEnd - compressedDataStart);
dataLH.m_uncompressedSize = uncompressedSize;
if (!mergedStream.SeekStart(dataLHStart) || !mergedStream.Write(&dataLH, sizeof(dataLH)) || !mergedStream.SeekStart(compressedDataEnd))
{
fprintf(stderr, "Error compressing data");
return -1;
}
numFiles++;
cdirSize += sizeof(PortabilityLayer::ZipCentralDirectoryFileHeader) + strlen(dataPackagedName);
hasData = true;
fclose(dataF);
insertedDataSize += sizeof(dataLH) + strlen(dataPackagedName) + (compressedDataEnd - compressedDataStart);
dataCDir.m_signature = PortabilityLayer::ZipCentralDirectoryFileHeader::kSignature;
dataCDir.m_versionCreated = PortabilityLayer::ZipConstants::kCompressedRequiredVersion;
dataCDir.m_versionRequired = PortabilityLayer::ZipConstants::kCompressedRequiredVersion;
dataCDir.m_flags = 0;
dataCDir.m_method = PortabilityLayer::ZipConstants::kDeflatedMethod;
dataCDir.m_modificationTime = dataLH.m_modificationTime;
dataCDir.m_modificationDate = dataLH.m_modificationDate;
dataCDir.m_crc = dataLH.m_crc;
dataCDir.m_compressedSize = dataLH.m_compressedSize;
dataCDir.m_uncompressedSize = dataLH.m_uncompressedSize;
dataCDir.m_fileNameLength = dataLH.m_fileNameLength;
dataCDir.m_extraFieldLength = dataLH.m_extraFieldLength;
dataCDir.m_commentLength = 0;
dataCDir.m_diskNumber = 0;
dataCDir.m_internalAttributes = 0;
dataCDir.m_externalAttributes = PortabilityLayer::ZipConstants::kArchivedAttributes;
dataCDir.m_localHeaderOffset = dataLHStart;
}
std::vector<PortabilityLayer::ZipCentralDirectoryFileHeader> resCentralDir;
std::vector<uint8_t> fileNameBytes;
std::vector<size_t> fileNameSizes;
FILE *resF = fopen_utf8(resName.c_str(), "rb");
{
PortabilityLayer::ZipEndOfCentralDirectoryRecord eocd;
PortabilityLayer::CFileStream resStream(resF);
if (!resStream.SeekEnd(sizeof(PortabilityLayer::ZipEndOfCentralDirectoryRecord)) || !resStream.ReadExact(&eocd, sizeof(eocd)) || !resStream.SeekStart(eocd.m_centralDirStartOffset))
{
fprintf(stderr, "Error reading res data");
return -1;
}
size_t numResFiles = eocd.m_numCentralDirRecords;
for (size_t i = 0; i < numResFiles; i++)
{
PortabilityLayer::ZipCentralDirectoryFileHeader cdirFile;
if (!resStream.ReadExact(&cdirFile, sizeof(cdirFile)))
{
fprintf(stderr, "Error reading cdir entry");
return -1;
}
size_t fileNameLength = cdirFile.m_fileNameLength;
fileNameSizes.push_back(fileNameLength);
if (fileNameLength > 0)
{
fileNameBytes.resize(fileNameBytes.size() + fileNameLength);
if (!resStream.Read(&fileNameBytes[fileNameBytes.size() - fileNameLength], fileNameLength))
{
fprintf(stderr, "Error reading cdir entry");
return -1;
}
}
if (!resStream.SeekCurrent(cdirFile.m_extraFieldLength + cdirFile.m_commentLength))
{
fprintf(stderr, "Error reading cdir entry");
return -1;
}
resCentralDir.push_back(cdirFile);
numFiles++;
cdirSize += sizeof(PortabilityLayer::ZipCentralDirectoryFileHeader) + fileNameLength;
}
for (size_t i = 0; i < resCentralDir.size(); i++)
{
PortabilityLayer::ZipCentralDirectoryFileHeader &cdirHeader = resCentralDir[i];
PortabilityLayer::ZipFileLocalHeader resLH;
if (!resStream.SeekStart(cdirHeader.m_localHeaderOffset) || !resStream.ReadExact(&resLH, sizeof(resLH)) || resLH.m_fileNameLength != cdirHeader.m_fileNameLength || resLH.m_compressedSize != cdirHeader.m_compressedSize || resLH.m_uncompressedSize != cdirHeader.m_uncompressedSize)
{
fprintf(stderr, "Error reading res");
return -1;
}
size_t chunkSizes[] = { resLH.m_fileNameLength, resLH.m_extraFieldLength, resLH.m_compressedSize };
resLH.m_extraFieldLength = 0;
cdirHeader.m_localHeaderOffset = mergedStream.Tell();
cdirHeader.m_extraFieldLength = 0;
cdirHeader.m_commentLength = 0;
if (!mergedStream.WriteExact(&resLH, sizeof(resLH)))
{
fprintf(stderr, "Error copying resource header");
return -1;
}
for (int chunk = 0; chunk < 3; chunk++)
{
size_t chunkCopySize = chunkSizes[chunk];
if (chunk == 1)
{
// Strip extra field
if (!resStream.SeekCurrent(chunkCopySize))
{
fprintf(stderr, "Error copying resource");
return -1;
}
}
else
{
uint8_t copyBuffer[1024];
while (chunkCopySize > 0)
{
size_t amountToCopy = std::min(sizeof(copyBuffer), chunkCopySize);
if (!resStream.ReadExact(copyBuffer, amountToCopy) || !mergedStream.WriteExact(copyBuffer, amountToCopy))
{
fprintf(stderr, "Error copying resource");
return -1;
}
chunkCopySize -= amountToCopy;
}
}
}
}
resStream.Close();
}
GpUFilePos_t cdirPos = mergedStream.Tell();
// Write metadata cdir
if (!mergedStream.WriteExact(&metaCDir, sizeof(metaCDir)) || !mergedStream.WriteExact(metaPackagedName, strlen(metaPackagedName)))
{
fprintf(stderr, "Error writing directory");
return -1;
}
if (hasData)
{
if (!mergedStream.WriteExact(&dataCDir, sizeof(dataCDir)) || !mergedStream.WriteExact(dataPackagedName, strlen(dataPackagedName)))
{
fprintf(stderr, "Error writing directory");
return -1;
}
}
size_t fnameBytesOffset = 0;
for (size_t i = 0; i < resCentralDir.size(); i++)
{
size_t fnameSize = fileNameSizes[i];
const PortabilityLayer::ZipCentralDirectoryFileHeader &cdir = resCentralDir[i];
if (!mergedStream.WriteExact(&cdir, sizeof(cdir)) || (fnameSize > 0 && !mergedStream.WriteExact(&fileNameBytes[fnameBytesOffset], fnameSize)))
{
fprintf(stderr, "Error writing directory");
return -1;
}
fnameBytesOffset += fnameSize;
}
PortabilityLayer::ZipEndOfCentralDirectoryRecord eocd;
eocd.m_signature = PortabilityLayer::ZipEndOfCentralDirectoryRecord::kSignature;
eocd.m_thisDiskNumber = 0;
eocd.m_centralDirDisk = 0;
eocd.m_numCentralDirRecordsThisDisk = numFiles;
eocd.m_numCentralDirRecords = numFiles;
eocd.m_centralDirectorySizeBytes = mergedStream.Tell() - cdirPos;
eocd.m_centralDirStartOffset = cdirPos;
eocd.m_commentLength = 0;
if (!mergedStream.WriteExact(&eocd, sizeof(eocd)))
{
fprintf(stderr, "Error writing EOCD");
return -1;
}
mergedStream.Close();
return 0;
}

92
MergeGPF/MergeGPF.vcxproj Normal file
View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{36DAF5FA-6ADB-4F20-9810-1610DE0AE653}</ProjectGuid>
<RootNamespace>MergeGPF</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\WindowsUnicodeToolShim.props" />
<Import Project="..\PortabilityLayer.props" />
<Import Project="..\Common.props" />
<Import Project="..\Debug.props" />
<Import Project="..\GpCommon.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\WindowsUnicodeToolShim.props" />
<Import Project="..\PortabilityLayer.props" />
<Import Project="..\Common.props" />
<Import Project="..\Release.props" />
<Import Project="..\GpCommon.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\PortabilityLayer\PortabilityLayer.vcxproj">
<Project>{6ec62b0f-9353-40a4-a510-3788f1368b33}</Project>
</ProjectReference>
<ProjectReference Include="..\WindowsUnicodeToolShim\WindowsUnicodeToolShim.vcxproj">
<Project>{15009625-1120-405e-8bba-69a16cd6713d}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="MergeGPF.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="MergeGPF.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -9,6 +9,7 @@
#include "MacBinary2.h"
#include "MacFileMem.h"
#include "WindowsUnicodeToolShim.h"
// Very simplified resource compiler
@@ -117,7 +118,7 @@ void DefError()
exit(-1);
}
int main(int argc, const char **argv)
int toolMain(int argc, const char **argv)
{
if (argc != 3)
{
@@ -125,8 +126,8 @@ int main(int argc, const char **argv)
return -1;
}
FILE *f = nullptr;
if (fopen_s(&f, argv[1], "rb"))
FILE *f = fopen_utf8(argv[1], "rb");
if (!f)
{
fprintf(stderr, "Failed to open input file");
return -1;
@@ -524,8 +525,8 @@ int main(int argc, const char **argv)
printf("Writing to %s...", argv[2]);
FILE *outF = nullptr;
if (fopen_s(&outF, argv[2], "wb"))
FILE *outF = fopen_utf8(argv[2], "wb");
if (!outF)
{
fprintf(stderr, "Failed to open output file");
return -1;

View File

@@ -41,6 +41,7 @@
<Import Project="..\Common.props" />
<Import Project="..\GpCommon.props" />
<Import Project="..\Debug.props" />
<Import Project="..\WindowsUnicodeToolShim.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
@@ -48,6 +49,7 @@
<Import Project="..\Common.props" />
<Import Project="..\GpCommon.props" />
<Import Project="..\Release.props" />
<Import Project="..\WindowsUnicodeToolShim.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
@@ -80,6 +82,9 @@
<ProjectReference Include="..\PortabilityLayer\PortabilityLayer.vcxproj">
<Project>{6ec62b0f-9353-40a4-a510-3788f1368b33}</Project>
</ProjectReference>
<ProjectReference Include="..\WindowsUnicodeToolShim\WindowsUnicodeToolShim.vcxproj">
<Project>{15009625-1120-405e-8bba-69a16cd6713d}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@@ -29,12 +29,14 @@ LOCAL_SRC_FILES := \
EllipsePlotter.cpp \
FileBrowserUI.cpp \
FileManager.cpp \
FileSectionStream.cpp \
FontFamily.cpp \
FontManager.cpp \
FontRenderer.cpp \
GPArchive.cpp \
HostSuspendHook.cpp \
IconLoader.cpp \
InflateStream.cpp \
InputManager.cpp \
LinePlotter.cpp \
MacBinary2.cpp \

View File

@@ -70,12 +70,7 @@ namespace PortabilityLayer
if (!m_file)
return false;
return fseek(m_file, static_cast<long>(loc), SEEK_END) == 0;
}
bool CFileStream::Truncate(GpUFilePos_t loc)
{
return false;
return fseek(m_file, -static_cast<long>(loc), SEEK_END) == 0;
}
GpUFilePos_t CFileStream::Tell() const

View File

@@ -24,7 +24,6 @@ namespace PortabilityLayer
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
bool Truncate(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;

View File

@@ -6,7 +6,7 @@ namespace PortabilityLayer
{
struct CombinedTimestamp
{
uint8_t m_utcTimestamp[8];
uint8_t m_macEpochTimestamp[8];
uint8_t m_localYear[4];
uint8_t m_localMonth;
@@ -18,26 +18,30 @@ namespace PortabilityLayer
uint8_t m_padding[3];
int64_t GetUTCTime() const;
void SetUTCTime(int64_t timestamp);
static const int32_t kMacEpochToUTC = 2082844800;
int64_t GetMacEpochTime() const;
void SetMacEpochTime(int64_t timestamp);
int32_t GetLocalYear() const;
void SetLocalYear(int32_t year);
void GetAsMSDOSTimestamp(uint16_t &msdosDate, uint16_t &msdosTime) const;
};
inline int64_t CombinedTimestamp::GetUTCTime() const
inline int64_t CombinedTimestamp::GetMacEpochTime() const
{
int64_t result = 0;
for (int i = 0; i < 8; i++)
result |= static_cast<int64_t>(m_utcTimestamp[i]) << (i * 8);
result |= static_cast<int64_t>(m_macEpochTimestamp[i]) << (i * 8);
return result;
}
void CombinedTimestamp::SetUTCTime(int64_t timestamp)
inline void CombinedTimestamp::SetMacEpochTime(int64_t timestamp)
{
for (int i = 0; i < 8; i++)
m_utcTimestamp[i] = static_cast<uint8_t>((timestamp >> (i * 8)) & 0xff);
m_macEpochTimestamp[i] = static_cast<uint8_t>((timestamp >> (i * 8)) & 0xff);
}
inline int32_t CombinedTimestamp::GetLocalYear() const
@@ -49,9 +53,45 @@ namespace PortabilityLayer
return result;
}
void CombinedTimestamp::SetLocalYear(int32_t timestamp)
inline void CombinedTimestamp::SetLocalYear(int32_t timestamp)
{
for (int i = 0; i < 4; i++)
m_localYear[i] = static_cast<uint8_t>((timestamp >> (i * 8)) & 0xff);
}
inline void CombinedTimestamp::GetAsMSDOSTimestamp(uint16_t &msdosDate, uint16_t &msdosTime) const
{
int32_t localYear = this->GetLocalYear();
uint8_t month = this->m_localMonth;
uint8_t day = this->m_localDay;
uint8_t hour = this->m_localHour;
uint8_t minute = this->m_localMinute;
uint8_t second = this->m_localSecond;
int32_t yearsSince1980 = localYear - 1980;
if (localYear < 1980)
{
// Time machine
yearsSince1980 = 0;
second = 0;
minute = 0;
hour = 0;
day = 1;
month = 1;
}
else if (localYear > 1980 + 127)
{
yearsSince1980 = 127;
second = 59;
minute = 59;
hour = 23;
day = 31;
month = 12;
}
msdosTime = (second / 2) | (minute << 5) | (hour << 11);
msdosDate = day | (month << 5) | (yearsSince1980 << 9);
}
}

View File

@@ -109,6 +109,32 @@ namespace PortabilityLayer
uint8_t m_flushBuffer[1024];
};
class InflateContextImpl final : public InflateContext
{
public:
static InflateContext *Create();
void Destroy() override;
bool Append(const void *buffer, size_t size, size_t &sizeConsumed) override;
bool Read(void *buffer, size_t size, size_t &sizeRead) override;
bool Reset() override;
bool Init();
private:
InflateContextImpl();
~InflateContextImpl();
bool m_streamInitialized;
bool m_isEndOfStream;
z_stream m_zStream;
uint8_t m_flushBuffer[1024];
const uint8_t *m_readPos;
};
}
PortabilityLayer::DeflateContextImpl::DeflateContextImpl(GpIOStream *stream, int compressionLevel)
@@ -197,6 +223,131 @@ bool PortabilityLayer::DeflateContextImpl::Flush()
}
PortabilityLayer::InflateContext *PortabilityLayer::InflateContextImpl::Create()
{
void *storage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(PortabilityLayer::InflateContextImpl));
if (!storage)
return nullptr;
InflateContextImpl *obj = new (storage) InflateContextImpl();
if (!obj->Init())
{
obj->Destroy();
return nullptr;
}
return obj;
}
void PortabilityLayer::InflateContextImpl::Destroy()
{
this->~InflateContextImpl();
PortabilityLayer::MemoryManager::GetInstance()->Release(this);
}
bool PortabilityLayer::InflateContextImpl::Append(const void *buffer, size_t size, size_t &sizeConsumed)
{
size_t consumed = 0;
m_zStream.avail_in = size;
m_zStream.next_in = static_cast<Bytef*>(const_cast<void*>(buffer));
for (;;)
{
if (m_isEndOfStream)
{
m_zStream.avail_in = 0;
m_zStream.next_in = nullptr;
}
if (m_zStream.avail_in == 0 || m_zStream.avail_out == 0)
{
sizeConsumed = consumed;
return true;
}
size_t lastAvailIn = m_zStream.avail_in;
int result = inflate(&m_zStream, Z_NO_FLUSH);
if (result == Z_STREAM_END)
m_isEndOfStream = true;
else if (result != Z_OK)
return false;
consumed += lastAvailIn - m_zStream.avail_in;
}
}
bool PortabilityLayer::InflateContextImpl::Read(void *buffer, size_t size, size_t &sizeRead)
{
size_t amountInOutputBuffer = static_cast<const uint8_t*>(m_zStream.next_out) - m_readPos;
if (size > amountInOutputBuffer)
size = amountInOutputBuffer;
if (size > 0)
{
if (buffer)
memcpy(buffer, m_readPos, size);
m_readPos += size;
if (m_readPos == m_zStream.next_out)
{
m_zStream.avail_out = sizeof(m_flushBuffer);
m_zStream.next_out = m_flushBuffer;
m_readPos = m_flushBuffer;
}
}
sizeRead = size;
return true;
}
bool PortabilityLayer::InflateContextImpl::Reset()
{
if (inflateReset2(&m_zStream, -15) != Z_OK)
return false;
m_isEndOfStream = false;
m_zStream.avail_out = sizeof(m_flushBuffer);
m_zStream.next_out = m_flushBuffer;
m_readPos = m_flushBuffer;
return true;
}
bool PortabilityLayer::InflateContextImpl::Init()
{
m_zStream.zalloc = ZlibAllocShim;
m_zStream.zfree = ZlibFreeShim;
m_zStream.opaque = MemoryManager::GetInstance();
if (inflateInit2(&m_zStream, -15) != Z_OK)
return false;
m_zStream.next_out = m_flushBuffer;
m_zStream.avail_out = sizeof(m_flushBuffer);
m_streamInitialized = true;
return true;
}
PortabilityLayer::InflateContextImpl::InflateContextImpl()
: m_streamInitialized(false)
, m_isEndOfStream(false)
, m_readPos(m_flushBuffer)
{
memset(&m_zStream, 0, sizeof(m_zStream));
}
PortabilityLayer::InflateContextImpl::~InflateContextImpl()
{
if (m_streamInitialized)
inflateEnd(&m_zStream);
}
PortabilityLayer::DeflateContext *PortabilityLayer::DeflateContext::Create(GpIOStream *stream, int compressionLevel)
{
void *storage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(PortabilityLayer::DeflateContextImpl));
@@ -217,3 +368,9 @@ uint32_t PortabilityLayer::DeflateContext::CRC32(uint32_t inputValue, const void
{
return crc32(inputValue, static_cast<const Bytef*>(buffer), bufferLength);
}
PortabilityLayer::InflateContext *PortabilityLayer::InflateContext::Create()
{
return InflateContextImpl::Create();
}

View File

@@ -19,6 +19,19 @@ namespace PortabilityLayer
static uint32_t CRC32(uint32_t inputValue, const void *buffer, size_t bufferLength);
};
class InflateContext
{
public:
static InflateContext *Create();
virtual void Destroy() = 0;
virtual bool Append(const void *buffer, size_t size, size_t &sizeConsumed) = 0;
virtual bool Read(void *buffer, size_t size, size_t &sizeRead) = 0;
virtual bool Reset() = 0;
};
class DeflateCodec
{
public:

View File

@@ -361,7 +361,7 @@ namespace PortabilityLayer
const bool haveEvent = WaitForEvent(&evt, 1);
if (window->IsHandlingTickEvents())
if (!haveEvent && window->IsHandlingTickEvents())
window->OnTick();
const int16_t selection = (filterFunc != nullptr) ? filterFunc(captureContext, this, haveEvent ? &evt : nullptr) : -1;

View File

@@ -565,8 +565,10 @@ namespace PortabilityLayer
return hit;
}
bool FileBrowserUI::Prompt(Mode mode, VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI)
bool FileBrowserUI::Prompt(Mode mode, VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI)
{
size_t extensionLength = strlen(extension);
int dialogID = 0;
bool isObstructive = false;
int windowHeight = 272;
@@ -603,35 +605,18 @@ namespace PortabilityLayer
{
size_t nameLength = strlen(fileName);
if (nameLength < 4)
if (nameLength < extensionLength)
continue;
const char *nameExt = fileName + (nameLength - 4);
const char *nameExt = fileName + (nameLength - extensionLength);
if (!memcmp(nameExt, ".gpf", 4))
if (!memcmp(nameExt, extension, extensionLength))
{
GpIOStream *metadataStream = fs->OpenFile(dirID, fileName, false, GpFileCreationDispositions::kOpenExisting);
if (!metadataStream)
PLPasStr fnamePStr = PLPasStr(nameLength - extensionLength, fileName);
if (!callbackAPI.m_filterFileCallback(dirID, fnamePStr))
continue;
MacFilePropertiesSerialized serializedMetadata;
if (metadataStream->Read(&serializedMetadata, sizeof(serializedMetadata)) != sizeof(serializedMetadata))
{
metadataStream->Close();
continue;
}
metadataStream->Close();
MacFileProperties metadata;
serializedMetadata.Deserialize(metadata);
char ftype[4];
fileType.ExportAsChars(ftype);
if (memcmp(metadata.m_fileType, ftype, 4))
continue;
if (!uiImpl.AppendName(fileName, nameLength - 4, callbackAPI.m_loadFileDetailsCallback(dirID, PLPasStr(nameLength - 4, fileName))))
if (!uiImpl.AppendName(fileName, nameLength - extensionLength, callbackAPI.m_loadFileDetailsCallback(dirID, fnamePStr)))
{
dirCursor->Destroy();
return false;
@@ -742,15 +727,24 @@ namespace PortabilityLayer
FileBrowserUIImpl::PopUpAlert(Rect::Create(0, 0, 135, 327), kFileBrowserUIBadNameDialogTemplateID, nullptr);
hit = -1;
}
else if (PortabilityLayer::FileManager::GetInstance()->FileExists(dirID, nameStr))
else
{
DialogTextSubstitutions substitutions(nameStr);
bool fileExists = false;
if (composites)
fileExists = PortabilityLayer::FileManager::GetInstance()->CompositeFileExists(dirID, nameStr);
else
fileExists = PortabilityLayer::FileManager::GetInstance()->NonCompositeFileExists(dirID, nameStr, extension);
PLDrivers::GetSystemServices()->Beep();
int16_t subHit = FileBrowserUIImpl::PopUpAlert(Rect::Create(0, 0, 135, 327), kFileBrowserUIOverwriteDialogTemplateID, &substitutions);
if (fileExists)
{
DialogTextSubstitutions substitutions(nameStr);
if (subHit == kOverwriteNoButton)
hit = -1;
PLDrivers::GetSystemServices()->Beep();
int16_t subHit = FileBrowserUIImpl::PopUpAlert(Rect::Create(0, 0, 135, 327), kFileBrowserUIOverwriteDialogTemplateID, &substitutions);
if (subHit == kOverwriteNoButton)
hit = -1;
}
}
}
@@ -763,7 +757,11 @@ namespace PortabilityLayer
{
PLPasStr uiFileName = uiImpl.GetSelectedFileName();
PortabilityLayer::FileManager::GetInstance()->DeleteFile(dirID, uiFileName);
if (composites)
PortabilityLayer::FileManager::GetInstance()->DeleteCompositeFile(dirID, uiFileName);
else
PortabilityLayer::FileManager::GetInstance()->DeleteNonCompositeFile(dirID, uiFileName, extension);
uiImpl.RemoveSelectedFile();
dialog->GetItems()[kOkayButton - 1].GetWidget()->SetEnabled(false);
dialog->GetItems()[kDeleteButton - 1].GetWidget()->SetEnabled(false);

View File

@@ -20,6 +20,7 @@ namespace PortabilityLayer
void *(*m_loadFileDetailsCallback)(VirtualDirectory_t dirID, const PLPasStr &filename);
void(*m_freeFileDetailsCallback)(void *fileDetails);
bool(*m_filterFileCallback)(VirtualDirectory_t dirID, const PLPasStr &filename);
};
class FileBrowserUI
@@ -32,6 +33,6 @@ namespace PortabilityLayer
Mode_Open,
};
static bool Prompt(Mode mode, VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI);
static bool Prompt(Mode mode, VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI);
};
}

View File

@@ -1,5 +1,6 @@
#include "FileManager.h"
#include "CombinedTimestamp.h"
#include "FileBrowserUI.h"
#include "IGpFileSystem.h"
#include "IGpSystemServices.h"
@@ -7,6 +8,7 @@
#include "MacBinary2.h"
#include "MacFileMem.h"
#include "ResTypeID.h"
#include "ZipFileProxy.h"
#include "PLDrivers.h"
#include "PLPasStr.h"
@@ -18,68 +20,188 @@
namespace PortabilityLayer
{
class VirtualFile;
class CompositeFileImpl;
typedef char ExtendedFileName_t[64 + 4];
namespace FileManagerTools
{
bool ConstructFilename(ExtendedFileName_t& extFN, const PLPasStr &fn, const char *extension);
};
class FileManagerImpl final : public FileManager
{
public:
bool FileExists(VirtualDirectory_t dirID, const PLPasStr &filename) override;
bool FileLocked(VirtualDirectory_t dirID, const PLPasStr &filename) override;
bool DeleteFile(VirtualDirectory_t dirID, const PLPasStr &filename) override;
CompositeFile *OpenCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename) override;
PLError_t OpenNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *extension, EFilePermission filePermission, GpFileCreationDisposition_t creationDisposition, GpIOStream *&outStream) override;
bool CompositeFileExists(VirtualDirectory_t dirID, const PLPasStr &filename) override;
bool NonCompositeFileExists(VirtualDirectory_t dirID, const PLPasStr &filename, const char *extension) override;
bool DeleteNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext) override;
bool DeleteCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename) override;
PLError_t CreateFile(VirtualDirectory_t dirID, const PLPasStr &filename, const MacFileProperties &mfp) override;
PLError_t CreateFileAtCurrentTime(VirtualDirectory_t dirID, const PLPasStr &filename, const ResTypeID &fileCreator, const ResTypeID &fileType) override;
PLError_t OpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, GpIOStream *&outRefNum) override;
PLError_t OpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, GpIOStream *&outRefNum) 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, GpFileCreationDisposition_t creationDisposition, GpIOStream *&outStream) override;
PLError_t RawOpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, GpFileCreationDisposition_t creationDisposition, GpIOStream *&outStream) override;
bool PromptSaveFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) override;
bool PromptOpenFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) override;
bool PromptSaveFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) override;
bool PromptOpenFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) override;
static FileManagerImpl *GetInstance();
private:
typedef char ExtendedFileName_t[64 + 4];
PLError_t OpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext, EFilePermission permission, GpIOStream *&outRefNum);
PLError_t RawOpenFileFork(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext, EFilePermission permission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, GpIOStream *&outStream);
static bool ConstructFilename(ExtendedFileName_t& extFN, const PLPasStr &fn, const char *extension);
static FileManagerImpl ms_instance;
};
bool FileManagerImpl::FileExists(VirtualDirectory_t dirID, const PLPasStr &filename)
class CompositeFileImpl final : public CompositeFile
{
public:
PLError_t OpenData(EFilePermission filePermission, GpFileCreationDisposition_t disposition, GpIOStream *&outStream) override;
PLError_t OpenResources(GpIOStream *&outStream, ZipFileProxy *&outProxy, bool &outIsProxyShared) override;
const MacFileProperties &GetProperties() const override;
VirtualDirectory_t GetDirectory() const override;
PLPasStr GetFileName() const override;
bool IsDataReadOnly() const override;
void Close() override;
static CompositeFileImpl *Create(VirtualDirectory_t dirID, const PLPasStr &filename, GpIOStream *stream, ZipFileProxy *zipFile, const MacFileProperties &mfp, bool resInline, bool dataInline, size_t inlineDataIndex);
private:
CompositeFileImpl(VirtualDirectory_t dirID, const PLPasStr &filename, GpIOStream *stream, ZipFileProxy *zipFile, const MacFileProperties &mfp, bool resInline, bool dataInline, size_t inlineDataIndex);
~CompositeFileImpl();
VirtualDirectory_t m_dirID;
PascalStr<255> m_filename;
GpIOStream *m_stream;
ZipFileProxy *m_zipFile;
MacFileProperties m_mfp;
size_t m_inlineDataIndex;
bool m_resInline;
bool m_dataInline;
};
CompositeFile *FileManagerImpl::OpenCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename)
{
ExtendedFileName_t extFN;
if (!ConstructFilename(extFN, filename, ".gpf"))
if (!FileManagerTools::ConstructFilename(extFN, filename, ".gpf"))
return nullptr;
GpIOStream *stream = PLDrivers::GetFileSystem()->OpenFile(dirID, extFN, false, GpFileCreationDispositions::kOpenExisting);
if (!stream)
return nullptr;
ZipFileProxy *zipFile = ZipFileProxy::Create(stream);
if (!zipFile)
{
stream->Close();
return nullptr;
}
size_t metaIndex = 0;
if (!zipFile->IndexFile("!!meta", metaIndex))
{
stream->Close();
return nullptr;
}
MacFilePropertiesSerialized mfps;
GpIOStream *metaStream = zipFile->OpenFile(metaIndex);
if (!metaStream)
{
zipFile->Destroy();
stream->Close();
return nullptr;
}
if (!metaStream->ReadExact(mfps.m_data, sizeof(mfps.m_data)))
{
metaStream->Close();
zipFile->Destroy();
stream->Close();
return nullptr;
}
metaStream->Close();
MacFileProperties mfp;
mfps.Deserialize(mfp);
size_t dataIndex = 0;
bool hasData = zipFile->IndexFile("!data", dataIndex);
size_t nonResFiles = 1 + (hasData ? 1 : 0);
bool hasResources = (zipFile->NumFiles() > nonResFiles);
if (!hasData && !hasResources)
{
zipFile->Destroy();
zipFile = nullptr;
stream->Close();
stream = nullptr;
}
CompositeFile *compositeFile = CompositeFileImpl::Create(dirID, filename, stream, zipFile, mfp, hasResources, hasData, hasData ? dataIndex : 0);
if (!compositeFile)
{
if (zipFile)
zipFile->Destroy();
if (stream)
stream->Close();
return nullptr;
}
return compositeFile;
}
PLError_t FileManagerImpl::OpenNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *extension, EFilePermission filePermission, GpFileCreationDisposition_t creationDisposition, GpIOStream *&outStream)
{
return RawOpenFileFork(dirID, filename, extension, filePermission, true, creationDisposition, outStream);
}
bool FileManagerImpl::NonCompositeFileExists(VirtualDirectory_t dirID, const PLPasStr &filename, const char *extension)
{
ExtendedFileName_t extFN;
if (!FileManagerTools::ConstructFilename(extFN, filename, extension))
return false;
return PLDrivers::GetFileSystem()->FileExists(dirID, extFN);
}
bool FileManagerImpl::FileLocked(VirtualDirectory_t dirID, const PLPasStr &filename)
bool FileManagerImpl::CompositeFileExists(VirtualDirectory_t dirID, const PLPasStr &filename)
{
const char *exts[3] = { ".gpf", ".gpa", ".gpd" };
for (int extIndex = 0; extIndex < sizeof(exts) / sizeof(exts[0]); extIndex++)
{
ExtendedFileName_t extFN;
if (!ConstructFilename(extFN, filename, exts[extIndex]))
return true;
bool exists = false;
if (PLDrivers::GetFileSystem()->FileLocked(dirID, extFN, &exists) && exists)
return true;
}
return false;
return NonCompositeFileExists(dirID, filename, ".gpf");
}
bool FileManagerImpl::DeleteFile(VirtualDirectory_t dirID, const PLPasStr &filename)
bool FileManagerImpl::DeleteNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext)
{
ExtendedFileName_t extFN;
if (!FileManagerTools::ConstructFilename(extFN, filename, ext))
return true;
bool existed = false;
if (!PLDrivers::GetFileSystem()->DeleteFile(dirID, extFN, existed))
{
if (!existed)
return false;
}
return true;
}
bool FileManagerImpl::DeleteCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename)
{
const size_t numExts = 3;
@@ -89,7 +211,7 @@ namespace PortabilityLayer
for (int extIndex = 0; extIndex < numExts; extIndex++)
{
ExtendedFileName_t extFN;
if (!ConstructFilename(extFN, filename, exts[extIndex]))
if (!FileManagerTools::ConstructFilename(extFN, filename, exts[extIndex]))
return true;
bool existed = false;
@@ -111,7 +233,7 @@ namespace PortabilityLayer
serialized.Serialize(mfp);
ExtendedFileName_t extFN;
if (!ConstructFilename(extFN, filename, ".gpf"))
if (!FileManagerTools::ConstructFilename(extFN, filename, ".gpf"))
return PLErrors::kBadFileName;
GpIOStream *stream = nullptr;
@@ -119,7 +241,9 @@ namespace PortabilityLayer
if (err)
return err;
if (stream->Write(serialized.m_data, sizeof(serialized.m_data)) != sizeof(serialized.m_data))
CombinedTimestamp ts;
if (!serialized.WriteAsPackage(*stream, ts))
{
stream->Close();
return PLErrors::kIOError;
@@ -135,38 +259,11 @@ namespace PortabilityLayer
MacFileProperties mfp;
fileCreator.ExportAsChars(mfp.m_fileCreator);
fileType.ExportAsChars(mfp.m_fileType);
mfp.m_creationDate = mfp.m_modifiedDate = PLDrivers::GetSystemServices()->GetTime();
mfp.m_createdTimeMacEpoch = mfp.m_modifiedTimeMacEpoch = PLDrivers::GetSystemServices()->GetTime();
return CreateFile(dirID, filename, mfp);
}
PLError_t FileManagerImpl::OpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission permission, GpIOStream *&outStream)
{
return OpenFileFork(dirID, filename, ".gpd", permission, outStream);
}
PLError_t FileManagerImpl::OpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission permission, GpIOStream *&outStream)
{
return OpenFileFork(dirID, filename, ".gpa", permission, outStream);
}
bool FileManagerImpl::ReadFileProperties(VirtualDirectory_t dirID, const PLPasStr &filename, MacFileProperties &properties)
{
GpIOStream *stream = nullptr;
PLError_t err = RawOpenFileFork(dirID, filename, ".gpf", EFilePermission_Read, true, GpFileCreationDispositions::kOpenExisting, stream);
if (err)
return false;
MacFilePropertiesSerialized serialized;
bool readOk = (stream->Read(serialized.m_data, MacFilePropertiesSerialized::kSize) == MacFilePropertiesSerialized::kSize);
stream->Close();
if (readOk)
serialized.Deserialize(properties);
return readOk;
}
PLError_t FileManagerImpl::RawOpenFileData(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission permission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, GpIOStream *&outStream)
{
return RawOpenFileFork(dirID, filename, ".gpd", permission, ignoreMeta, createDisposition, outStream);
@@ -177,18 +274,18 @@ namespace PortabilityLayer
return RawOpenFileFork(dirID, filename, ".gpa", permission, ignoreMeta, createDisposition, outStream);
}
bool FileManagerImpl::PromptSaveFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &detailsAPI)
bool FileManagerImpl::PromptSaveFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &detailsAPI)
{
ExtendedFileName_t extFN;
if (!ConstructFilename(extFN, initialFileName, ""))
if (!FileManagerTools::ConstructFilename(extFN, initialFileName, ""))
return false;
return FileBrowserUI::Prompt(FileBrowserUI::Mode_Save, dirID, fileType, path, outPathLength, pathCapacity, initialFileName, promptText, detailsAPI);
return FileBrowserUI::Prompt(FileBrowserUI::Mode_Save, dirID, extension, path, outPathLength, pathCapacity, initialFileName, promptText, composites, detailsAPI);
}
bool FileManagerImpl::PromptOpenFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &detailsAPI)
bool FileManagerImpl::PromptOpenFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &detailsAPI)
{
return FileBrowserUI::Prompt(FileBrowserUI::Mode_Open, dirID, fileType, path, outPathLength, pathCapacity, PSTR(""), promptText, detailsAPI);
return FileBrowserUI::Prompt(FileBrowserUI::Mode_Open, dirID, extension, path, outPathLength, pathCapacity, PSTR(""), promptText, composites, detailsAPI);
}
FileManagerImpl *FileManagerImpl::GetInstance()
@@ -220,14 +317,14 @@ namespace PortabilityLayer
if (!ignoreMeta)
{
if (!ConstructFilename(gpfExtFN, filename, ".gpf"))
if (!FileManagerTools::ConstructFilename(gpfExtFN, filename, ".gpf"))
return PLErrors::kBadFileName;
if (!PLDrivers::GetFileSystem()->FileExists(dirID, gpfExtFN))
return PLErrors::kFileNotFound;
}
if (!ConstructFilename(extFN, filename, ext))
if (!FileManagerTools::ConstructFilename(extFN, filename, ext))
return PLErrors::kBadFileName;
GpIOStream *fstream = nullptr;
@@ -253,19 +350,37 @@ namespace PortabilityLayer
}
if (!fstream)
{
if (ignoreMeta)
{
if (!PLDrivers::GetFileSystem()->FileExists(dirID, extFN))
return PLErrors::kFileNotFound;
}
return PLErrors::kAccessDenied;
}
outStream = fstream;
return PLErrors::kNone;
}
bool FileManagerImpl::ConstructFilename(ExtendedFileName_t &extFN, const PLPasStr &fn, const char *extension)
FileManagerImpl FileManagerImpl::ms_instance;
FileManager *FileManager::GetInstance()
{
return FileManagerImpl::GetInstance();
}
bool FileManagerTools::ConstructFilename(ExtendedFileName_t &extFN, const PLPasStr &fn, const char *extension)
{
const size_t fnameSize = fn.Length();
if (fnameSize >= 64)
return false;
assert(strlen(extension) <= 4);
memcpy(extFN, fn.Chars(), fnameSize);
memcpy(extFN + fnameSize, extension, strlen(extension) + 1);
@@ -275,10 +390,119 @@ namespace PortabilityLayer
return true;
}
FileManagerImpl FileManagerImpl::ms_instance;
FileManager *FileManager::GetInstance()
// ==========================================================================
PLError_t CompositeFileImpl::OpenData(EFilePermission filePermission, GpFileCreationDisposition_t disposition, GpIOStream *&outStream)
{
return FileManagerImpl::GetInstance();
if (m_dataInline)
{
if (filePermission == EFilePermission_Any || filePermission == EFilePermission_Read)
{
GpIOStream *stream = m_zipFile->OpenFile(m_inlineDataIndex);
if (!stream)
return PLErrors::kIOError;
outStream = stream;
return PLErrors::kNone;
}
else
return PLErrors::kAccessDenied;
}
else
return FileManager::GetInstance()->RawOpenFileData(m_dirID, m_filename.ToShortStr(), filePermission, true, disposition, outStream);
}
PLError_t CompositeFileImpl::OpenResources(GpIOStream *&outStream, ZipFileProxy *&outProxy, bool &outIsProxyShared)
{
if (m_resInline)
{
outStream = nullptr;
outProxy = m_zipFile;
outIsProxyShared = true;
return PLErrors::kNone;
}
else
{
GpIOStream *stream = nullptr;
PLError_t err = FileManager::GetInstance()->RawOpenFileResources(m_dirID, m_filename.ToShortStr(), EFilePermission_Read, true, GpFileCreationDispositions::kOpenExisting, stream);
if (err != PLErrors::kNone)
return err;
ZipFileProxy *proxy = ZipFileProxy::Create(stream);
if (!proxy)
{
stream->Close();
return PLErrors::kIOError;
}
outStream = stream;
outProxy = proxy;
outIsProxyShared = false;
return PLErrors::kNone;
}
}
const MacFileProperties &CompositeFileImpl::GetProperties() const
{
return m_mfp;
}
VirtualDirectory_t CompositeFileImpl::GetDirectory() const
{
return m_dirID;
}
PLPasStr CompositeFileImpl::GetFileName() const
{
return m_filename.ToShortStr();
}
bool CompositeFileImpl::IsDataReadOnly() const
{
if (m_dataInline)
return true;
ExtendedFileName_t extFN;
if (!FileManagerTools::ConstructFilename(extFN, m_filename.ToShortStr(), ".gpd"))
return false;
bool exists = false;
return PLDrivers::GetFileSystem()->FileLocked(m_dirID, extFN, exists);
}
void CompositeFileImpl::Close()
{
this->~CompositeFileImpl();
free(this);
}
CompositeFileImpl *CompositeFileImpl::Create(VirtualDirectory_t dirID, const PLPasStr &filename, GpIOStream *stream, ZipFileProxy *zipFile, const MacFileProperties &mfp, bool resInline, bool dataInline, size_t inlineDataIndex)
{
void *storage = malloc(sizeof(CompositeFileImpl));
if (!storage)
return nullptr;
return new (storage) CompositeFileImpl(dirID, filename, stream, zipFile, mfp, resInline, dataInline, inlineDataIndex);
}
CompositeFileImpl::CompositeFileImpl(VirtualDirectory_t dirID, const PLPasStr &filename, GpIOStream *stream, ZipFileProxy *zipFile, const MacFileProperties &mfp, bool resInline, bool dataInline, size_t inlineDataIndex)
: m_dirID(dirID)
, m_filename(filename)
, m_stream(stream)
, m_zipFile(zipFile)
, m_mfp(mfp)
, m_resInline(resInline)
, m_dataInline(dataInline)
, m_inlineDataIndex(inlineDataIndex)
{
}
CompositeFileImpl::~CompositeFileImpl()
{
if (m_zipFile)
m_zipFile->Destroy();
if (m_stream)
m_stream->Close();
}
}

View File

@@ -17,27 +17,46 @@ namespace PortabilityLayer
class ResTypeID;
struct MacFileProperties;
struct FileBrowserUI_DetailsCallbackAPI;
class ZipFileProxy;
class CompositeFile
{
public:
virtual PLError_t OpenData(EFilePermission filePermission, GpFileCreationDisposition_t disposition, GpIOStream *&outStream) = 0;
virtual PLError_t OpenResources(GpIOStream *&outStream, ZipFileProxy *&outProxy, bool &outIsProxyShared) = 0;
virtual const MacFileProperties &GetProperties() const = 0;
virtual VirtualDirectory_t GetDirectory() const = 0;
virtual PLPasStr GetFileName() const = 0;
virtual bool IsDataReadOnly() const = 0;
virtual void Close() = 0;
protected:
inline CompositeFile() {}
inline ~CompositeFile() {}
};
class FileManager
{
public:
virtual bool FileExists(VirtualDirectory_t dirID, const PLPasStr &filename) = 0;
virtual bool FileLocked(VirtualDirectory_t dirID, const PLPasStr &filename) = 0;
virtual bool DeleteFile(VirtualDirectory_t dirID, const PLPasStr &filename) = 0;
virtual CompositeFile *OpenCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename) = 0;
virtual PLError_t OpenNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *extension, EFilePermission filePermission, GpFileCreationDisposition_t creationDisposition, GpIOStream *&outStream) = 0;
virtual bool CompositeFileExists(VirtualDirectory_t dirID, const PLPasStr &filename) = 0;
virtual bool NonCompositeFileExists(VirtualDirectory_t dirID, const PLPasStr &filename, const char *extension) = 0;
virtual bool DeleteNonCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename, const char *ext) = 0;
virtual bool DeleteCompositeFile(VirtualDirectory_t dirID, const PLPasStr &filename) = 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;
// 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, GpIOStream *&outStream) = 0;
virtual PLError_t OpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, GpIOStream *&outStream) = 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, GpFileCreationDisposition_t createDisposition, GpIOStream *&outStream) = 0;
virtual PLError_t RawOpenFileResources(VirtualDirectory_t dirID, const PLPasStr &filename, EFilePermission filePermission, bool ignoreMeta, GpFileCreationDisposition_t createDisposition, GpIOStream *&outStream) = 0;
virtual bool PromptSaveFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) = 0;
virtual bool PromptOpenFile(VirtualDirectory_t dirID, const ResTypeID &fileType, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) = 0;
virtual bool PromptSaveFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &initialFileName, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) = 0;
virtual bool PromptOpenFile(VirtualDirectory_t dirID, const char *extension, char *path, size_t &outPathLength, size_t pathCapacity, const PLPasStr &promptText, bool composites, const FileBrowserUI_DetailsCallbackAPI &callbackAPI) = 0;
static FileManager *GetInstance();
};

View File

@@ -0,0 +1,171 @@
#include "FileSectionStream.h"
#include <stdlib.h>
#include <new>
namespace PortabilityLayer
{
class FileSectionStreamImpl final : public GpIOStream
{
public:
FileSectionStreamImpl(GpIOStream *stream, GpUFilePos_t start, GpUFilePos_t size);
~FileSectionStreamImpl();
size_t Read(void *bytesOut, size_t size) override;
size_t Write(const void *bytes, size_t size) override;
bool IsSeekable() const override;
bool IsReadOnly() const override;
bool IsWriteOnly() const override;
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;
void Flush() override;
private:
GpIOStream *m_stream;
GpUFilePos_t m_start;
GpUFilePos_t m_size;
GpUFilePos_t m_expectedPosition;
bool m_isSeekable;
};
FileSectionStreamImpl::FileSectionStreamImpl(GpIOStream *stream, GpUFilePos_t start, GpUFilePos_t size)
: m_stream(stream)
, m_start(start)
, m_size(size)
, m_expectedPosition(start)
, m_isSeekable(stream->IsSeekable())
{
}
FileSectionStreamImpl::~FileSectionStreamImpl()
{
}
size_t FileSectionStreamImpl::Read(void *bytesOut, size_t size)
{
if (m_stream->Tell() != m_expectedPosition)
{
if (!m_stream->SeekStart(m_expectedPosition))
return 0;
}
GpUFilePos_t localPos = m_expectedPosition - m_start;
GpUFilePos_t availableBytes = m_size - localPos;
if (size > availableBytes)
size = static_cast<size_t>(availableBytes);
const size_t actuallyRead = m_stream->Read(bytesOut, size);
m_expectedPosition += actuallyRead;
return actuallyRead;
}
size_t FileSectionStreamImpl::Write(const void *bytes, size_t size)
{
return 0;
}
bool FileSectionStreamImpl::IsSeekable() const
{
return m_isSeekable;
}
bool FileSectionStreamImpl::IsReadOnly() const
{
return true;
}
bool FileSectionStreamImpl::IsWriteOnly() const
{
return false;
}
bool FileSectionStreamImpl::SeekStart(GpUFilePos_t loc)
{
if (loc == m_expectedPosition - m_start)
return true;
if (!m_isSeekable)
return false;
if (loc >= m_size)
return false;
else
{
m_expectedPosition = m_start + loc;
return true;
}
}
bool FileSectionStreamImpl::SeekCurrent(GpFilePos_t loc)
{
GpUFilePos_t localPos = m_expectedPosition - m_start;
if (loc < 0)
{
GpUFilePos_t negativePos = static_cast<GpUFilePos_t>(-loc);
if (negativePos > localPos)
return false;
m_expectedPosition -= negativePos;
return true;
}
else if (loc > 0)
{
GpUFilePos_t positivePos = static_cast<GpUFilePos_t>(loc);
if (m_size - localPos < positivePos)
return false;
m_expectedPosition += positivePos;
return true;
}
else
return true;
}
bool FileSectionStreamImpl::SeekEnd(GpUFilePos_t loc)
{
if (loc > m_size)
return false;
m_expectedPosition = m_start + (m_size - loc);
return true;
}
GpUFilePos_t FileSectionStreamImpl::Size() const
{
return m_size;
}
GpUFilePos_t FileSectionStreamImpl::Tell() const
{
return m_expectedPosition - m_start;
}
void FileSectionStreamImpl::Close()
{
this->~FileSectionStreamImpl();
free(this);
}
void FileSectionStreamImpl::Flush()
{
m_stream->Flush();
}
GpIOStream *FileSectionStream::Create(GpIOStream *stream, GpUFilePos_t start, GpUFilePos_t size)
{
void *storage = malloc(sizeof(FileSectionStreamImpl));
if (!storage)
return nullptr;
return new (storage) FileSectionStreamImpl(stream, start, size);
}
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "GpIOStream.h"
namespace PortabilityLayer
{
namespace FileSectionStream
{
GpIOStream *Create(GpIOStream *stream, GpUFilePos_t start, GpUFilePos_t size);
};
}

View File

@@ -29,7 +29,6 @@ namespace PortabilityLayer
FontFamily *GetHandwritingFont(int textSize, int variationFlags) const override;
FontFamily *GetMonospaceFont(int textSize, int variationFlags) const override;
RenderedFont *GetRenderedFont(IGpFont *font, int size, bool aa, FontHacks fontHacks) override;
RenderedFont *GetRenderedFontFromFamily(FontFamily *font, int size, bool aa, int flags) override;
RenderedFont *LoadCachedRenderedFont(int cacheID, int size, bool aa, int flags) const override;
@@ -51,7 +50,7 @@ namespace PortabilityLayer
struct CachedRenderedFont
{
RenderedFont *m_rfont;
const IGpFont *m_font;
int m_fontCacheID;
int m_size;
uint32_t m_lastUsage;
bool m_aa;
@@ -59,6 +58,9 @@ namespace PortabilityLayer
FontManagerImpl();
bool FindOrReserveCacheSlot(int cacheID, int size, bool aa, CachedRenderedFont *&outCacheSlot, RenderedFont *&outRF);
void ReplaceCachedRenderedFont(CachedRenderedFont &cacheSlot, RenderedFont *rfont, int cacheID, int size, bool aa, int flags);
void ResetUsageCounter();
static int CRFSortPredicate(const void *a, const void *b);
@@ -147,7 +149,7 @@ namespace PortabilityLayer
return m_monospaceFont;
}
RenderedFont *FontManagerImpl::GetRenderedFont(IGpFont *font, int size, bool aa, FontHacks fontHacks)
bool FontManagerImpl::FindOrReserveCacheSlot(int cacheID, int size, bool aa, CachedRenderedFont *&outCacheSlot, RenderedFont *&outRF)
{
CachedRenderedFont *newCacheSlot = &m_cachedRenderedFonts[0];
@@ -160,7 +162,7 @@ namespace PortabilityLayer
break;
}
if (crf.m_font == font && crf.m_size == size && crf.m_aa == aa)
if (crf.m_fontCacheID == cacheID && crf.m_size == size && crf.m_aa == aa)
{
crf.m_lastUsage = m_usageCounter;
RenderedFont *rf = crf.m_rfont;
@@ -169,52 +171,70 @@ namespace PortabilityLayer
else
m_usageCounter++;
return rf;
outRF = rf;
return true;
}
if (newCacheSlot->m_rfont != nullptr && crf.m_lastUsage < newCacheSlot->m_lastUsage)
newCacheSlot = &crf;
}
RenderedFont *rfont = FontRenderer::GetInstance()->RenderFont(font, size, aa, fontHacks);
if (!rfont)
return nullptr;
outCacheSlot = newCacheSlot;
return false;
}
if (newCacheSlot->m_rfont)
newCacheSlot->m_rfont->Destroy();
void FontManagerImpl::ReplaceCachedRenderedFont(CachedRenderedFont &cacheSlot, RenderedFont *rfont, int cacheID, int size, bool aa, int flags)
{
if (cacheSlot.m_rfont)
cacheSlot.m_rfont->Destroy();
newCacheSlot->m_font = font;
newCacheSlot->m_lastUsage = m_usageCounter;
newCacheSlot->m_size = size;
newCacheSlot->m_rfont = rfont;
newCacheSlot->m_aa = aa;
cacheSlot.m_fontCacheID = cacheID;
cacheSlot.m_lastUsage = m_usageCounter;
cacheSlot.m_size = size;
cacheSlot.m_rfont = rfont;
cacheSlot.m_aa = aa;
if (m_usageCounter == UINT32_MAX)
ResetUsageCounter();
else
m_usageCounter++;
return rfont;
}
RenderedFont *FontManagerImpl::GetRenderedFontFromFamily(FontFamily *fontFamily, int size, bool aa, int flags)
{
PortabilityLayer::FontManager *fm = PortabilityLayer::FontManager::GetInstance();
RenderedFont *rfont = fm->LoadCachedRenderedFont(fontFamily->GetCacheID(), size, aa, flags);
if (rfont)
RenderedFont *rfont = nullptr;
CachedRenderedFont *cacheSlot = nullptr;
int cacheID = fontFamily->GetCacheID();
if (this->FindOrReserveCacheSlot(cacheID, size, aa, cacheSlot, rfont))
return rfont;
rfont = fm->LoadCachedRenderedFont(cacheID, size, aa, flags);
if (rfont)
{
ReplaceCachedRenderedFont(*cacheSlot, rfont, cacheID, size, aa, flags);
return rfont;
}
const int variation = fontFamily->GetVariationForFlags(flags);
IGpFont *hostFont = fontFamily->GetFontForVariation(variation);
if (!hostFont)
return nullptr;
rfont = fm->GetRenderedFont(hostFont, size, aa, fontFamily->GetHacksForVariation(variation));
rfont = FontRenderer::GetInstance()->RenderFont(hostFont, size, aa, fontFamily->GetHacksForVariation(variation));
if (rfont)
{
fm->SaveCachedRenderedFont(rfont, fontFamily->GetCacheID(), size, aa, flags);
ReplaceCachedRenderedFont(*cacheSlot, rfont, cacheID, size, aa, flags);
return rfont;
}
return rfont;
}

View File

@@ -20,7 +20,6 @@ namespace PortabilityLayer
virtual FontFamily *GetHandwritingFont(int fontSize, int variationFlags) const = 0;
virtual FontFamily *GetMonospaceFont(int fontSize, int variationFlags) const = 0;
virtual RenderedFont *GetRenderedFont(IGpFont *font, int size, bool aa, FontHacks fontHacks) = 0;
virtual RenderedFont *GetRenderedFontFromFamily(FontFamily *fontFamily, int fontSize, bool aa, int flags) = 0;
virtual RenderedFont *LoadCachedRenderedFont(int cacheID, int size, bool aa, int flags) const = 0;

View File

@@ -27,7 +27,11 @@ static const char *gs_forbiddenNames[] =
static bool IsCharForbidden(char c)
{
return (c < ' ' || c == '<' || c == '>' || c == ':' || c == '\"' || c == '/' || c == '\\' || c == '|' || c == '?' || c == '*' || c > '~' || c == '$' || c == '#');
if ((c & 0x80) != 0)
return true;
// <= '$' includes space, ! " # $
return (c <= '$' || c == '<' || c == '>' || c == ':' || c == '\"' || c == '/' || c == '\\' || c == '|' || c == '?' || c == '*' || c > '~');
}
namespace PortabilityLayer

View File

@@ -0,0 +1,250 @@
#include "InflateStream.h"
#include "DeflateCodec.h"
#include <stdlib.h>
#include <new>
namespace PortabilityLayer
{
class InflateStreamImpl final : public GpIOStream
{
public:
InflateStreamImpl(GpIOStream *stream, GpUFilePos_t start, size_t compressedSize, size_t decompressedSize, InflateContext *inflateContext);
InflateStreamImpl();
size_t Read(void *bytesOut, size_t size) override;
size_t Write(const void *bytes, size_t size) override;
bool IsSeekable() const override;
bool IsReadOnly() const override;
bool IsWriteOnly() const override;
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;
void Flush() override;
private:
GpIOStream *m_stream;
InflateContext *m_inflateContext;
bool m_contextBroken;
GpUFilePos_t m_start;
GpUFilePos_t m_compressedSize;
GpUFilePos_t m_compressedPos;
size_t m_decompressedSize;
size_t m_decompressedPos;
uint8_t m_compressedInputBytes[1024];
size_t m_compressedInputReadOffset;
size_t m_compressedInputSize;
};
InflateStreamImpl::InflateStreamImpl(GpIOStream *stream, GpUFilePos_t start, size_t compressedSize, size_t decompressedSize, InflateContext *inflateContext)
: m_stream(stream)
, m_start(start)
, m_compressedPos(start)
, m_compressedSize(compressedSize)
, m_decompressedSize(decompressedSize)
, m_decompressedPos(0)
, m_inflateContext(inflateContext)
, m_compressedInputReadOffset(0)
, m_compressedInputSize(0)
, m_contextBroken(false)
{
}
InflateStreamImpl::InflateStreamImpl()
{
}
size_t InflateStreamImpl::Read(void *bytesOut, size_t size)
{
if (m_contextBroken)
return 0;
size_t sizeAvailable = m_decompressedSize - m_decompressedPos;
if (size > sizeAvailable)
size = sizeAvailable;
size_t totalConsumed = 0;
while (size > 0)
{
size_t sizeConsumed = 0;
if (!m_inflateContext->Read(bytesOut, size, sizeConsumed))
{
m_contextBroken = true;
return 0;
}
if (bytesOut)
bytesOut = static_cast<uint8_t*>(bytesOut) + sizeConsumed;
size -= sizeConsumed;
m_decompressedPos += sizeConsumed;
totalConsumed += sizeConsumed;
if (sizeConsumed == 0)
{
if (m_compressedInputSize == m_compressedInputReadOffset)
{
if (m_compressedPos != m_stream->Tell())
{
if (!m_stream->SeekStart(m_compressedPos))
return 0;
}
GpUFilePos_t compressedAvailable = m_compressedSize - (m_compressedPos - m_start);
if (compressedAvailable == 0)
return totalConsumed;
size_t compressedToRead = sizeof(m_compressedInputBytes);
if (compressedToRead > compressedAvailable)
compressedToRead = compressedAvailable;
if (!m_stream->ReadExact(m_compressedInputBytes, compressedToRead))
return 0;
m_compressedInputReadOffset = 0;
m_compressedInputSize = compressedToRead;
m_compressedPos += compressedToRead;
}
size_t compressedInputAvailable = m_compressedInputSize - m_compressedInputReadOffset;
size_t compressedInputConsumed = 0;
if (!m_inflateContext->Append(m_compressedInputBytes + m_compressedInputReadOffset, compressedInputAvailable, compressedInputConsumed))
{
m_contextBroken = true;
return 0;
}
if (compressedInputConsumed == 0)
return 0; // This should never happen
m_compressedInputReadOffset += compressedInputConsumed;
}
}
return totalConsumed;
}
size_t InflateStreamImpl::Write(const void *bytes, size_t size)
{
return 0;
}
bool InflateStreamImpl::IsSeekable() const
{
return true;
}
bool InflateStreamImpl::IsReadOnly() const
{
return true;
}
bool InflateStreamImpl::IsWriteOnly() const
{
return false;
}
bool InflateStreamImpl::SeekStart(GpUFilePos_t loc)
{
if (m_contextBroken)
return false;
if (loc > m_decompressedSize)
return false;
if (loc < m_decompressedPos)
{
if (!m_inflateContext->Reset())
{
m_contextBroken = true;
return false;
}
m_decompressedPos = 0;
m_compressedInputReadOffset = 0;
m_compressedInputSize = 0;
m_compressedPos = m_start;
}
size_t skipAhead = static_cast<size_t>(loc) - m_decompressedPos;
return this->Read(nullptr, skipAhead) == skipAhead;
}
bool InflateStreamImpl::SeekCurrent(GpFilePos_t loc)
{
if (m_contextBroken)
return false;
if (loc < 0)
{
GpUFilePos_t negativePos = static_cast<GpUFilePos_t>(-loc);
if (negativePos > m_decompressedPos)
return false;
return SeekStart(m_decompressedPos - negativePos);
}
else if (loc > 0)
{
GpUFilePos_t positivePos = static_cast<GpUFilePos_t>(-loc);
if (positivePos > m_decompressedSize - m_decompressedPos)
return false;
return this->Read(nullptr, static_cast<size_t>(positivePos));
}
else
return true;
}
bool InflateStreamImpl::SeekEnd(GpUFilePos_t loc)
{
if (loc > m_decompressedSize)
return false;
return this->SeekStart(m_decompressedSize - loc);
}
GpUFilePos_t InflateStreamImpl::Size() const
{
return m_decompressedSize;
}
GpUFilePos_t InflateStreamImpl::Tell() const
{
return m_decompressedPos;
}
void InflateStreamImpl::Close()
{
this->~InflateStreamImpl();
free(this);
}
void InflateStreamImpl::Flush()
{
}
GpIOStream *InflateStream::Create(GpIOStream *stream, GpUFilePos_t start, size_t compressedSize, size_t decompressedSize)
{
InflateContext *inflateContext = InflateContext::Create();
if (!inflateContext)
return nullptr;
void *storage = malloc(sizeof(InflateStreamImpl));
if (!storage)
{
inflateContext->Destroy();
return nullptr;
}
return new (storage) InflateStreamImpl(stream, start, compressedSize, decompressedSize, inflateContext);
}
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "GpIOStream.h"
namespace PortabilityLayer
{
namespace InflateStream
{
GpIOStream *Create(GpIOStream *stream, GpUFilePos_t start, size_t compressedSize, size_t decompressedSize);
};
}

View File

@@ -24,7 +24,7 @@ namespace
const unsigned int Protected = 81;
const unsigned int DataForkSize = 83;
const unsigned int ResourceForkSize = 87;
const unsigned int CreationDate = 91;
const unsigned int CreatedDate = 91;
const unsigned int ModifiedDate = 95;
const unsigned int CommentLength = 99;
const unsigned int FinderFlagsLow = 101;
@@ -75,8 +75,8 @@ namespace PortabilityLayer
mb2Header[MB2FileOffsets::Protected] = fileInfo.m_properties.m_protected;
BytePack::BigUInt32(mb2Header + MB2FileOffsets::DataForkSize, fileInfo.m_dataForkSize);
BytePack::BigUInt32(mb2Header + MB2FileOffsets::ResourceForkSize, fileInfo.m_resourceForkSize);
BytePack::BigUInt32(mb2Header + MB2FileOffsets::CreationDate, static_cast<uint32_t>(fileInfo.m_properties.m_creationDate));
BytePack::BigUInt32(mb2Header + MB2FileOffsets::ModifiedDate, static_cast<uint32_t>(fileInfo.m_properties.m_modifiedDate));
BytePack::BigUInt32(mb2Header + MB2FileOffsets::CreatedDate, static_cast<uint32_t>(fileInfo.m_properties.m_createdTimeMacEpoch));
BytePack::BigUInt32(mb2Header + MB2FileOffsets::ModifiedDate, static_cast<uint32_t>(fileInfo.m_properties.m_modifiedTimeMacEpoch));
BytePack::BigUInt16(mb2Header + MB2FileOffsets::CommentLength, fileInfo.m_commentSize);
mb2Header[MB2FileOffsets::FinderFlagsLow] = static_cast<uint8_t>(fileInfo.m_properties.m_finderFlags & 0xff);
@@ -130,8 +130,8 @@ namespace PortabilityLayer
fileInfo.m_properties.m_protected = mb2Header[MB2FileOffsets::Protected];
fileInfo.m_dataForkSize = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::DataForkSize);
fileInfo.m_resourceForkSize = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::ResourceForkSize);
fileInfo.m_properties.m_creationDate = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::CreationDate);
fileInfo.m_properties.m_modifiedDate = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::ModifiedDate);
fileInfo.m_properties.m_createdTimeMacEpoch = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::CreatedDate);
fileInfo.m_properties.m_modifiedTimeMacEpoch = ByteUnpack::BigUInt32(mb2Header + MB2FileOffsets::ModifiedDate);
fileInfo.m_commentSize = ByteUnpack::BigUInt16(mb2Header + MB2FileOffsets::CommentLength);
fileInfo.m_properties.m_finderFlags |= mb2Header[MB2FileOffsets::FinderFlagsLow];

View File

@@ -1,63 +1,185 @@
#include "MacFileInfo.h"
#include "PLBigEndian.h"
#include <string.h>
namespace PortabilityLayer
{
static const unsigned int kOffsetFileType = 0;
static const unsigned int kOffsetFileCreator = 4;
static const unsigned int kOffsetXPos = 8;
static const unsigned int kOffsetYPos = 10;
static const unsigned int kOffsetFinderFlags = 12;
static const unsigned int kProtected = 14;
static const unsigned int kCreationDate = 15;
static const unsigned int kModifiedDate = 19;
static const unsigned int kSize = 23;
uint8_t m_data[kSize];
void MacFilePropertiesSerialized::Deserialize(MacFileProperties &props) const
{
memcpy(props.m_fileType, m_data + kOffsetFileType, 4);
memcpy(props.m_fileCreator, m_data + kOffsetFileCreator, 4);
memcpy(&props.m_xPos, m_data + kOffsetXPos, 2);
memcpy(&props.m_yPos, m_data + kOffsetYPos, 2);
memcpy(&props.m_finderFlags, m_data + kOffsetFinderFlags, 2);
memcpy(&props.m_protected, m_data + kProtected, 1);
memcpy(&props.m_creationDate, m_data + kCreationDate, 8);
memcpy(&props.m_modifiedDate, m_data + kModifiedDate, 8);
PortabilityLayer::ByteSwap::BigInt16(props.m_xPos);
PortabilityLayer::ByteSwap::BigInt16(props.m_yPos);
PortabilityLayer::ByteSwap::BigUInt16(props.m_finderFlags);
PortabilityLayer::ByteSwap::BigInt64(props.m_creationDate);
PortabilityLayer::ByteSwap::BigInt64(props.m_modifiedDate);
}
void MacFilePropertiesSerialized::Serialize(const MacFileProperties &props)
{
int16_t xPos = props.m_xPos;
int16_t yPos = props.m_yPos;
uint16_t finderFlags = props.m_finderFlags;
uint64_t creationDate = props.m_creationDate;
uint64_t modifiedDate = props.m_modifiedDate;
PortabilityLayer::ByteSwap::BigInt16(xPos);
PortabilityLayer::ByteSwap::BigInt16(yPos);
PortabilityLayer::ByteSwap::BigUInt16(finderFlags);
PortabilityLayer::ByteSwap::BigUInt64(creationDate);
PortabilityLayer::ByteSwap::BigUInt64(modifiedDate);
memcpy(m_data + kOffsetFileType, props.m_fileType, 4);
memcpy(m_data + kOffsetFileCreator, props.m_fileCreator, 4);
memcpy(m_data + kOffsetXPos, &xPos, 2);
memcpy(m_data + kOffsetYPos, &yPos, 2);
memcpy(m_data + kOffsetFinderFlags, &finderFlags, 2);
memcpy(m_data + kProtected, &props.m_protected, 1);
memcpy(m_data + kCreationDate, &creationDate, 8);
memcpy(m_data + kModifiedDate, &modifiedDate, 8);
}
}
#include "MacFileInfo.h"
#include "DeflateCodec.h"
#include "GpIOStream.h"
#include "ZipFile.h"
#include "PLBigEndian.h"
#include "CombinedTimestamp.h"
#include <string.h>
namespace PortabilityLayer
{
void MacFilePropertiesSerialized::Deserialize(MacFileProperties &props) const
{
memcpy(props.m_fileType, m_data + kOffsetFileType, 4);
memcpy(props.m_fileCreator, m_data + kOffsetFileCreator, 4);
memcpy(&props.m_xPos, m_data + kOffsetXPos, 2);
memcpy(&props.m_yPos, m_data + kOffsetYPos, 2);
memcpy(&props.m_finderFlags, m_data + kOffsetFinderFlags, 2);
memcpy(&props.m_protected, m_data + kOffsetProtected, 1);
memcpy(&props.m_createdTimeMacEpoch, m_data + kOffsetCreatedDate, 8);
memcpy(&props.m_modifiedTimeMacEpoch, m_data + kOffsetModifiedDate, 8);
PortabilityLayer::ByteSwap::BigInt16(props.m_xPos);
PortabilityLayer::ByteSwap::BigInt16(props.m_yPos);
PortabilityLayer::ByteSwap::BigUInt16(props.m_finderFlags);
PortabilityLayer::ByteSwap::BigInt64(props.m_createdTimeMacEpoch);
PortabilityLayer::ByteSwap::BigInt64(props.m_modifiedTimeMacEpoch);
}
void MacFilePropertiesSerialized::Serialize(const MacFileProperties &props)
{
int16_t xPos = props.m_xPos;
int16_t yPos = props.m_yPos;
uint16_t finderFlags = props.m_finderFlags;
uint64_t createdDate = props.m_createdTimeMacEpoch;
uint64_t modifiedDate = props.m_modifiedTimeMacEpoch;
PortabilityLayer::ByteSwap::BigInt16(xPos);
PortabilityLayer::ByteSwap::BigInt16(yPos);
PortabilityLayer::ByteSwap::BigUInt16(finderFlags);
PortabilityLayer::ByteSwap::BigUInt64(createdDate);
PortabilityLayer::ByteSwap::BigUInt64(modifiedDate);
memcpy(m_data + kOffsetFileType, props.m_fileType, 4);
memcpy(m_data + kOffsetFileCreator, props.m_fileCreator, 4);
memcpy(m_data + kOffsetXPos, &xPos, 2);
memcpy(m_data + kOffsetYPos, &yPos, 2);
memcpy(m_data + kOffsetFinderFlags, &finderFlags, 2);
memcpy(m_data + kOffsetProtected, &props.m_protected, 1);
memcpy(m_data + kOffsetCreatedDate, &createdDate, 8);
memcpy(m_data + kOffsetModifiedDate, &modifiedDate, 8);
}
bool MacFilePropertiesSerialized::WriteAsPackage(GpIOStream &stream, const CombinedTimestamp &ts) const
{
if (!WriteIsolated(stream, ts))
return false;
const char *packagedName = GetPackagedName();
uint16_t msdosDate, msdosTime;
ts.GetAsMSDOSTimestamp(msdosDate, msdosTime);
ZipCentralDirectoryFileHeader cdh;
cdh.m_signature = ZipCentralDirectoryFileHeader::kSignature;
cdh.m_versionCreated = ZipConstants::kCompressedRequiredVersion;
cdh.m_versionRequired = ZipConstants::kStoredRequiredVersion;
cdh.m_flags = 0;
cdh.m_method = ZipConstants::kStoredMethod;
cdh.m_modificationTime = msdosTime;
cdh.m_modificationDate = msdosDate;
cdh.m_crc = PortabilityLayer::DeflateContext::CRC32(0, m_data, sizeof(m_data));
cdh.m_compressedSize = sizeof(m_data);
cdh.m_uncompressedSize = sizeof(m_data);
cdh.m_fileNameLength = strlen(packagedName);
cdh.m_extraFieldLength = 0;
cdh.m_commentLength = 0;
cdh.m_diskNumber = 0;
cdh.m_internalAttributes = ZipConstants::kArchivedAttributes;
cdh.m_externalAttributes = PortabilityLayer::ZipConstants::kArchivedAttributes;
cdh.m_localHeaderOffset = 0;
if (!stream.WriteExact(&cdh, sizeof(cdh)))
return false;
if (!stream.WriteExact(packagedName, strlen(packagedName)))
return false;
ZipEndOfCentralDirectoryRecord eod;
eod.m_signature = ZipEndOfCentralDirectoryRecord::kSignature;
eod.m_thisDiskNumber = 0;
eod.m_centralDirDisk = 0;
eod.m_numCentralDirRecordsThisDisk = 1;
eod.m_numCentralDirRecords = 1;
eod.m_centralDirectorySizeBytes = sizeof(ZipCentralDirectoryFileHeader) + strlen(packagedName);
eod.m_centralDirStartOffset = sizeof(ZipFileLocalHeader) + strlen(packagedName) + sizeof(m_data);
eod.m_commentLength = 0;
if (stream.Write(&eod, sizeof(eod)) != sizeof(eod))
return false;
return true;
}
bool MacFilePropertiesSerialized::WriteIsolated(GpIOStream &stream, const CombinedTimestamp &ts) const
{
static const char *packagedName = GetPackagedName();
ZipFileLocalHeader lh;
static const uint32_t kSignature = 0x04034b50;
uint16_t msdosDate, msdosTime;
ts.GetAsMSDOSTimestamp(msdosDate, msdosTime);
lh.m_signature = ZipFileLocalHeader::kSignature;
lh.m_versionRequired = ZipConstants::kStoredRequiredVersion;
lh.m_flags = 0;
lh.m_method = ZipConstants::kStoredMethod;
lh.m_modificationTime = msdosTime;
lh.m_modificationDate = msdosDate;
lh.m_crc = DeflateContext::CRC32(0, m_data, sizeof(m_data));
lh.m_compressedSize = sizeof(m_data);
lh.m_uncompressedSize = sizeof(m_data);
lh.m_fileNameLength = strlen(packagedName);
lh.m_extraFieldLength = 0;
if (stream.Write(&lh, sizeof(lh)) != sizeof(lh))
return false;
if (stream.Write(packagedName, strlen(packagedName)) != strlen(packagedName))
return false;
if (stream.Write(m_data, sizeof(m_data)) != sizeof(m_data))
return false;
return true;
}
bool MacFilePropertiesSerialized::ReadFromPackage(GpIOStream &stream)
{
const char *packagedName = GetPackagedName();
ZipFileLocalHeader lh;
if (stream.Read(&lh, sizeof(lh)) != sizeof(lh))
return false;
if (lh.m_signature != ZipFileLocalHeader::kSignature)
return false;
if (lh.m_versionRequired != ZipConstants::kStoredRequiredVersion)
return false;
if (lh.m_flags != 0)
return false;
if (lh.m_method != ZipConstants::kStoredMethod)
return false;
if (lh.m_compressedSize != sizeof(m_data))
return false;
if (lh.m_uncompressedSize != sizeof(m_data))
return false;
if (lh.m_fileNameLength != strlen(packagedName))
return false;
if (lh.m_extraFieldLength != 0)
return false;
if (!stream.SeekCurrent(lh.m_fileNameLength))
return false;
if (stream.Read(m_data, sizeof(m_data)) != sizeof(m_data))
return false;
if (lh.m_crc != DeflateContext::CRC32(0, m_data, sizeof(m_data)))
return false;
return true;
}
const char *MacFilePropertiesSerialized::GetPackagedName()
{
return "!!meta";
}
}

View File

@@ -1,86 +1,96 @@
#pragma once
#include "DataTypes.h"
#include "PascalStr.h"
namespace PortabilityLayer
{
enum FinderFileFlags
{
FINDER_FILE_FLAG_LOCKED = (1 << 15),
FINDER_FILE_FLAG_INVISIBLE = (1 << 14),
FINDER_FILE_FLAG_BUNDLE = (1 << 13),
FINDER_FILE_FLAG_SYSTEM = (1 << 12),
FINDER_FILE_FLAG_COPY_PROTECTED = (1 << 11),
FINDER_FILE_FLAG_BUSY = (1 << 10),
FINDER_FILE_FLAG_CHANGED = (1 << 9),
FINDER_FILE_FLAG_INITED = (1 << 8),
};
struct MacFileProperties
{
MacFileProperties();
char m_fileType[4];
char m_fileCreator[4];
int16_t m_xPos;
int16_t m_yPos;
uint16_t m_finderFlags;
uint8_t m_protected;
int64_t m_creationDate;
int64_t m_modifiedDate;
};
struct MacFilePropertiesSerialized
{
static const unsigned int kOffsetFileType = 0;
static const unsigned int kOffsetFileCreator = 4;
static const unsigned int kOffsetXPos = 8;
static const unsigned int kOffsetYPos = 10;
static const unsigned int kOffsetFinderFlags = 12;
static const unsigned int kProtected = 14;
static const unsigned int kCreationDate = 15;
static const unsigned int kModifiedDate = 23;
static const unsigned int kSize = 31;
uint8_t m_data[kSize];
void Deserialize(MacFileProperties &props) const;
void Serialize(const MacFileProperties &props);
};
struct MacFileInfo
{
MacFileInfo();
PascalStr<64> m_fileName;
uint16_t m_commentSize;
uint32_t m_dataForkSize;
uint32_t m_resourceForkSize;
MacFileProperties m_properties;
};
}
namespace PortabilityLayer
{
inline MacFileProperties::MacFileProperties()
: m_xPos(0)
, m_yPos(0)
, m_finderFlags(0)
, m_protected(0)
, m_creationDate(0)
, m_modifiedDate(0)
{
m_fileType[0] = m_fileType[1] = m_fileType[2] = m_fileType[3] = '\0';
m_fileCreator[0] = m_fileCreator[1] = m_fileCreator[2] = m_fileCreator[3] = '\0';
}
inline MacFileInfo::MacFileInfo()
: m_dataForkSize(0)
, m_resourceForkSize(0)
, m_commentSize(0)
{
}
}
#pragma once
#include "DataTypes.h"
#include "PascalStr.h"
class GpIOStream;
namespace PortabilityLayer
{
struct CombinedTimestamp;
enum FinderFileFlags
{
FINDER_FILE_FLAG_LOCKED = (1 << 15),
FINDER_FILE_FLAG_INVISIBLE = (1 << 14),
FINDER_FILE_FLAG_BUNDLE = (1 << 13),
FINDER_FILE_FLAG_SYSTEM = (1 << 12),
FINDER_FILE_FLAG_COPY_PROTECTED = (1 << 11),
FINDER_FILE_FLAG_BUSY = (1 << 10),
FINDER_FILE_FLAG_CHANGED = (1 << 9),
FINDER_FILE_FLAG_INITED = (1 << 8),
};
struct MacFileProperties
{
MacFileProperties();
char m_fileType[4];
char m_fileCreator[4];
int16_t m_xPos;
int16_t m_yPos;
uint16_t m_finderFlags;
uint8_t m_protected;
int64_t m_createdTimeMacEpoch;
int64_t m_modifiedTimeMacEpoch;
};
struct MacFilePropertiesSerialized
{
static const unsigned int kOffsetFileType = 0;
static const unsigned int kOffsetFileCreator = 4;
static const unsigned int kOffsetXPos = 8;
static const unsigned int kOffsetYPos = 10;
static const unsigned int kOffsetFinderFlags = 12;
static const unsigned int kOffsetProtected = 14;
static const unsigned int kOffsetCreatedDate = 15;
static const unsigned int kOffsetModifiedDate = 23;
static const unsigned int kSize = 31;
uint8_t m_data[kSize];
void Deserialize(MacFileProperties &props) const;
void Serialize(const MacFileProperties &props);
bool WriteAsPackage(GpIOStream &stream, const CombinedTimestamp &ts) const;
bool WriteIsolated(GpIOStream &stream, const CombinedTimestamp &ts) const;
bool ReadFromPackage(GpIOStream &stream);
static const char *GetPackagedName();
};
struct MacFileInfo
{
MacFileInfo();
PascalStr<64> m_fileName;
uint16_t m_commentSize;
uint32_t m_dataForkSize;
uint32_t m_resourceForkSize;
MacFileProperties m_properties;
};
}
namespace PortabilityLayer
{
inline MacFileProperties::MacFileProperties()
: m_xPos(0)
, m_yPos(0)
, m_finderFlags(0)
, m_protected(0)
, m_createdTimeMacEpoch(0)
, m_modifiedTimeMacEpoch(0)
{
m_fileType[0] = m_fileType[1] = m_fileType[2] = m_fileType[3] = '\0';
m_fileCreator[0] = m_fileCreator[1] = m_fileCreator[2] = m_fileCreator[3] = '\0';
}
inline MacFileInfo::MacFileInfo()
: m_dataForkSize(0)
, m_resourceForkSize(0)
, m_commentSize(0)
{
}
}

View File

@@ -1,125 +1,120 @@
#include "MemReaderStream.h"
#include "MemoryManager.h"
#include <string.h>
namespace PortabilityLayer
{
MemReaderStream::MemReaderStream(const void *memStream, size_t size)
: m_bytes(static_cast<const uint8_t*>(memStream))
, m_size(size)
, m_loc(0)
{
#include "MemoryManager.h"
#include <string.h>
namespace PortabilityLayer
{
MemReaderStream::MemReaderStream(const void *memStream, size_t size)
: m_bytes(static_cast<const uint8_t*>(memStream))
, m_size(size)
, m_loc(0)
{
}
MemReaderStream::~MemReaderStream()
{
}
size_t MemReaderStream::Read(void *bytesOut, size_t size)
{
size_t available = m_size - m_loc;
if (size > available)
size = available;
memcpy(bytesOut, m_bytes + m_loc, size);
m_loc += size;
return size;
}
size_t MemReaderStream::Write(const void *bytes, size_t size)
{
return 0;
}
bool IsSeekable()
{
return true;
}
bool MemReaderStream::IsSeekable() const
{
return true;
}
bool MemReaderStream::IsReadOnly() const
{
return true;
}
bool MemReaderStream::IsWriteOnly() const
{
return false;
}
bool MemReaderStream::SeekStart(GpUFilePos_t loc)
{
if (loc > m_size)
m_loc = m_size;
else
m_loc = static_cast<size_t>(loc);
return true;
}
bool MemReaderStream::SeekCurrent(GpFilePos_t loc)
{
if (loc < 0)
{
if (static_cast<GpFilePos_t>(m_loc) + loc < 0)
m_loc = 0;
else
m_loc = static_cast<size_t>(static_cast<GpFilePos_t>(m_loc) + loc);
}
else
{
const size_t available = m_size - m_loc;
if (static_cast<GpUFilePos_t>(loc) > available)
m_loc = m_size;
else
m_loc = static_cast<size_t>(static_cast<GpFilePos_t>(m_loc) + loc);
}
return true;
}
bool MemReaderStream::SeekEnd(GpUFilePos_t loc)
{
if (m_size < loc)
m_loc = 0;
else
m_loc = m_size - static_cast<size_t>(loc);
return true;
}
bool MemReaderStream::Truncate(GpUFilePos_t loc)
{
return false;
}
GpUFilePos_t MemReaderStream::Size() const
{
return m_size;
}
GpUFilePos_t MemReaderStream::Tell() const
{
return static_cast<GpUFilePos_t>(m_loc);
}
void MemReaderStream::Close()
{
}
void MemReaderStream::Flush()
{
{
}
size_t MemReaderStream::Read(void *bytesOut, size_t size)
{
size_t available = m_size - m_loc;
if (size > available)
size = available;
memcpy(bytesOut, m_bytes + m_loc, size);
m_loc += size;
return size;
}
size_t MemReaderStream::Write(const void *bytes, size_t size)
{
return 0;
}
bool IsSeekable()
{
return true;
}
bool MemReaderStream::IsSeekable() const
{
return true;
}
bool MemReaderStream::IsReadOnly() const
{
return true;
}
bool MemReaderStream::IsWriteOnly() const
{
return false;
}
bool MemReaderStream::SeekStart(GpUFilePos_t loc)
{
if (loc > m_size)
m_loc = m_size;
else
m_loc = static_cast<size_t>(loc);
return true;
}
bool MemReaderStream::SeekCurrent(GpFilePos_t loc)
{
if (loc < 0)
{
if (static_cast<GpFilePos_t>(m_loc) + loc < 0)
m_loc = 0;
else
m_loc = static_cast<size_t>(static_cast<GpFilePos_t>(m_loc) + loc);
}
else
{
const size_t available = m_size - m_loc;
if (static_cast<GpUFilePos_t>(loc) > available)
m_loc = m_size;
else
m_loc = static_cast<size_t>(static_cast<GpFilePos_t>(m_loc) + loc);
}
return true;
}
bool MemReaderStream::SeekEnd(GpUFilePos_t loc)
{
if (m_size < loc)
m_loc = 0;
else
m_loc = m_size - static_cast<size_t>(loc);
return true;
}
GpUFilePos_t MemReaderStream::Size() const
{
return m_size;
}
GpUFilePos_t MemReaderStream::Tell() const
{
return static_cast<GpUFilePos_t>(m_loc);
}
void MemReaderStream::Close()
{
}
void MemReaderStream::Flush()
{
}
MemBufferReaderStream::~MemBufferReaderStream()
{
{
if (m_buffer)
MemoryManager::GetInstance()->Release(m_buffer);
}
@@ -132,7 +127,7 @@ namespace PortabilityLayer
return new (storage) MemBufferReaderStream(buffer, size);
}
void MemBufferReaderStream::Close()
{
this->~MemBufferReaderStream();
@@ -144,4 +139,4 @@ namespace PortabilityLayer
, m_buffer(buffer)
{
}
}
}

View File

@@ -1,55 +1,54 @@
#pragma once
#ifndef __PL_MEM_READER_STREAM_H__
#define __PL_MEM_READER_STREAM_H__
#include "CoreDefs.h"
#include "GpIOStream.h"
namespace PortabilityLayer
{
class MemReaderStream : public GpIOStream
{
public:
#pragma once
#ifndef __PL_MEM_READER_STREAM_H__
#define __PL_MEM_READER_STREAM_H__
#include "CoreDefs.h"
#include "GpIOStream.h"
namespace PortabilityLayer
{
class MemReaderStream : public GpIOStream
{
public:
MemReaderStream(const void *memStream, size_t size);
virtual ~MemReaderStream();
size_t Read(void *bytesOut, size_t size) override;
size_t Write(const void *bytes, size_t size) override;
bool IsSeekable() const override;
bool IsReadOnly() const override;
bool IsWriteOnly() const override;
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
bool Truncate(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;
void Flush() override;
private:
MemReaderStream() GP_DELETED;
const uint8_t *m_bytes;
size_t m_size;
size_t m_loc;
virtual ~MemReaderStream();
size_t Read(void *bytesOut, size_t size) override;
size_t Write(const void *bytes, size_t size) override;
bool IsSeekable() const override;
bool IsReadOnly() const override;
bool IsWriteOnly() const override;
bool SeekStart(GpUFilePos_t loc) override;
bool SeekCurrent(GpFilePos_t loc) override;
bool SeekEnd(GpUFilePos_t loc) override;
GpUFilePos_t Size() const override;
GpUFilePos_t Tell() const override;
void Close() override;
void Flush() override;
private:
MemReaderStream() GP_DELETED;
const uint8_t *m_bytes;
size_t m_size;
size_t m_loc;
};
class MemBufferReaderStream final : public MemReaderStream
{
public:
~MemBufferReaderStream() override;
static MemBufferReaderStream *Create(void *buffer, size_t size);
void Close() override;
private:
MemBufferReaderStream() GP_DELETED;
MemBufferReaderStream(void *buffer, size_t size);
void *m_buffer;
};
}
#endif
void *m_buffer;
};
}
#endif

View File

@@ -109,7 +109,8 @@ namespace PortabilityLayer
uint8_t *bytes = static_cast<uint8_t*>(buf);
const MMBlock *mmBlock = reinterpret_cast<const MMBlock*>(bytes - MMBlock::AlignedSize());
free(bytes - MMBlock::AlignedSize() - mmBlock->m_offsetFromAllocLocation);
void *freeLoc = bytes - MMBlock::AlignedSize() - mmBlock->m_offsetFromAllocLocation;
free(freeLoc);
}
MMHandleBlock *MemoryManagerImpl::AllocHandle(size_t size)

View File

@@ -15,6 +15,7 @@
#include "IGpDirectoryCursor.h"
#include "HostSuspendCallArgument.h"
#include "HostSuspendHook.h"
#include "IGpClipboardContents.h"
#include "IGpCursor.h"
#include "IGpDisplayDriver.h"
#include "IGpFileSystem.h"
@@ -45,6 +46,8 @@
#include "PLTimeTaggedVOSEvent.h"
#include "PLWidgets.h"
#include "UTF8.h"
#include <assert.h>
#include <algorithm>
@@ -78,7 +81,7 @@ static bool ConvertFilenameToSafePStr(const char *str, uint8_t *pstr)
{
const char c = *str++;
if (c == '.' || c == ' ' || c == '_' || c == '\'' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
if (c == '.' || c == ' ' || c == '_' || c == '!' || c == '\'' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
continue;
else
return false;
@@ -422,18 +425,6 @@ VFileSpec MakeVFileSpec(PortabilityLayer::VirtualDirectory_t dir, const PLPasStr
return spec;
}
PLError_t FSpGetFInfo(const VFileSpec &spec, VFileInfo &finfo)
{
PortabilityLayer::MacFileProperties mfp;
if (!PortabilityLayer::FileManager::GetInstance()->ReadFileProperties(spec.m_dir, spec.m_name, mfp))
return PLErrors::kFileNotFound;
finfo.m_type = PortabilityLayer::ResTypeID(mfp.m_fileType);
finfo.m_creator = PortabilityLayer::ResTypeID(mfp.m_fileCreator);
return PLErrors::kNone;
}
DirectoryFileListEntry *GetDirectoryFiles(PortabilityLayer::VirtualDirectory_t dirID)
{
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
@@ -466,10 +457,10 @@ DirectoryFileListEntry *GetDirectoryFiles(PortabilityLayer::VirtualDirectory_t d
PortabilityLayer::MacFileProperties mfp;
PortabilityLayer::MacFilePropertiesSerialized mfs;
const size_t gpfSize = stream->Read(mfs.m_data, PortabilityLayer::MacFilePropertiesSerialized::kSize);
bool deserializedOK = mfs.ReadFromPackage(*stream);
stream->Close();
if (gpfSize != PortabilityLayer::MacFilePropertiesSerialized::kSize)
if (!deserializedOK)
continue;
mfs.Deserialize(mfp);
@@ -688,6 +679,141 @@ WindowPtr PL_GetPutInFrontWindowPtr()
return PortabilityLayer::WindowManager::GetInstance()->GetPutInFrontSentinel();
}
class PLClipboardContentsText : public IGpClipboardContentsText
{
public:
static PLClipboardContentsText *CreateFromMacRomanStr(const uint8_t *chars, size_t size);
GpClipboardContentsType_t GetContentsType() const override;
void Destroy() override;
IGpClipboardContents *Clone() const override;
const uint8_t *GetBytes() const override;
size_t GetSize() const override;
private:
PLClipboardContentsText(uint8_t *utf8Bytes, size_t size);
~PLClipboardContentsText();
uint8_t *m_utf8Bytes;
size_t m_size;
};
PLClipboardContentsText *PLClipboardContentsText::CreateFromMacRomanStr(const uint8_t *chars, size_t length)
{
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
size_t numUTF8Bytes = 0;
for (size_t i = 0; i < length; i++)
{
uint8_t utf8Bytes[PortabilityLayer::UTF8Processor::kMaxEncodedBytes];
uint16_t codePoint = MacRoman::ToUnicode(chars[i]);
size_t numBytesEmitted = 0;
PortabilityLayer::UTF8Processor::EncodeCodePoint(utf8Bytes, numBytesEmitted, codePoint);
numUTF8Bytes += numBytesEmitted;
}
uint8_t *utf8Bytes = nullptr;
if (numUTF8Bytes)
{
utf8Bytes = static_cast<uint8_t*>(mm->Alloc(numUTF8Bytes));
if (!utf8Bytes)
return nullptr;
numUTF8Bytes = 0;
for (size_t i = 0; i < length; i++)
{
uint16_t codePoint = MacRoman::ToUnicode(chars[i]);
size_t numBytesEmitted = 0;
PortabilityLayer::UTF8Processor::EncodeCodePoint(utf8Bytes + numUTF8Bytes, numBytesEmitted, codePoint);
numUTF8Bytes += numBytesEmitted;
}
}
void *storage = mm->Alloc(sizeof(PLClipboardContentsText));
if (!storage)
{
mm->Release(utf8Bytes);
return nullptr;
}
return new (storage) PLClipboardContentsText(utf8Bytes, numUTF8Bytes);
}
PLClipboardContentsText::PLClipboardContentsText(uint8_t *utf8Bytes, size_t size)
: m_utf8Bytes(utf8Bytes)
, m_size(size)
{
}
PLClipboardContentsText::~PLClipboardContentsText()
{
PortabilityLayer::MemoryManager::GetInstance()->Release(m_utf8Bytes);
}
GpClipboardContentsType_t PLClipboardContentsText::GetContentsType() const
{
return GpClipboardContentsTypes::kText;
}
void PLClipboardContentsText::Destroy()
{
this->~PLClipboardContentsText();
PortabilityLayer::MemoryManager::GetInstance()->Release(this);
}
IGpClipboardContents *PLClipboardContentsText::Clone() const
{
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
uint8_t *bytesCopy = nullptr;
if (m_size)
{
bytesCopy = static_cast<uint8_t*>(mm->Alloc(m_size));
if (!bytesCopy)
return nullptr;
memcpy(bytesCopy, m_utf8Bytes, m_size);
}
void *storage = mm->Alloc(sizeof(PLClipboardContentsText));
if (!storage)
{
if (bytesCopy)
mm->Release(bytesCopy);
return nullptr;
}
return new (storage) PLClipboardContentsText(bytesCopy, m_size);
}
const uint8_t *PLClipboardContentsText::GetBytes() const
{
return m_utf8Bytes;
}
size_t PLClipboardContentsText::GetSize() const
{
return m_size;
}
void PL_CopyStringToClipboard(const uint8_t *chars, size_t length)
{
if (IGpClipboardContentsText *clipboardText = PLClipboardContentsText::CreateFromMacRomanStr(chars, length))
{
PLDrivers::GetSystemServices()->SetClipboardContents(clipboardText);
clipboardText->Destroy();
}
}
Window::Window()
: m_surface(PortabilityLayer::QDPortType_Window)
, m_wmX(0)

View File

@@ -243,13 +243,10 @@ void GetIndString(unsigned char *str, int stringsID, int fnameIndex); // Fetches
VFileSpec MakeVFileSpec(PortabilityLayer::VirtualDirectory_t dir, const PLPasStr &fileName);
PLError_t FSpGetFInfo(const VFileSpec &spec, VFileInfo &finfoOut);
DirectoryFileListEntry *GetDirectoryFiles(PortabilityLayer::VirtualDirectory_t dirID);
void DisposeDirectoryFiles(DirectoryFileListEntry *firstDFL);
void GetMouse(Window *window, Point *point);
Boolean Button(); // Returns true if there's a mouse down event in the queue
Boolean StillDown();
Boolean WaitMouseUp();
@@ -279,3 +276,5 @@ void PL_NotYetImplemented();
void PL_NotYetImplemented_Minor();
void PL_NotYetImplemented_TODO(const char *category);
void PL_Init();
void PL_CopyStringToClipboard(const uint8_t *chars, size_t length);

View File

@@ -2,6 +2,7 @@
#include "FontFamily.h"
#include "FontManager.h"
#include "IGpClipboardContents.h"
#include "IGpSystemServices.h"
#include "InputManager.h"
#include "MacRomanConversion.h"
@@ -9,14 +10,16 @@
#include "RenderedFont.h"
#include "GpRenderedFontMetrics.h"
#include "ResolveCachingColor.h"
#include "TextPlacer.h"
#include "Rect2i.h"
#include "TextPlacer.h"
#include "UTF8.h"
#include "PLDrivers.h"
#include "PLKeyEncoding.h"
#include "PLQDraw.h"
#include "PLStandardColors.h"
#include "PLTimeTaggedVOSEvent.h"
#include "PLCore.h"
#include <algorithm>
@@ -36,7 +39,10 @@ namespace PortabilityLayer
, m_caratTimer(0)
, m_isMultiLine(false)
, m_isDraggingSelection(false)
, m_isDraggingWords(false)
, m_dragSelectionStartChar(false)
, m_doubleClickTime(0)
, m_doubleClickPoint(Point::Create(0, 0))
, m_scrollOffset(0, 0)
, m_characterFilter(nullptr)
, m_characterFilterContext(nullptr)
@@ -233,6 +239,8 @@ namespace PortabilityLayer
{
const KeyDownStates *downStates = PortabilityLayer::InputManager::GetInstance()->GetKeys();
const bool isShiftHeld = downStates->m_special.Get(GpKeySpecials::kLeftShift) || downStates->m_special.Get(GpKeySpecials::kRightShift);
const bool isWords = downStates->m_special.Get(GpKeySpecials::kLeftCtrl) || downStates->m_special.Get(GpKeySpecials::kRightCtrl);
const bool isCtrlHeld = downStates->m_special.Get(GpKeySpecials::kLeftCtrl) || downStates->m_special.Get(GpKeySpecials::kRightCtrl);
if (keyEvent.m_keyIDSubset == GpKeyIDSubsets::kSpecial)
{
@@ -248,12 +256,12 @@ namespace PortabilityLayer
}
else if (keyEvent.m_key.m_specialKey == GpKeySpecials::kLeftArrow)
{
HandleLeftArrow(keyEvent.m_repeatCount, isShiftHeld);
HandleLeftArrow(keyEvent.m_repeatCount, isShiftHeld, isWords);
return WidgetHandleStates::kDigested;
}
else if (keyEvent.m_key.m_specialKey == GpKeySpecials::kRightArrow)
{
HandleRightArrow(keyEvent.m_repeatCount, isShiftHeld);
HandleRightArrow(keyEvent.m_repeatCount, isShiftHeld, isWords);
return WidgetHandleStates::kDigested;
}
else if (keyEvent.m_key.m_specialKey == GpKeySpecials::kDownArrow)
@@ -277,6 +285,39 @@ namespace PortabilityLayer
return WidgetHandleStates::kDigested;
}
}
if (isCtrlHeld && keyEvent.m_keyIDSubset == GpKeyIDSubsets::kASCII)
{
if (keyEvent.m_key.m_asciiChar == kCopyShortcutKey)
{
if (m_selStartChar != m_selEndChar)
HandleCopy();
return WidgetHandleStates::kDigested;
}
if (keyEvent.m_key.m_asciiChar == kCutShortcutKey)
{
if (m_selStartChar != m_selEndChar)
HandleCut();
return WidgetHandleStates::kDigested;
}
else if (keyEvent.m_key.m_asciiChar == kPasteShortcutKey)
{
HandlePaste(keyEvent.m_repeatCount);
return WidgetHandleStates::kDigested;
}
else if (keyEvent.m_key.m_asciiChar == kSelectAllShortcutKey)
{
m_selStartChar = 0;
m_selEndChar = m_length;
m_caratSelectionAnchor = CaratSelectionAnchor_Start;
m_caratScrollLocked = false;
AdjustScrollToCarat();
m_caratTimer = 0;
Redraw();
}
}
}
}
else if (evt.m_vosEvent.m_eventType == GpVOSEventTypes::kMouseInput)
@@ -289,8 +330,17 @@ namespace PortabilityLayer
if (m_rect.Contains(pt))
{
const uint32_t doubleTime = 30; // PL_NotYetImplemented_TODO: Get this from the system settings
m_window->FocusWidget(this);
m_isDraggingSelection = true;
m_isDraggingWords = false;
if (evt.m_timestamp >= m_doubleClickTime && evt.m_timestamp - m_doubleClickTime <= doubleTime)
{
if (pt == m_doubleClickPoint)
m_isDraggingWords = true;
}
return HandleDragSelection(evt);
}
else
@@ -493,7 +543,7 @@ namespace PortabilityLayer
{
bool isOutOfRange = false;
m_caratScrollPosition.m_y -= lineGap;
caratChar = FindVerticalMovementCaratPos(m_caratScrollPosition, isOutOfRange);
caratChar = FindVerticalMovementCaratPos(m_caratScrollPosition, isOutOfRange, nullptr);
HandleKeyMoveCarat(caratChar, shiftHeld);
if (isOutOfRange)
@@ -534,7 +584,7 @@ namespace PortabilityLayer
{
bool isOutOfRange = false;
m_caratScrollPosition.m_y += lineGap;
caratChar = FindVerticalMovementCaratPos(m_caratScrollPosition, isOutOfRange);
caratChar = FindVerticalMovementCaratPos(m_caratScrollPosition, isOutOfRange, nullptr);
HandleKeyMoveCarat(caratChar, shiftHeld);
if (isOutOfRange)
@@ -550,7 +600,7 @@ namespace PortabilityLayer
Redraw();
}
void EditboxWidget::HandleLeftArrow(const uint32_t numRepeatsRequested, bool shiftHeld)
void EditboxWidget::HandleLeftArrow(const uint32_t numRepeatsRequested, bool shiftHeld, bool wholeWords)
{
size_t caratChar = ResolveCaratChar();
@@ -559,7 +609,13 @@ namespace PortabilityLayer
if (!shiftHeld && m_selStartChar != m_selEndChar)
m_selEndChar = m_selStartChar;
else if (caratChar > 0)
HandleKeyMoveCarat(caratChar - 1, shiftHeld);
{
size_t spanLength = 1;
if (wholeWords)
spanLength = IdentifySpanLength(caratChar - 1, SpanScanDirection_Left);
HandleKeyMoveCarat(caratChar - spanLength, shiftHeld);
}
}
m_caratScrollLocked = false;
@@ -623,9 +679,93 @@ namespace PortabilityLayer
Redraw();
}
}
void EditboxWidget::HandleCopy()
{
if (m_selStartChar == m_selEndChar)
return;
PL_CopyStringToClipboard(m_chars + m_selStartChar, m_selEndChar - m_selStartChar);
}
void EditboxWidget::HandleRightArrow(const uint32_t numRepeatsRequested, bool shiftHeld)
void EditboxWidget::HandleCut()
{
HandleCopy();
HandleBackspace(1);
}
void EditboxWidget::HandlePaste(size_t repeatCount)
{
IGpClipboardContents *clipboardContents = PLDrivers::GetSystemServices()->GetClipboardContents();
if (clipboardContents == nullptr)
return;
if (clipboardContents->GetContentsType() != GpClipboardContentsTypes::kText)
{
clipboardContents->Destroy();
return;
}
IGpClipboardContentsText *textContents = static_cast<IGpClipboardContentsText*>(clipboardContents);
const size_t utf8Size = textContents->GetSize();
const uint8_t *utf8Bytes = textContents->GetBytes();
size_t numCodePoints = 0;
for (size_t i = 0; i < utf8Size; )
{
uint32_t codePoint = 0;
size_t numDigested = 0;
if (!UTF8Processor::DecodeCodePoint(utf8Bytes + i, utf8Size - i, numDigested, codePoint))
{
clipboardContents->Destroy();
return;
}
i += numDigested;
numCodePoints++;
}
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
uint8_t *decodedChars = static_cast<uint8_t*>(mm->Alloc(numCodePoints));
if (!decodedChars)
{
clipboardContents->Destroy();
return;
}
numCodePoints = 0;
for (size_t i = 0; i < utf8Size; )
{
uint32_t codePoint = 0;
size_t numDigested = 0;
if (!UTF8Processor::DecodeCodePoint(utf8Bytes + i, utf8Size - i, numDigested, codePoint))
{
clipboardContents->Destroy();
return;
}
if (codePoint > 0xffff || !MacRoman::FromUnicode(decodedChars[numCodePoints], static_cast<uint16_t>(codePoint)))
decodedChars[numCodePoints] = '?';
numCodePoints++;
i += numDigested;
}
// This is extremely suboptimal due to the embedded redraw...
for (size_t i = 0; i < repeatCount; i++)
{
for (size_t j = 0; j < numCodePoints; j++)
HandleCharacter(decodedChars[j], 1);
}
mm->Release(decodedChars);
clipboardContents->Destroy();
}
void EditboxWidget::HandleRightArrow(const uint32_t numRepeatsRequested, bool shiftHeld, bool wholeWords)
{
size_t caratChar = ResolveCaratChar();
@@ -634,7 +774,13 @@ namespace PortabilityLayer
if (!shiftHeld && m_selStartChar != m_selEndChar)
m_selStartChar = m_selEndChar;
else if (caratChar < m_length)
HandleKeyMoveCarat(caratChar + 1, shiftHeld);
{
size_t spanLength = 1;
if (wholeWords)
spanLength = IdentifySpanLength(caratChar, SpanScanDirection_Right);
HandleKeyMoveCarat(caratChar + spanLength, shiftHeld);
}
}
m_caratScrollLocked = false;
@@ -645,12 +791,15 @@ namespace PortabilityLayer
Redraw();
}
size_t EditboxWidget::FindVerticalMovementCaratPos(const Vec2i &desiredPos, bool &isOutOfRange) const
size_t EditboxWidget::FindVerticalMovementCaratPos(const Vec2i &desiredPos, bool &isOutOfRange, CaratCharacterAlignment *optOutAlignment) const
{
Vec2i basePoint = Vec2i(0, 0);
if (desiredPos.m_y < basePoint.m_y)
{
if (optOutAlignment)
*optOutAlignment = CaratCharacterAlignment_Start;
isOutOfRange = true;
return 0;
}
@@ -658,6 +807,7 @@ namespace PortabilityLayer
PortabilityLayer::TextPlacer placer(basePoint, m_rect.Width(), GetRenderedFont(), GetString());
bool foundLine = false;
bool foundChar = false;
size_t caratChar = 0;
PortabilityLayer::GlyphPlacementCharacteristics characteristics;
@@ -675,34 +825,96 @@ namespace PortabilityLayer
if (desiredPos.m_x <= 0)
break;
if (characteristics.m_character != '\r')
caratChar++;
if (characteristics.m_character == '\r')
break;
caratChar++;
if (characteristics.m_glyphStartPos.m_x <= desiredPos.m_x && characteristics.m_glyphEndPos.m_x > desiredPos.m_x)
{
int32_t distanceToEnd = characteristics.m_glyphEndPos.m_x - desiredPos.m_x;
int32_t distanceToStart = desiredPos.m_x - characteristics.m_glyphStartPos.m_x;
foundChar = true;
if (distanceToStart <= distanceToEnd)
{
if (optOutAlignment)
*optOutAlignment = CaratCharacterAlignment_BeforeChar;
caratChar = characteristics.m_characterIndex;
}
else
{
if (optOutAlignment)
*optOutAlignment = CaratCharacterAlignment_AfterChar;
caratChar = characteristics.m_characterIndex + 1;
}
break;
}
}
}
if (!foundChar)
{
if (optOutAlignment)
*optOutAlignment = CaratCharacterAlignment_EndOfLine;
}
if (foundLine)
{
isOutOfRange = false;
return caratChar;
}
isOutOfRange = true;
return m_length;
else
{
isOutOfRange = true;
return m_length;
}
}
void EditboxWidget::ExpandSelectionToWords(size_t rootChar, size_t &outStartChar, size_t &outEndChar)
{
assert(rootChar < m_length);
CharacterCategory charCategory = CategorizeCharacter(m_chars[rootChar]);
if (charCategory == CharacterCategory_LineBreak)
{
outStartChar = rootChar;
outEndChar = rootChar;
}
else
{
size_t selStart = rootChar;
size_t selEnd = rootChar + 1;
while (selStart > 0)
{
CharacterCategory candidateCategory = CategorizeCharacter(m_chars[selStart - 1]);
if (candidateCategory != charCategory)
break;
selStart--;
}
while (selEnd < m_length)
{
CharacterCategory candidateCategory = CategorizeCharacter(m_chars[selEnd]);
if (candidateCategory != charCategory)
break;
selEnd++;
}
outStartChar = selStart;
outEndChar = selEnd;
}
}
// Handles adjustment of the selection range and anchor when the carat is moved with shift held
void EditboxWidget::HandleKeyMoveCarat(size_t newPos, bool shiftHeld)
{
@@ -749,13 +961,67 @@ namespace PortabilityLayer
paragraph = -((-relativeY + (linegap - 1)) / linegap);
bool isOutOfRange = false;
const size_t caratPos = FindVerticalMovementCaratPos(Vec2i(relativePoint.m_x, paragraph * linegap), isOutOfRange);
CaratCharacterAlignment cca;
const size_t caratPos = FindVerticalMovementCaratPos(Vec2i(relativePoint.m_x, paragraph * linegap), isOutOfRange, &cca);
if (mouseEvent.m_eventType == GpMouseEventTypes::kDown)
{
m_dragSelectionStartChar = caratPos;
m_selStartChar = caratPos;
m_selEndChar = caratPos;
if (m_isDraggingWords)
{
FindVerticalMovementCaratPos(Vec2i(relativePoint.m_x, paragraph * linegap), isOutOfRange, &cca);
bool hasRootChar = false;
size_t rootChar = 0;
switch (cca)
{
case CaratCharacterAlignment_Start:
if (m_length > 0)
{
hasRootChar = true;
rootChar = caratPos;
}
break;
case CaratCharacterAlignment_AfterChar:
assert(caratPos >= 1);
hasRootChar = true;
rootChar = caratPos - 1;
break;
case CaratCharacterAlignment_BeforeChar:
hasRootChar = true;
rootChar = caratPos;
break;
case CaratCharacterAlignment_EndOfLine:
if (m_length > 0 && (caratPos == 0 || m_chars[caratPos - 1] != '\r'))
{
hasRootChar = true;
rootChar = caratPos - 1;
}
break;
}
if (hasRootChar)
{
size_t startChar, endChar;
ExpandSelectionToWords(rootChar, startChar, endChar);
m_dragSelectionStartChar = startChar;
m_dragSelectionEndChar = endChar;
}
else
{
m_dragSelectionStartChar = caratPos;
m_dragSelectionEndChar = caratPos;
}
}
else
{
m_dragSelectionStartChar = caratPos;
m_dragSelectionEndChar = caratPos;
}
m_selStartChar = m_dragSelectionStartChar;
m_selEndChar = m_dragSelectionEndChar;
m_caratSelectionAnchor = CaratSelectionAnchor_End;
m_caratTimer = 0;
@@ -763,17 +1029,40 @@ namespace PortabilityLayer
}
else
{
if (caratPos < m_dragSelectionStartChar)
size_t selExpandMin = caratPos;
size_t selExpandMax = caratPos;
if (m_isDraggingWords && (caratPos < m_dragSelectionStartChar || caratPos > m_dragSelectionEndChar))
{
size_t rootChar = 0;
if (caratPos < m_dragSelectionStartChar)
rootChar = caratPos;
else
{
assert(caratPos > m_dragSelectionEndChar);
rootChar = caratPos - 1;
}
ExpandSelectionToWords(rootChar, selExpandMin, selExpandMax);
}
if (selExpandMin < m_dragSelectionStartChar)
{
m_caratSelectionAnchor = CaratSelectionAnchor_Start;
m_selStartChar = caratPos;
m_selEndChar = m_dragSelectionStartChar;
m_selStartChar = selExpandMin;
m_selEndChar = m_dragSelectionEndChar;
}
else if (selExpandMax > m_dragSelectionEndChar)
{
m_caratSelectionAnchor = CaratSelectionAnchor_End;
m_selEndChar = selExpandMax;
m_selStartChar = m_dragSelectionStartChar;
}
else
{
m_caratSelectionAnchor = CaratSelectionAnchor_End;
m_selEndChar = caratPos;
m_selStartChar = m_dragSelectionStartChar;
m_selEndChar = m_dragSelectionEndChar;
}
AdjustScrollToCarat();
@@ -788,6 +1077,10 @@ namespace PortabilityLayer
m_caratScrollLocked = false;
m_isDraggingSelection = false;
m_doubleClickTime = evt.m_timestamp;
m_doubleClickPoint = pt;
return WidgetHandleStates::kDigested;
}
}
@@ -986,6 +1279,52 @@ namespace PortabilityLayer
}
}
size_t EditboxWidget::IdentifySpanLength(size_t startChar, SpanScanDirection scanDirection) const
{
assert(startChar < m_length);
CharacterCategory contiguousCategory = CategorizeCharacter(m_chars[startChar]);
size_t spanLength = 1;
for (;;)
{
if (scanDirection == SpanScanDirection_Left && (startChar + 1 - spanLength) == 0)
break;
if (scanDirection == SpanScanDirection_Right && startChar + spanLength == m_length)
break;
size_t nextCharPos = startChar;
if (scanDirection == SpanScanDirection_Left)
nextCharPos -= spanLength;
else if (scanDirection == SpanScanDirection_Right)
nextCharPos += spanLength;
const CharacterCategory thisCategory = CategorizeCharacter(m_chars[nextCharPos]);
if (thisCategory != contiguousCategory)
{
// If span starts with whitespace, it can continue to the next category, otherwise stop
if (contiguousCategory == CharacterCategory_Whitespace)
contiguousCategory = thisCategory;
else
break;
}
spanLength++;
}
return spanLength;
}
EditboxWidget::CharacterCategory EditboxWidget::CategorizeCharacter(uint8_t character)
{
const CharacterCategorySpan *ccs = gs_characterCategorySpans;
while (character > ccs->m_lastCharacterPosInclusive)
ccs++;
return ccs->m_category;
}
FontFamily *EditboxWidget::GetFontFamily() const
{
return PortabilityLayer::FontManager::GetInstance()->GetSystemFont(12, FontFamilyFlag_None);
@@ -1015,4 +1354,42 @@ namespace PortabilityLayer
{
m_capacity = std::min<size_t>(255, capacity);
}
const EditboxWidget::CharacterCategorySpan EditboxWidget::gs_characterCategorySpans[] =
{
{ 0x20, EditboxWidget::CharacterCategory_Whitespace },
{ 0x2f, EditboxWidget::CharacterCategory_Punctuation },
{ 0x39, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0x40, EditboxWidget::CharacterCategory_Punctuation },
{ 0x5a, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0x60, EditboxWidget::CharacterCategory_Punctuation },
{ 0x7a, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0x7e, EditboxWidget::CharacterCategory_Punctuation },
{ 0x7f, EditboxWidget::CharacterCategory_Whitespace },
{ 0x9f, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xa6, EditboxWidget::CharacterCategory_Punctuation },
{ 0xa7, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xad, EditboxWidget::CharacterCategory_Punctuation },
{ 0xaf, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xb4, EditboxWidget::CharacterCategory_Punctuation },
{ 0xb5, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xb8, EditboxWidget::CharacterCategory_Punctuation },
{ 0xb9, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xbc, EditboxWidget::CharacterCategory_Punctuation },
{ 0xbf, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xc3, EditboxWidget::CharacterCategory_Punctuation },
{ 0xc4, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xc9, EditboxWidget::CharacterCategory_Punctuation },
{ 0xca, EditboxWidget::CharacterCategory_Whitespace },
{ 0xcf, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xd7, EditboxWidget::CharacterCategory_Punctuation },
{ 0xd9, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xdd, EditboxWidget::CharacterCategory_Punctuation },
{ 0xdf, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xe4, EditboxWidget::CharacterCategory_Punctuation },
{ 0xef, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xf0, EditboxWidget::CharacterCategory_Punctuation },
{ 0xf5, EditboxWidget::CharacterCategory_AlphaNumeric },
{ 0xff, EditboxWidget::CharacterCategory_Punctuation },
};
}

View File

@@ -42,12 +42,40 @@ namespace PortabilityLayer
static const unsigned int kCaratBlinkRate = 20;
static const unsigned int kMouseScrollRate = 20;
enum SpanScanDirection
{
SpanScanDirection_Left,
SpanScanDirection_Right,
};
enum CaratSelectionAnchor
{
CaratSelectionAnchor_Start,
CaratSelectionAnchor_End
};
enum CharacterCategory
{
CharacterCategory_AlphaNumeric,
CharacterCategory_Whitespace,
CharacterCategory_LineBreak,
CharacterCategory_Punctuation,
};
struct CharacterCategorySpan
{
uint8_t m_lastCharacterPosInclusive;
CharacterCategory m_category;
};
enum CaratCharacterAlignment
{
CaratCharacterAlignment_Start, // Before the start of the text
CaratCharacterAlignment_AfterChar, // Carat is after the character that was clicked
CaratCharacterAlignment_BeforeChar, // Carat is before the character that was clicked
CaratCharacterAlignment_EndOfLine, // Carat is at the end of a line
};
void OnTick() override;
void Redraw();
@@ -57,13 +85,17 @@ namespace PortabilityLayer
void HandleUpArrow(const uint32_t numRepeatsRequested, bool shiftHeld);
void HandleDownArrow(const uint32_t numRepeatsRequested, bool shiftHeld);
void HandleLeftArrow(const uint32_t numRepeatsRequested, bool shiftHeld);
void HandleRightArrow(const uint32_t numRepeatsRequested, bool shiftHeld);
void HandleLeftArrow(const uint32_t numRepeatsRequested, bool shiftHeld, bool wholeWords);
void HandleRightArrow(const uint32_t numRepeatsRequested, bool shiftHeld, bool wholeWords);
void HandleHome(bool shiftHeld);
void HandleEnd(bool shiftHeld);
void HandleCopy();
void HandleCut();
void HandlePaste(size_t repeatCount);
size_t FindVerticalMovementCaratPos(const Vec2i &desiredPos, bool &isOutOfRange) const;
size_t FindVerticalMovementCaratPos(const Vec2i &desiredPos, bool &isOutOfRange, CaratCharacterAlignment *optOutAlignment) const;
void ExpandSelectionToWords(size_t caratPos, size_t &outStartChar, size_t &outEndChar);
void HandleKeyMoveCarat(size_t newPos, bool shiftHeld);
WidgetHandleState_t HandleDragSelection(const TimeTaggedVOSEvent &evt);
@@ -75,6 +107,8 @@ namespace PortabilityLayer
size_t ResolveCaratChar() const;
void AdjustScrollToCarat();
void AdjustScrollToTextBounds();
size_t IdentifySpanLength(size_t startChar, SpanScanDirection scanDirection) const;
static CharacterCategory CategorizeCharacter(uint8_t character);
PortabilityLayer::FontFamily *GetFontFamily() const;
PortabilityLayer::RenderedFont *GetRenderedFont() const;
@@ -94,11 +128,22 @@ namespace PortabilityLayer
bool m_hasFocus;
bool m_isMultiLine;
bool m_isDraggingSelection;
bool m_isDraggingWords;
size_t m_dragSelectionStartChar;
size_t m_dragSelectionEndChar;
uint32_t m_doubleClickTime;
Point m_doubleClickPoint;
uint16_t m_caratTimer;
CharacterFilterCallback_t m_characterFilter;
void *m_characterFilterContext;
static const CharacterCategorySpan gs_characterCategorySpans[];
static const int kCopyShortcutKey = 'C';
static const int kCutShortcutKey = 'X';
static const int kPasteShortcutKey = 'V';
static const int kSelectAllShortcutKey = 'A';
};
}

View File

@@ -15,6 +15,7 @@ namespace PLErrors
kBadFileName,
kFileNotFound,
kAccessDenied,
kFileIsBusy,
kOutOfMemory,
@@ -23,8 +24,6 @@ namespace PLErrors
kIOError,
kResourceError,
kUserCancelled_TEMP,
};
}

View File

@@ -1,6 +1,7 @@
#include "PLMovies.h"
#include "BitmapImage.h"
#include "FileManager.h"
#include "MemoryManager.h"
#include "PLQDraw.h"
#include "PLResources.h"
@@ -56,15 +57,19 @@ void AnimationPackage::Destroy()
PortabilityLayer::MemoryManager::GetInstance()->Release(this);
}
bool AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t virtualDir, const PLPasStr &path)
PLError_t AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &name)
{
m_resArchive = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(virtualDir, path);
m_compositeFile = PortabilityLayer::FileManager::GetInstance()->OpenCompositeFile(dirID, name);
if (!m_compositeFile)
return PLErrors::kFileNotFound;
m_resArchive = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(m_compositeFile);
if (!m_resArchive)
return false;
return PLErrors::kResourceError;
THandle<void> movieMetadataRes = m_resArchive->LoadResource('muvi', 0);
if (!movieMetadataRes)
return false;
return PLErrors::kResourceError;
const void *movieMetadata = *movieMetadataRes;
@@ -74,25 +79,25 @@ bool AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t virtualDir, con
movieMetadataRes.Dispose();
if (document.HasParseError() || !document.IsObject())
return false;
return PLErrors::kResourceError;
if (!document.HasMember("frameRateNumerator") || !document.HasMember("frameRateDenominator"))
return false;
return PLErrors::kResourceError;
const rapidjson::Value &frameRateNumeratorJSON = document["frameRateNumerator"];
const rapidjson::Value &frameRateDenominatorJSON = document["frameRateDenominator"];
if (!frameRateNumeratorJSON.IsInt() && !frameRateDenominatorJSON.IsInt())
return false;
return PLErrors::kResourceError;
const int frameRateNumerator = frameRateNumeratorJSON.GetInt();
const int frameRateDenominator = frameRateDenominatorJSON.GetInt();
if (frameRateNumerator < 1 || frameRateDenominator < 1)
return false;
return PLErrors::kResourceError;
if (frameRateDenominator > INT_MAX / 60 || frameRateDenominator * 60 < frameRateNumerator)
return false; // We only support up to 60fps
return PLErrors::kResourceError; // We only support up to 60fps
m_frameRateNumerator = frameRateNumerator;
m_frameRateDenominator = frameRateDenominator;
@@ -101,7 +106,7 @@ bool AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t virtualDir, con
for (;;)
{
if (numFrames + 1 > 0x7fff)
return false;
return PLErrors::kResourceError;
THandle<void> frameRes = m_resArchive->LoadResource('PICT', numFrames + 1);
if (!frameRes)
@@ -109,18 +114,18 @@ bool AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t virtualDir, con
else
{
if (frameRes.MMBlock()->m_size < sizeof(BitmapImage))
return false;
return PLErrors::kResourceError;
numFrames++;
}
}
if (numFrames == 0)
return false;
return PLErrors::kResourceError;
void *imageListStorage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(THandle<BitmapImage>) * numFrames);
if (!imageListStorage)
return false;
return PLErrors::kResourceError;
m_images = static_cast<THandle<BitmapImage>*>(imageListStorage);
@@ -132,7 +137,7 @@ bool AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t virtualDir, con
m_numImages = numFrames;
return true;
return PLErrors::kNone;
}
const THandle<BitmapImage> &AnimationPackage::GetFrame(size_t index) const
@@ -159,6 +164,7 @@ uint32_t AnimationPackage::GetFrameRateDenominator() const
AnimationPackage::AnimationPackage()
: m_images(nullptr)
, m_resArchive(nullptr)
, m_compositeFile(nullptr)
, m_numImages(0)
{
}
@@ -167,6 +173,10 @@ AnimationPackage::~AnimationPackage()
{
if (m_resArchive)
m_resArchive->Destroy();
if (m_compositeFile)
m_compositeFile->Close();
PortabilityLayer::MemoryManager::GetInstance()->Release(m_images);
}

Some files were not shown because too many files have changed in this diff Show More