mirror of
https://github.com/elasota/Aerofoil.git
synced 2026-03-01 21:34:15 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c147e1100e | ||
|
|
0a2e730d26 | ||
|
|
922cd0fd06 | ||
|
|
b8bf6be44f | ||
|
|
2897c4ffab | ||
|
|
35c174984b | ||
|
|
da16edea8d | ||
|
|
2fe1ea2ee7 | ||
|
|
0931f25f23 | ||
|
|
7f4d782c0d | ||
|
|
f2cda23b0f | ||
|
|
6292705968 | ||
|
|
6931b3f505 | ||
|
|
d70a5b3bfc | ||
|
|
f0913d0d6a | ||
|
|
80584eb781 | ||
|
|
fe4a8a55c6 | ||
|
|
55ec6c516c | ||
|
|
3917e1a370 | ||
|
|
6715bcb030 | ||
|
|
c981a97a20 | ||
|
|
a5562f96e0 | ||
|
|
d97bd8ad1c | ||
|
|
a43f32ab88 | ||
|
|
e94d1511b1 | ||
|
|
95e4eb4e34 | ||
|
|
15bdb97c38 | ||
|
|
e7246c1444 | ||
|
|
35b8e922d7 | ||
|
|
3090f70ee6 | ||
|
|
484e18a9af | ||
|
|
83d37a7c94 | ||
|
|
fc043af3a1 | ||
|
|
2ebd3f2cf3 | ||
|
|
ebab2ee188 |
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
AerofoilAndroid/app/jni/AerofoilPortable
Symbolic link
1
AerofoilAndroid/app/jni/AerofoilPortable
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../AerofoilPortable
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 ..
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
20
AerofoilPortable/Android.mk
Normal file
20
AerofoilPortable/Android.mk
Normal 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)
|
||||
66
AerofoilPortable/GpMutex_Cpp11.h
Normal file
66
AerofoilPortable/GpMutex_Cpp11.h
Normal 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;
|
||||
50
AerofoilPortable/GpSystemServices_POSIX.cpp
Normal file
50
AerofoilPortable/GpSystemServices_POSIX.cpp
Normal 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;
|
||||
}
|
||||
17
AerofoilPortable/GpSystemServices_POSIX.h
Normal file
17
AerofoilPortable/GpSystemServices_POSIX.h
Normal 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;
|
||||
};
|
||||
82
AerofoilPortable/GpThreadEvent_Cpp11.cpp
Normal file
82
AerofoilPortable/GpThreadEvent_Cpp11.cpp
Normal 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);
|
||||
}
|
||||
|
||||
28
AerofoilPortable/GpThreadEvent_Cpp11.h
Normal file
28
AerofoilPortable/GpThreadEvent_Cpp11.h
Normal 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;
|
||||
};
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
309
AerofoilSDL/GpInputDriver_SDL_Gamepad.cpp
Normal file
309
AerofoilSDL/GpInputDriver_SDL_Gamepad.cpp
Normal 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);
|
||||
}
|
||||
14
AerofoilSDL/GpInputDriver_SDL_Gamepad.h
Normal file
14
AerofoilSDL/GpInputDriver_SDL_Gamepad.h
Normal 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();
|
||||
};
|
||||
@@ -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)
|
||||
|
||||
35
AerofoilSDL/ShaderCode/DrawQuad32P.cpp
Normal file
35
AerofoilSDL/ShaderCode/DrawQuad32P.cpp
Normal 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;
|
||||
}
|
||||
@@ -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 ],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -426,6 +426,7 @@ void DoOptionsMenu (short theItem)
|
||||
}
|
||||
|
||||
OpenMainWindow();
|
||||
RedrawSplashScreen();
|
||||
incrementModeTime = TickCount() + kIdleSplashTicks;
|
||||
}
|
||||
else if (theMode == kSplashMode) // switching to edit mode
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
enum EGpInputDriverType
|
||||
{
|
||||
EGpInputDriverType_XInput,
|
||||
EGpInputDriverType_SDL2_Gamepad,
|
||||
|
||||
EGpInputDriverType_Count,
|
||||
};
|
||||
|
||||
@@ -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"
|
||||
|
||||
11
GpCommon/GpClipboardContentsType.h
Normal file
11
GpCommon/GpClipboardContentsType.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace GpClipboardContentsTypes
|
||||
{
|
||||
enum GpClipboardContentsType
|
||||
{
|
||||
kText,
|
||||
};
|
||||
}
|
||||
|
||||
typedef GpClipboardContentsTypes::GpClipboardContentsType GpClipboardContentsType_t;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
16
GpCommon/IGpClipboardContents.h
Normal file
16
GpCommon/IGpClipboardContents.h
Normal 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
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
388
MergeGPF/MergeGPF.cpp
Normal 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
92
MergeGPF/MergeGPF.vcxproj
Normal 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>
|
||||
22
MergeGPF/MergeGPF.vcxproj.filters
Normal file
22
MergeGPF/MergeGPF.vcxproj.filters
Normal 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>
|
||||
@@ -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;
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
171
PortabilityLayer/FileSectionStream.cpp
Normal file
171
PortabilityLayer/FileSectionStream.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
11
PortabilityLayer/FileSectionStream.h
Normal file
11
PortabilityLayer/FileSectionStream.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "GpIOStream.h"
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
namespace FileSectionStream
|
||||
{
|
||||
GpIOStream *Create(GpIOStream *stream, GpUFilePos_t start, GpUFilePos_t size);
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
250
PortabilityLayer/InflateStream.cpp
Normal file
250
PortabilityLayer/InflateStream.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
11
PortabilityLayer/InflateStream.h
Normal file
11
PortabilityLayer/InflateStream.h
Normal 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);
|
||||
};
|
||||
}
|
||||
@@ -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];
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace PLErrors
|
||||
kBadFileName,
|
||||
kFileNotFound,
|
||||
kAccessDenied,
|
||||
kFileIsBusy,
|
||||
|
||||
kOutOfMemory,
|
||||
|
||||
@@ -23,8 +24,6 @@ namespace PLErrors
|
||||
kIOError,
|
||||
|
||||
kResourceError,
|
||||
|
||||
kUserCancelled_TEMP,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user