From fb7b9b02d909c31c810159c62281aecefb918c4e Mon Sep 17 00:00:00 2001 From: elasota Date: Fri, 7 May 2021 23:09:35 -0400 Subject: [PATCH] Web save mechanism --- .gitmodules | 4 + AerofoilWeb/FileSaverDotJS | 1 + AerofoilWeb/GpFileSystem_Web.cpp | 1673 +++++++++++++++--------------- AerofoilWeb/GpFileSystem_Web.h | 3 +- AerofoilWeb/GpMain_SDL_Web.cpp | 215 ++-- AerofoilWeb/Link.bat | 2 + AerofoilWeb/shell_minimal.html | 3 +- 7 files changed, 977 insertions(+), 924 deletions(-) create mode 100644 .gitmodules create mode 160000 AerofoilWeb/FileSaverDotJS diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..75d6a2d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "AerofoilWeb/FileSaverDotJS"] + path = AerofoilWeb/FileSaverDotJS + url = https://github.com/eligrey/FileSaver.js.git + branch = master diff --git a/AerofoilWeb/FileSaverDotJS b/AerofoilWeb/FileSaverDotJS new file mode 160000 index 0000000..b5e61ec --- /dev/null +++ b/AerofoilWeb/FileSaverDotJS @@ -0,0 +1 @@ +Subproject commit b5e61ec88969461ce0504658af07c2b56650ee8c diff --git a/AerofoilWeb/GpFileSystem_Web.cpp b/AerofoilWeb/GpFileSystem_Web.cpp index b823a12..ef760f2 100644 --- a/AerofoilWeb/GpFileSystem_Web.cpp +++ b/AerofoilWeb/GpFileSystem_Web.cpp @@ -1,817 +1,862 @@ -#define _LARGEFILE64_SOURCE -#include "GpFileSystem_Web.h" -#include "GpIOStream.h" -#include "IGpDirectoryCursor.h" -#include "IGpSystemServices.h" -#include "IGpMutex.h" -#include "VirtualDirectory.h" - -#include "PLDrivers.h" - -#include "SDL2/SDL.h" -#include "SDL2/SDL_rwops.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "UTF8.h" - -#if defined(__CYGWIN__) || defined(__EMSCRIPTEN__) -typedef off_t off64_t; -#define fstat64 fstat -#define fseek64 fseek -#define ftruncate64 ftruncate -#define stat64 stat -#endif - -EM_JS(void, FlushFileSystem, (), { - Asyncify.handleSleep(wakeUp => { - FS.syncfs(false, function (err) { - assert(!err); - wakeUp(); - }); - }); -}); - - -class GpDirectoryCursor_Web final : public IGpDirectoryCursor -{ -public: - explicit GpDirectoryCursor_Web(DIR *dir, const std::string &prefix); - ~GpDirectoryCursor_Web(); - - bool GetNext(const char *&outFileName) override; - void Destroy() override; - -private: - DIR *m_dir; - std::string m_prefix; - std::string m_decodedFileName; -}; - -GpDirectoryCursor_Web::GpDirectoryCursor_Web(DIR *dir, const std::string &prefix) - : m_dir(dir) - , m_prefix(prefix) -{ -} - -GpDirectoryCursor_Web::~GpDirectoryCursor_Web() -{ - closedir(m_dir); -} - -bool GpDirectoryCursor_Web::GetNext(const char *&outFileName) -{ - const size_t prefixLength = m_prefix.size(); - - for (;;) - { - struct dirent *dir = readdir(m_dir); - if (!dir) - return false; - - const char *fname = dir->d_name; - - const size_t fnameLen = strlen(fname); - if (fnameLen > prefixLength && (prefixLength == 0 || !memcmp(&m_prefix[0], fname, prefixLength))) - { - const char *encodedResult = fname + prefixLength; - - m_decodedFileName.clear(); - for (size_t i = 0; encodedResult[i] != 0; i++) - { - char c = encodedResult[i]; - if (c == '%') - { - char highNibble = encodedResult[i + 1]; - if ((highNibble >= '0' && highNibble <= '9') || (highNibble >= 'a' && highNibble <= 'f') || (highNibble >= 'A' && highNibble <= 'F')) - { - char lowNibble = encodedResult[i + 2]; - - if ((lowNibble >= '0' && lowNibble <= '9') || (lowNibble >= 'a' && lowNibble <= 'f') || (lowNibble >= 'A' && lowNibble <= 'F')) - { - bool failedNibble = false; - char nibbles[2] = { highNibble, lowNibble }; - int decNibbles[2]; - for (int ni = 0; ni < 2; ni++) - { - char nc = nibbles[ni]; - if (nc >= '0' && nc <= '9') - decNibbles[ni] = nc - '0'; - else if (nc >= 'a' && nc <= 'f') - decNibbles[ni] = 0xa + (nc - 'a'); - else if (nc >= 'A' && nc <= 'F') - decNibbles[ni] = 0xa + (nc - 'A'); - else - failedNibble = true; - } - - if (!failedNibble) - { - c = static_cast((decNibbles[0] << 4) + decNibbles[1]); - i += 2; - } - } - } - } - - m_decodedFileName += c; - } - - outFileName = m_decodedFileName.c_str(); - return true; - } - } - return true; -} - -void GpDirectoryCursor_Web::Destroy() -{ - delete this; -} - -class GpFileStream_Web_StaticMemFile final : public GpIOStream -{ -public: - GpFileStream_Web_StaticMemFile(const unsigned char *bytes, size_t size); - ~GpFileStream_Web_StaticMemFile(); - - 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 GP_ASYNCIFY_PARANOID_NAMED(Close)() override; - void Flush() override; - -private: - const unsigned char *m_bytes; - size_t m_offset; - size_t m_size; -}; - -GpFileStream_Web_StaticMemFile::GpFileStream_Web_StaticMemFile(const unsigned char *bytes, size_t size) - : m_bytes(bytes) - , m_size(size) - , m_offset(0) -{ -} - -GpFileStream_Web_StaticMemFile::~GpFileStream_Web_StaticMemFile() -{ -} - -size_t GpFileStream_Web_StaticMemFile::Read(void *bytesOut, size_t size) -{ - size_t available = m_size - m_offset; - size = std::min(size, available); - - memcpy(bytesOut, m_bytes + m_offset, size); - m_offset += size; - return size; -} - -size_t GpFileStream_Web_StaticMemFile::Write(const void *bytes, size_t size) -{ - return 0; -} - -bool GpFileStream_Web_StaticMemFile::IsSeekable() const -{ - return true; -} - -bool GpFileStream_Web_StaticMemFile::IsReadOnly() const -{ - return true; -} - -bool GpFileStream_Web_StaticMemFile::IsWriteOnly() const -{ - return false; -} - -bool GpFileStream_Web_StaticMemFile::SeekStart(GpUFilePos_t loc) -{ - if (loc > m_size) - return false; - - m_offset = static_cast(loc); - return true; -} - -bool GpFileStream_Web_StaticMemFile::SeekCurrent(GpFilePos_t loc) -{ - GpFilePos_t minOffset = -static_cast(m_offset); - GpFilePos_t maxOffset = static_cast(m_size - m_offset); - - if (loc < minOffset || loc > maxOffset) - return false; - - m_offset = static_cast(static_cast(m_offset) + loc); - return true; -} - -bool GpFileStream_Web_StaticMemFile::SeekEnd(GpUFilePos_t loc) -{ - if (loc > m_size) - return false; - - m_offset = m_size - loc; - return true; -} - -GpUFilePos_t GpFileStream_Web_StaticMemFile::Size() const -{ - return m_size; -} - -GpUFilePos_t GpFileStream_Web_StaticMemFile::Tell() const -{ - return m_offset; -} - -void GpFileStream_Web_StaticMemFile::GP_ASYNCIFY_PARANOID_NAMED(Close)() -{ - delete this; -} - -void GpFileStream_Web_StaticMemFile::Flush() -{ -} - - -class GpFileStream_Web_File final : public GpIOStream -{ -public: - GpFileStream_Web_File(FILE *f, bool readOnly, bool writeOnly, bool synchronizeOnClose); - ~GpFileStream_Web_File(); - - 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 GP_ASYNCIFY_PARANOID_NAMED(Close)() override; - void Flush() override; - -private: - FILE *m_f; - bool m_seekable; - bool m_isReadOnly; - bool m_isWriteOnly; - bool m_synchronizeOnClose; -}; - - -GpFileStream_Web_File::GpFileStream_Web_File(FILE *f, bool readOnly, bool writeOnly, bool synchronizeOnClose) - : m_f(f) - , m_isReadOnly(readOnly) - , m_isWriteOnly(writeOnly) - , m_synchronizeOnClose(synchronizeOnClose) -{ - m_seekable = (fseek(m_f, 0, SEEK_CUR) == 0); -} - -GpFileStream_Web_File::~GpFileStream_Web_File() -{ - fclose(m_f); - - if (m_synchronizeOnClose) - GpFileSystem_Web::MarkFSStateDirty(); -} - -size_t GpFileStream_Web_File::Read(void *bytesOut, size_t size) -{ - if (m_isWriteOnly) - return 0; - return fread(bytesOut, 1, size, m_f); -} - -size_t GpFileStream_Web_File::Write(const void *bytes, size_t size) -{ - if (m_isReadOnly) - return 0; - return fwrite(bytes, 1, size, m_f); -} - -bool GpFileStream_Web_File::IsSeekable() const -{ - return m_seekable; -} - -bool GpFileStream_Web_File::IsReadOnly() const -{ - return m_isReadOnly; -} - -bool GpFileStream_Web_File::IsWriteOnly() const -{ - return m_isWriteOnly; -} - -bool GpFileStream_Web_File::SeekStart(GpUFilePos_t loc) -{ - if (!m_seekable) - return false; - - fflush(m_f); - return fseek64(m_f, static_cast(loc), SEEK_SET) >= 0; -} - -bool GpFileStream_Web_File::SeekCurrent(GpFilePos_t loc) -{ - if (!m_seekable) - return false; - - fflush(m_f); - return fseek64(m_f, static_cast(loc), SEEK_CUR) >= 0; -} - -bool GpFileStream_Web_File::SeekEnd(GpUFilePos_t loc) -{ - if (!m_seekable) - return false; - - fflush(m_f); - return fseek64(m_f, -static_cast(loc), SEEK_END) >= 0; -} - -GpUFilePos_t GpFileStream_Web_File::Size() const -{ - fflush(m_f); - - struct stat64 s; - if (fstat64(fileno(m_f), &s) < 0) - return 0; - - return static_cast(s.st_size); -} - -GpUFilePos_t GpFileStream_Web_File::Tell() const -{ - return static_cast(ftell(m_f)); -} - -void GpFileStream_Web_File::GP_ASYNCIFY_PARANOID_NAMED(Close)() -{ - this->~GpFileStream_Web_File(); - free(this); -} - -void GpFileStream_Web_File::Flush() -{ - fflush(m_f); -} - - -bool GpFileSystem_Web::ms_fsStateDirty; - - -bool GpFileSystem_Web::ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, bool trailingSlash, std::string &resolution) -{ +#define _LARGEFILE64_SOURCE +#include "GpFileSystem_Web.h" +#include "GpIOStream.h" +#include "IGpDirectoryCursor.h" +#include "IGpSystemServices.h" +#include "IGpMutex.h" +#include "VirtualDirectory.h" + +#include "PLDrivers.h" + +#include "SDL2/SDL.h" +#include "SDL2/SDL_rwops.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "UTF8.h" + +#if defined(__CYGWIN__) || defined(__EMSCRIPTEN__) +typedef off_t off64_t; +#define fstat64 fstat +#define fseek64 fseek +#define ftruncate64 ftruncate +#define stat64 stat +#endif + +EM_JS(void, FlushFileSystem, (), { + Asyncify.handleSleep(wakeUp => { + FS.syncfs(false, function (err) { + assert(!err); + wakeUp(); + }); + }); +}); + +EM_JS(void, DownloadAndDeleteFile, (const char *fileNamePtr, const char *prettyNamePtr), { + var fileName = UTF8ToString(fileNamePtr); + var prettyName = UTF8ToString(prettyNamePtr); + console.log("Flush download of file " + fileName + " as " + prettyName); + + var mimeType = "application/octet-stream"; + if (prettyName.endsWith(".bin")) + mimeType = "application/macbinary"; + else if (prettyName.endsWith(".gpf")) + mimeType = "application/zip"; + + var byteArray = FS.readFile(fileName, { encoding: "binary" }); + var blob = new Blob([byteArray], { type: mimeType }); + saveAs(blob, prettyName); +}); + + +class GpDirectoryCursor_Web final : public IGpDirectoryCursor +{ +public: + explicit GpDirectoryCursor_Web(DIR *dir, const std::string &prefix); + ~GpDirectoryCursor_Web(); + + bool GetNext(const char *&outFileName) override; + void Destroy() override; + +private: + DIR *m_dir; + std::string m_prefix; + std::string m_decodedFileName; +}; + +GpDirectoryCursor_Web::GpDirectoryCursor_Web(DIR *dir, const std::string &prefix) + : m_dir(dir) + , m_prefix(prefix) +{ +} + +GpDirectoryCursor_Web::~GpDirectoryCursor_Web() +{ + closedir(m_dir); +} + +bool GpDirectoryCursor_Web::GetNext(const char *&outFileName) +{ + const size_t prefixLength = m_prefix.size(); + + for (;;) + { + struct dirent *dir = readdir(m_dir); + if (!dir) + return false; + + const char *fname = dir->d_name; + + const size_t fnameLen = strlen(fname); + if (fnameLen > prefixLength && (prefixLength == 0 || !memcmp(&m_prefix[0], fname, prefixLength))) + { + const char *encodedResult = fname + prefixLength; + + m_decodedFileName.clear(); + for (size_t i = 0; encodedResult[i] != 0; i++) + { + char c = encodedResult[i]; + if (c == '%') + { + char highNibble = encodedResult[i + 1]; + if ((highNibble >= '0' && highNibble <= '9') || (highNibble >= 'a' && highNibble <= 'f') || (highNibble >= 'A' && highNibble <= 'F')) + { + char lowNibble = encodedResult[i + 2]; + + if ((lowNibble >= '0' && lowNibble <= '9') || (lowNibble >= 'a' && lowNibble <= 'f') || (lowNibble >= 'A' && lowNibble <= 'F')) + { + bool failedNibble = false; + char nibbles[2] = { highNibble, lowNibble }; + int decNibbles[2]; + for (int ni = 0; ni < 2; ni++) + { + char nc = nibbles[ni]; + if (nc >= '0' && nc <= '9') + decNibbles[ni] = nc - '0'; + else if (nc >= 'a' && nc <= 'f') + decNibbles[ni] = 0xa + (nc - 'a'); + else if (nc >= 'A' && nc <= 'F') + decNibbles[ni] = 0xa + (nc - 'A'); + else + failedNibble = true; + } + + if (!failedNibble) + { + c = static_cast((decNibbles[0] << 4) + decNibbles[1]); + i += 2; + } + } + } + } + + m_decodedFileName += c; + } + + outFileName = m_decodedFileName.c_str(); + return true; + } + } + return true; +} + +void GpDirectoryCursor_Web::Destroy() +{ + delete this; +} + +class GpFileStream_Web_StaticMemFile final : public GpIOStream +{ +public: + GpFileStream_Web_StaticMemFile(const unsigned char *bytes, size_t size); + ~GpFileStream_Web_StaticMemFile(); + + 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 GP_ASYNCIFY_PARANOID_NAMED(Close)() override; + void Flush() override; + +private: + const unsigned char *m_bytes; + size_t m_offset; + size_t m_size; +}; + +GpFileStream_Web_StaticMemFile::GpFileStream_Web_StaticMemFile(const unsigned char *bytes, size_t size) + : m_bytes(bytes) + , m_size(size) + , m_offset(0) +{ +} + +GpFileStream_Web_StaticMemFile::~GpFileStream_Web_StaticMemFile() +{ +} + +size_t GpFileStream_Web_StaticMemFile::Read(void *bytesOut, size_t size) +{ + size_t available = m_size - m_offset; + size = std::min(size, available); + + memcpy(bytesOut, m_bytes + m_offset, size); + m_offset += size; + return size; +} + +size_t GpFileStream_Web_StaticMemFile::Write(const void *bytes, size_t size) +{ + return 0; +} + +bool GpFileStream_Web_StaticMemFile::IsSeekable() const +{ + return true; +} + +bool GpFileStream_Web_StaticMemFile::IsReadOnly() const +{ + return true; +} + +bool GpFileStream_Web_StaticMemFile::IsWriteOnly() const +{ + return false; +} + +bool GpFileStream_Web_StaticMemFile::SeekStart(GpUFilePos_t loc) +{ + if (loc > m_size) + return false; + + m_offset = static_cast(loc); + return true; +} + +bool GpFileStream_Web_StaticMemFile::SeekCurrent(GpFilePos_t loc) +{ + GpFilePos_t minOffset = -static_cast(m_offset); + GpFilePos_t maxOffset = static_cast(m_size - m_offset); + + if (loc < minOffset || loc > maxOffset) + return false; + + m_offset = static_cast(static_cast(m_offset) + loc); + return true; +} + +bool GpFileStream_Web_StaticMemFile::SeekEnd(GpUFilePos_t loc) +{ + if (loc > m_size) + return false; + + m_offset = m_size - loc; + return true; +} + +GpUFilePos_t GpFileStream_Web_StaticMemFile::Size() const +{ + return m_size; +} + +GpUFilePos_t GpFileStream_Web_StaticMemFile::Tell() const +{ + return m_offset; +} + +void GpFileStream_Web_StaticMemFile::GP_ASYNCIFY_PARANOID_NAMED(Close)() +{ + delete this; +} + +void GpFileStream_Web_StaticMemFile::Flush() +{ +} + + +class GpFileStream_Web_File final : public GpIOStream +{ +public: + GpFileStream_Web_File(FILE *f, const std::string &filePath, const std::string &prettyName, bool readOnly, bool writeOnly, bool synchronizeOnClose, bool isIDB); + ~GpFileStream_Web_File(); + + 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 GP_ASYNCIFY_PARANOID_NAMED(Close)() override; + void Flush() override; + +private: + FILE *m_f; + std::string m_filePath; + std::string m_prettyName; + bool m_seekable; + bool m_isReadOnly; + bool m_isWriteOnly; + bool m_synchronizeOnClose; + bool m_isIDB; +}; + + +GpFileStream_Web_File::GpFileStream_Web_File(FILE *f, const std::string &filePath, const std::string &prettyName, bool readOnly, bool writeOnly, bool synchronizeOnClose, bool isIDB) + : m_f(f) + , m_isReadOnly(readOnly) + , m_isWriteOnly(writeOnly) + , m_synchronizeOnClose(synchronizeOnClose) + , m_isIDB(isIDB) + , m_filePath(filePath) + , m_prettyName(prettyName) +{ + m_seekable = (fseek(m_f, 0, SEEK_CUR) == 0); +} + +GpFileStream_Web_File::~GpFileStream_Web_File() +{ + fclose(m_f); + + if (m_synchronizeOnClose) + { + if (m_isIDB) + GpFileSystem_Web::MarkFSStateDirty(); + else + GpFileSystem_Web::SyncDownloadFile(m_filePath, m_prettyName); + } +} + +size_t GpFileStream_Web_File::Read(void *bytesOut, size_t size) +{ + if (m_isWriteOnly) + return 0; + return fread(bytesOut, 1, size, m_f); +} + +size_t GpFileStream_Web_File::Write(const void *bytes, size_t size) +{ + if (m_isReadOnly) + return 0; + return fwrite(bytes, 1, size, m_f); +} + +bool GpFileStream_Web_File::IsSeekable() const +{ + return m_seekable; +} + +bool GpFileStream_Web_File::IsReadOnly() const +{ + return m_isReadOnly; +} + +bool GpFileStream_Web_File::IsWriteOnly() const +{ + return m_isWriteOnly; +} + +bool GpFileStream_Web_File::SeekStart(GpUFilePos_t loc) +{ + if (!m_seekable) + return false; + + fflush(m_f); + return fseek64(m_f, static_cast(loc), SEEK_SET) >= 0; +} + +bool GpFileStream_Web_File::SeekCurrent(GpFilePos_t loc) +{ + if (!m_seekable) + return false; + + fflush(m_f); + return fseek64(m_f, static_cast(loc), SEEK_CUR) >= 0; +} + +bool GpFileStream_Web_File::SeekEnd(GpUFilePos_t loc) +{ + if (!m_seekable) + return false; + + fflush(m_f); + return fseek64(m_f, -static_cast(loc), SEEK_END) >= 0; +} + +GpUFilePos_t GpFileStream_Web_File::Size() const +{ + fflush(m_f); + + struct stat64 s; + if (fstat64(fileno(m_f), &s) < 0) + return 0; + + return static_cast(s.st_size); +} + +GpUFilePos_t GpFileStream_Web_File::Tell() const +{ + return static_cast(ftell(m_f)); +} + +void GpFileStream_Web_File::GP_ASYNCIFY_PARANOID_NAMED(Close)() +{ + this->~GpFileStream_Web_File(); + free(this); +} + +void GpFileStream_Web_File::Flush() +{ + fflush(m_f); +} + + +bool GpFileSystem_Web::ms_fsStateDirty; + + +bool GpFileSystem_Web::ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, bool trailingSlash, std::string &resolution, bool &outIsIDB) +{ const char *pathAppend = nullptr; - const std::string *rootPath = nullptr; - std::string unsanitized; - - switch (virtualDirectory) - { - case PortabilityLayer::VirtualDirectories::kApplicationData: - unsanitized = std::string("Packaged"); - break; - case PortabilityLayer::VirtualDirectories::kGameData: - unsanitized = std::string("Packaged/Houses"); - break; - case PortabilityLayer::VirtualDirectories::kFonts: - unsanitized = std::string("Resources"); - break; - case PortabilityLayer::VirtualDirectories::kHighScores: + const std::string *rootPath = nullptr; + std::string unsanitized; + bool isIDB = false; + + switch (virtualDirectory) + { + case PortabilityLayer::VirtualDirectories::kApplicationData: + unsanitized = std::string("Packaged"); + break; + case PortabilityLayer::VirtualDirectories::kGameData: + unsanitized = std::string("Packaged/Houses"); + break; + case PortabilityLayer::VirtualDirectories::kFonts: + unsanitized = std::string("Resources"); + break; + case PortabilityLayer::VirtualDirectories::kHighScores: pathAppend = "HighScores"; - rootPath = &m_basePath; - break; - case PortabilityLayer::VirtualDirectories::kUserData: - pathAppend = "Houses"; - rootPath = &m_basePath; - break; - case PortabilityLayer::VirtualDirectories::kUserSaves: - pathAppend = "SavedGames"; - rootPath = &m_basePath; - break; - case PortabilityLayer::VirtualDirectories::kPrefs: - pathAppend = "Prefs"; - rootPath = &m_basePath; - break; - case PortabilityLayer::VirtualDirectories::kSourceExport: + rootPath = &m_basePath; + isIDB = true; + break; + case PortabilityLayer::VirtualDirectories::kUserData: + pathAppend = "Houses"; + rootPath = &m_basePath; + isIDB = true; + break; + case PortabilityLayer::VirtualDirectories::kUserSaves: + pathAppend = "SavedGames"; + rootPath = &m_basePath; + isIDB = true; + break; + case PortabilityLayer::VirtualDirectories::kPrefs: + pathAppend = "Prefs"; + rootPath = &m_basePath; + isIDB = true; + break; + case PortabilityLayer::VirtualDirectories::kSourceExport: pathAppend = "Export"; - rootPath = &m_exportPath; - break; - default: - return false; - }; - - if (pathAppend) - { - unsanitized = pathAppend; - - for (size_t i = 0; i < numPaths; i++) - { - unsanitized += "/"; - unsanitized += paths[i]; - } - - if (trailingSlash) - unsanitized += "/"; - - std::string sanitized; - for (size_t i = 0; i < unsanitized.size(); i++) - { - char c = unsanitized[i]; - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') - sanitized += c; - else - { - const char *nibbles = "0123456789abcdef"; - char subPath[4]; - subPath[0] = '%'; - subPath[1] = nibbles[(c >> 4) & 0xf]; - subPath[2] = nibbles[c & 0xf]; - subPath[3] = 0; - - sanitized += subPath; - } - } - - resolution = (*rootPath) + "/" + sanitized; - } - else - { - std::string sanitized = m_basePath + unsanitized; - - for (size_t i = 0; i < numPaths; i++) - { - sanitized += "/"; - sanitized += paths[i]; - } - - resolution = sanitized; - } - - return true; -} - -GpFileSystem_Web::GpFileSystem_Web() - : m_delayCallback(nullptr) -{ -} - -GpFileSystem_Web::~GpFileSystem_Web() -{ -} - -void GpFileSystem_Web::Init() -{ - m_prefsPath = "/aerofoil"; - m_exportPath = "/aerofoil_memfs"; - - char *baseDir = SDL_GetBasePath(); - m_basePath = baseDir; - SDL_free(baseDir); - - char baseDirSeparator = m_basePath[m_basePath.size() - 1]; - if (m_basePath.size() >= 4 && m_basePath.substr(m_basePath.size() - 4, 3) == "bin") - m_basePath = m_basePath.substr(0, m_basePath.size() - 4) + "lib" + baseDirSeparator + "aerofoil" + baseDirSeparator; -} - -bool GpFileSystem_Web::FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) -{ - if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory)) - { - for (size_t i = 0; i < catalog->m_numEntries; i++) - { - const GpFileSystem_Web_Resources::FileCatalogEntry &entry = catalog->m_entries[i]; - if (!strcmp(path, entry.m_fileName)) - return true; - } - - return false; - } - - std::string resolvedPath; - if (!ResolvePath(virtualDirectory, &path, 1, false, resolvedPath)) - return false; - - struct stat s; - return stat(resolvedPath.c_str(), &s) == 0; -} - -bool GpFileSystem_Web::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists) -{ - if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory)) - { - for (size_t i = 0; i < catalog->m_numEntries; i++) - { - const GpFileSystem_Web_Resources::FileCatalogEntry &entry = catalog->m_entries[i]; - if (!strcmp(path, entry.m_fileName)) - { - exists = true; - return true; - } - } - - exists = false; - return false; - } - - std::string resolvedPath; - if (!ResolvePath(virtualDirectory, &path, 1, false, resolvedPath)) - { - if (exists) - exists = false; - return false; - } - - int permissions = access(resolvedPath.c_str(), W_OK | F_OK); - exists = ((permissions & F_OK) != 0); - return ((permissions & W_OK) != 0); -} - -GpIOStream *GpFileSystem_Web::OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) -{ - if (numSubPaths == 1) - { - if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory)) - { - for (size_t i = 0; i < catalog->m_numEntries; i++) - { - const GpFileSystem_Web_Resources::FileCatalogEntry &entry = catalog->m_entries[i]; - if (!strcmp(subPaths[0], entry.m_fileName)) - return new GpFileStream_Web_StaticMemFile(entry.m_data, entry.m_size); - } - - return nullptr; - } - } - - const char *mode = nullptr; - bool canWrite = false; - bool needResetPosition = false; - - switch (createDisposition) - { - case GpFileCreationDispositions::kCreateOrOverwrite: - mode = "wb"; - break; - case GpFileCreationDispositions::kCreateNew: - mode = "x+b"; - break; - case GpFileCreationDispositions::kCreateOrOpen: - mode = "a+b"; - needResetPosition = true; - break; - case GpFileCreationDispositions::kOpenExisting: - mode = writeAccess ? "r+b" : "rb"; - break; - case GpFileCreationDispositions::kOverwriteExisting: - mode = "r+b"; - break; - default: - return nullptr; - }; - - if (virtualDirectory == PortabilityLayer::VirtualDirectories::kSourceExport) - return nullptr; - - std::string resolvedPath; - if (!ResolvePath(virtualDirectory, subPaths, numSubPaths, false, resolvedPath)) - return nullptr; - - void *objStorage = malloc(sizeof(GpFileStream_Web_File)); - if (!objStorage) - return nullptr; - - FILE *f = fopen(resolvedPath.c_str(), mode); - if (!f) - { - free(objStorage); - return nullptr; - } - - if (needResetPosition) - fseek(f, 0, SEEK_SET); - - if (createDisposition == GpFileCreationDispositions::kOverwriteExisting) - { - if (ftruncate64(fileno(f), 0) < 0) - { - free(objStorage); - fclose(f); - return nullptr; - } - } - - return new (objStorage) GpFileStream_Web_File(f, !writeAccess, false, writeAccess); -} - -bool GpFileSystem_Web::DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) -{ - if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory)) - return false; - - std::string resolvedPath; - if (!ResolvePath(virtualDirectory, &path, 1, false, resolvedPath)) - { - existed = false; - return false; - } - - if (unlink(resolvedPath.c_str()) < 0) - { - existed = (errno != ENOENT); - if (existed) - FlushFileSystem(); - - return false; - } - existed = true; - return true; -} - -bool GpFileSystem_Web::ValidateFilePath(const char *path, size_t length) const -{ - for (size_t i = 0; i < length; i++) - { - const char c = path[i]; - if (c >= '0' && c <= '9') - continue; - - if (c == '_' || c == '.' || c == '\'' || c == '!') - continue; - - if (c == ' ' && i != 0 && i != length - 1) - continue; - - if (c >= 'a' && c <= 'z') - continue; - - if (c >= 'A' && c <= 'Z') - continue; - - return false; - } - - return true; -} - -bool GpFileSystem_Web::ValidateFilePathUnicodeChar(uint32_t c) const -{ - if (c >= '0' && c <= '9') - return true; - - if (c == '_' || c == '\'') - return true; - - if (c == ' ') - return true; - - if (c >= 'a' && c <= 'z') - return true; - - if (c >= 'A' && c <= 'Z') - return true; - - return false; -} - -void GpFileSystem_Web::SetDelayCallback(DelayCallback_t delayCallback) -{ - m_delayCallback = delayCallback; -} - -GpFileSystem_Web *GpFileSystem_Web::GetInstance() -{ - return &ms_instance; -} - -class GpDirectoryCursor_StringList final : public IGpDirectoryCursor -{ -public: - explicit GpDirectoryCursor_StringList(std::vector &paths); - ~GpDirectoryCursor_StringList(); - - bool GetNext(const char *&outFileName) override; - void Destroy() override; - -private: - std::vector m_paths; - size_t m_index; -}; - -GpDirectoryCursor_StringList::GpDirectoryCursor_StringList(std::vector &paths) - : m_index(0) -{ - std::swap(paths, m_paths); -} - -GpDirectoryCursor_StringList::~GpDirectoryCursor_StringList() -{ -} - -bool GpDirectoryCursor_StringList::GetNext(const char *&outFileName) -{ - if (m_index == m_paths.size()) - return false; - outFileName = m_paths[m_index].c_str(); - m_index++; - return true; -} - -void GpDirectoryCursor_StringList::Destroy() -{ - delete this; -} - - -IGpDirectoryCursor *GpFileSystem_Web::ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) -{ - if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory)) - return ScanCatalog(*catalog); - - std::string resolvedPrefix; - if (!ResolvePath(virtualDirectory, paths, numPaths, true, resolvedPrefix)) - return nullptr; - - std::string trimmedPrefix = resolvedPrefix.substr(m_prefsPath.size() + 1); - - DIR *d = opendir(m_prefsPath.c_str()); - if (!d) - return nullptr; - - return new GpDirectoryCursor_Web(d, trimmedPrefix); -} - -const GpFileSystem_Web_Resources::FileCatalog *GpFileSystem_Web::GetCatalogForVirtualDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory) -{ - if (virtualDirectory == PortabilityLayer::VirtualDirectories::kApplicationData) - return &GpFileSystem_Web_Resources::ApplicationData::GetCatalog(); - - if (virtualDirectory == PortabilityLayer::VirtualDirectories::kGameData) - return &GpFileSystem_Web_Resources::GameData::GetCatalog(); - - return nullptr; -} - -IGpDirectoryCursor *GpFileSystem_Web::ScanCatalog(const GpFileSystem_Web_Resources::FileCatalog &catalog) -{ - std::vector paths; - for (size_t i = 0; i < catalog.m_numEntries; i++) - paths.push_back(std::string(catalog.m_entries[i].m_fileName)); - - return new GpDirectoryCursor_StringList(paths); -} - -void GpFileSystem_Web::MarkFSStateDirty() -{ - ms_fsStateDirty = true; -} - -void GpFileSystem_Web::FlushFS() -{ - if (ms_fsStateDirty) - { - ms_fsStateDirty = false; - FlushFileSystem(); - } -} - - -#if GP_ASYNCIFY_PARANOID -void GpIOStream::Close() -{ - this->GP_ASYNCIFY_PARANOID_NAMED(Close)(); - GpFileSystem_Web::FlushFS(); -} - -bool IGpFileSystem::DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) -{ - return static_cast(this)->DeleteFile(virtualDirectory, path, existed); -} - -#endif - -GpFileSystem_Web GpFileSystem_Web::ms_instance; + rootPath = &m_exportPath; + isIDB = false; + break; + default: + return false; + }; + + if (pathAppend) + { + unsanitized = pathAppend; + + for (size_t i = 0; i < numPaths; i++) + { + unsanitized += "/"; + unsanitized += paths[i]; + } + + if (trailingSlash) + unsanitized += "/"; + + std::string sanitized; + for (size_t i = 0; i < unsanitized.size(); i++) + { + char c = unsanitized[i]; + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') + sanitized += c; + else + { + const char *nibbles = "0123456789abcdef"; + char subPath[4]; + subPath[0] = '%'; + subPath[1] = nibbles[(c >> 4) & 0xf]; + subPath[2] = nibbles[c & 0xf]; + subPath[3] = 0; + + sanitized += subPath; + } + } + + resolution = (*rootPath) + "/" + sanitized; + } + else + { + std::string sanitized = m_basePath + unsanitized; + + for (size_t i = 0; i < numPaths; i++) + { + sanitized += "/"; + sanitized += paths[i]; + } + + resolution = sanitized; + } + + outIsIDB = isIDB; + return true; +} + +GpFileSystem_Web::GpFileSystem_Web() + : m_delayCallback(nullptr) +{ +} + +GpFileSystem_Web::~GpFileSystem_Web() +{ +} + +void GpFileSystem_Web::Init() +{ + m_prefsPath = "/aerofoil"; + m_exportPath = "/aerofoil_memfs"; + + char *baseDir = SDL_GetBasePath(); + m_basePath = baseDir; + SDL_free(baseDir); + + char baseDirSeparator = m_basePath[m_basePath.size() - 1]; + if (m_basePath.size() >= 4 && m_basePath.substr(m_basePath.size() - 4, 3) == "bin") + m_basePath = m_basePath.substr(0, m_basePath.size() - 4) + "lib" + baseDirSeparator + "aerofoil" + baseDirSeparator; +} + +bool GpFileSystem_Web::FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) +{ + if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory)) + { + for (size_t i = 0; i < catalog->m_numEntries; i++) + { + const GpFileSystem_Web_Resources::FileCatalogEntry &entry = catalog->m_entries[i]; + if (!strcmp(path, entry.m_fileName)) + return true; + } + + return false; + } + + std::string resolvedPath; + bool isIDB = false; + if (!ResolvePath(virtualDirectory, &path, 1, false, resolvedPath, isIDB)) + return false; + + struct stat s; + return stat(resolvedPath.c_str(), &s) == 0; +} + +bool GpFileSystem_Web::FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &exists) +{ + if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory)) + { + for (size_t i = 0; i < catalog->m_numEntries; i++) + { + const GpFileSystem_Web_Resources::FileCatalogEntry &entry = catalog->m_entries[i]; + if (!strcmp(path, entry.m_fileName)) + { + exists = true; + return true; + } + } + + exists = false; + return false; + } + + std::string resolvedPath; + bool isIDB = false; + if (!ResolvePath(virtualDirectory, &path, 1, false, resolvedPath, isIDB)) + { + if (exists) + exists = false; + return false; + } + + int permissions = access(resolvedPath.c_str(), W_OK | F_OK); + exists = ((permissions & F_OK) != 0); + return ((permissions & W_OK) != 0); +} + +GpIOStream *GpFileSystem_Web::OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) +{ + if (numSubPaths == 1) + { + if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory)) + { + for (size_t i = 0; i < catalog->m_numEntries; i++) + { + const GpFileSystem_Web_Resources::FileCatalogEntry &entry = catalog->m_entries[i]; + if (!strcmp(subPaths[0], entry.m_fileName)) + return new GpFileStream_Web_StaticMemFile(entry.m_data, entry.m_size); + } + + return nullptr; + } + } + + const char *mode = nullptr; + bool canWrite = false; + bool needResetPosition = false; + + switch (createDisposition) + { + case GpFileCreationDispositions::kCreateOrOverwrite: + mode = "wb"; + break; + case GpFileCreationDispositions::kCreateNew: + mode = "x+b"; + break; + case GpFileCreationDispositions::kCreateOrOpen: + mode = "a+b"; + needResetPosition = true; + break; + case GpFileCreationDispositions::kOpenExisting: + mode = writeAccess ? "r+b" : "rb"; + break; + case GpFileCreationDispositions::kOverwriteExisting: + mode = "r+b"; + break; + default: + return nullptr; + }; + + std::string resolvedPath; + bool isIDB = false; + if (!ResolvePath(virtualDirectory, subPaths, numSubPaths, false, resolvedPath, isIDB)) + return nullptr; + + void *objStorage = malloc(sizeof(GpFileStream_Web_File)); + if (!objStorage) + return nullptr; + + FILE *f = fopen(resolvedPath.c_str(), mode); + if (!f) + { + free(objStorage); + return nullptr; + } + + if (needResetPosition) + fseek(f, 0, SEEK_SET); + + if (createDisposition == GpFileCreationDispositions::kOverwriteExisting) + { + if (ftruncate64(fileno(f), 0) < 0) + { + free(objStorage); + fclose(f); + return nullptr; + } + } + + std::string prettyName; + if (numSubPaths > 0) + prettyName = subPaths[numSubPaths - 1]; + + return new (objStorage) GpFileStream_Web_File(f, resolvedPath, prettyName, !writeAccess, false, writeAccess, isIDB); +} + +bool GpFileSystem_Web::DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) +{ + if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory)) + return false; + + std::string resolvedPath; + bool isIDB = false; + if (!ResolvePath(virtualDirectory, &path, 1, false, resolvedPath, isIDB)) + { + existed = false; + return false; + } + + if (unlink(resolvedPath.c_str()) < 0) + { + existed = (errno != ENOENT); + if (existed && isIDB) + FlushFileSystem(); + + return false; + } + existed = true; + return true; +} + +bool GpFileSystem_Web::ValidateFilePath(const char *path, size_t length) const +{ + for (size_t i = 0; i < length; i++) + { + const char c = path[i]; + if (c >= '0' && c <= '9') + continue; + + if (c == '_' || c == '.' || c == '\'' || c == '!') + continue; + + if (c == ' ' && i != 0 && i != length - 1) + continue; + + if (c >= 'a' && c <= 'z') + continue; + + if (c >= 'A' && c <= 'Z') + continue; + + return false; + } + + return true; +} + +bool GpFileSystem_Web::ValidateFilePathUnicodeChar(uint32_t c) const +{ + if (c >= '0' && c <= '9') + return true; + + if (c == '_' || c == '\'') + return true; + + if (c == ' ') + return true; + + if (c >= 'a' && c <= 'z') + return true; + + if (c >= 'A' && c <= 'Z') + return true; + + return false; +} + +void GpFileSystem_Web::SetDelayCallback(DelayCallback_t delayCallback) +{ + m_delayCallback = delayCallback; +} + +GpFileSystem_Web *GpFileSystem_Web::GetInstance() +{ + return &ms_instance; +} + +class GpDirectoryCursor_StringList final : public IGpDirectoryCursor +{ +public: + explicit GpDirectoryCursor_StringList(std::vector &paths); + ~GpDirectoryCursor_StringList(); + + bool GetNext(const char *&outFileName) override; + void Destroy() override; + +private: + std::vector m_paths; + size_t m_index; +}; + +GpDirectoryCursor_StringList::GpDirectoryCursor_StringList(std::vector &paths) + : m_index(0) +{ + std::swap(paths, m_paths); +} + +GpDirectoryCursor_StringList::~GpDirectoryCursor_StringList() +{ +} + +bool GpDirectoryCursor_StringList::GetNext(const char *&outFileName) +{ + if (m_index == m_paths.size()) + return false; + outFileName = m_paths[m_index].c_str(); + m_index++; + return true; +} + +void GpDirectoryCursor_StringList::Destroy() +{ + delete this; +} + + +IGpDirectoryCursor *GpFileSystem_Web::ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) +{ + if (const GpFileSystem_Web_Resources::FileCatalog *catalog = GetCatalogForVirtualDirectory(virtualDirectory)) + return ScanCatalog(*catalog); + + std::string resolvedPrefix; + bool isIDB = false; + if (!ResolvePath(virtualDirectory, paths, numPaths, true, resolvedPrefix, isIDB)) + return nullptr; + + std::string trimmedPrefix = resolvedPrefix.substr(m_prefsPath.size() + 1); + + DIR *d = opendir(m_prefsPath.c_str()); + if (!d) + return nullptr; + + return new GpDirectoryCursor_Web(d, trimmedPrefix); +} + +const GpFileSystem_Web_Resources::FileCatalog *GpFileSystem_Web::GetCatalogForVirtualDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory) +{ + if (virtualDirectory == PortabilityLayer::VirtualDirectories::kApplicationData) + return &GpFileSystem_Web_Resources::ApplicationData::GetCatalog(); + + if (virtualDirectory == PortabilityLayer::VirtualDirectories::kGameData) + return &GpFileSystem_Web_Resources::GameData::GetCatalog(); + + return nullptr; +} + +IGpDirectoryCursor *GpFileSystem_Web::ScanCatalog(const GpFileSystem_Web_Resources::FileCatalog &catalog) +{ + std::vector paths; + for (size_t i = 0; i < catalog.m_numEntries; i++) + paths.push_back(std::string(catalog.m_entries[i].m_fileName)); + + return new GpDirectoryCursor_StringList(paths); +} + +void GpFileSystem_Web::MarkFSStateDirty() +{ + ms_fsStateDirty = true; +} + +void GpFileSystem_Web::SyncDownloadFile(const std::string &filePath, const std::string &prettyName) +{ + DownloadAndDeleteFile(filePath.c_str(), prettyName.c_str()); +} + +void GpFileSystem_Web::FlushFS() +{ + if (ms_fsStateDirty) + { + ms_fsStateDirty = false; + FlushFileSystem(); + } +} + + +#if GP_ASYNCIFY_PARANOID +void GpIOStream::Close() +{ + this->GP_ASYNCIFY_PARANOID_NAMED(Close)(); + GpFileSystem_Web::FlushFS(); +} + +bool IGpFileSystem::DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) +{ + return static_cast(this)->DeleteFile(virtualDirectory, path, existed); +} + +#endif + +GpFileSystem_Web GpFileSystem_Web::ms_instance; diff --git a/AerofoilWeb/GpFileSystem_Web.h b/AerofoilWeb/GpFileSystem_Web.h index f0ae197..2885719 100644 --- a/AerofoilWeb/GpFileSystem_Web.h +++ b/AerofoilWeb/GpFileSystem_Web.h @@ -30,6 +30,7 @@ public: void SetDelayCallback(DelayCallback_t delayCallback) override; static void MarkFSStateDirty(); + static void SyncDownloadFile(const std::string &filePath, const std::string &prettyName); static void FlushFS(); static GpFileSystem_Web *GetInstance(); @@ -54,7 +55,7 @@ private: static IGpDirectoryCursor *ScanCatalog(const GpFileSystem_Web_Resources::FileCatalog &catalog); - bool ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, bool trailingSlash, std::string &resolution); + bool ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, bool trailingSlash, std::string &resolution, bool &outIsIDB); DelayCallback_t m_delayCallback; diff --git a/AerofoilWeb/GpMain_SDL_Web.cpp b/AerofoilWeb/GpMain_SDL_Web.cpp index 9ef6412..05c87eb 100644 --- a/AerofoilWeb/GpMain_SDL_Web.cpp +++ b/AerofoilWeb/GpMain_SDL_Web.cpp @@ -1,110 +1,109 @@ -#include -#include "SDL_main.h" - -#include "GpMain.h" -#include "GpAllocator_C.h" -#include "GpAudioDriverFactory.h" -#include "GpDisplayDriverFactory.h" -#include "GpGlobalConfig.h" -#include "GpFileSystem_Web.h" -#include "GpLogDriver_Web.h" -#include "GpFontHandlerFactory.h" -#include "GpInputDriverFactory.h" -#include "GpAppInterface.h" -#include "GpSystemServices_Web.h" -#include "GpVOSEvent.h" -#include "GpX.h" - -#include "IGpFileSystem.h" -#include "IGpThreadEvent.h" -#include "IGpVOSEventQueue.h" - -#include -#include - -GpXGlobals g_gpXGlobals; - -IGpDisplayDriver *GpDriver_CreateDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties); -IGpAudioDriver *GpDriver_CreateAudioDriver_SDL(const GpAudioDriverProperties &properties); -IGpInputDriver *GpDriver_CreateInputDriver_SDL2_Gamepad(const GpInputDriverProperties &properties); - -EM_JS(void, InitFileSystem, (), { +#include +#include "SDL_main.h" + +#include "GpMain.h" +#include "GpAllocator_C.h" +#include "GpAudioDriverFactory.h" +#include "GpDisplayDriverFactory.h" +#include "GpGlobalConfig.h" +#include "GpFileSystem_Web.h" +#include "GpLogDriver_Web.h" +#include "GpFontHandlerFactory.h" +#include "GpInputDriverFactory.h" +#include "GpAppInterface.h" +#include "GpSystemServices_Web.h" +#include "GpVOSEvent.h" +#include "GpX.h" + +#include "IGpFileSystem.h" +#include "IGpThreadEvent.h" +#include "IGpVOSEventQueue.h" + +#include +#include + +GpXGlobals g_gpXGlobals; + +IGpDisplayDriver *GpDriver_CreateDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties); +IGpAudioDriver *GpDriver_CreateAudioDriver_SDL(const GpAudioDriverProperties &properties); +IGpInputDriver *GpDriver_CreateInputDriver_SDL2_Gamepad(const GpInputDriverProperties &properties); + +EM_JS(void, InitFileSystem, (), { Asyncify.handleSleep(wakeUp => { FS.mkdir('/aerofoil'); - //FS.mkdir('/aerofoil_memfs'); - FS.mount(IDBFS, {}, '/aerofoil'); - //FS.mount(MEMFS, {}, '/aerofoil_memfs'); - //FS.mkdir('/aerofoil_memfs/Export'); - FS.syncfs(true, function (err) { - assert(!err); - wakeUp(); - }); - }); -}); - -int main(int argc, char* argv[]) -{ - IGpAllocator *alloc = GpAllocator_C::GetInstance(); - - InitFileSystem(); - - GpLogDriver_Web::Init(); - IGpLogDriver *logger = GpLogDriver_Web::GetInstance(); - -#if GP_ASYNCIFY_PARANOID - SDL_SetHint(SDL_HINT_EMSCRIPTEN_ASYNCIFY, "0"); -#endif - - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) - return -1; - - if (logger) - logger->Printf(IGpLogDriver::Category_Information, "Starting filesystem..."); - - GpFileSystem_Web::GetInstance()->Init(); - - GpDriverCollection *drivers = GpAppInterface_Get()->PL_GetDriverCollection(); - - if (logger) - logger->Printf(IGpLogDriver::Category_Information, "Setting up drivers..."); - - drivers->SetDriver(GpFileSystem_Web::GetInstance()); - drivers->SetDriver(GpSystemServices_Web::GetInstance()); - drivers->SetDriver(GpLogDriver_Web::GetInstance()); - drivers->SetDriver(alloc); - - g_gpGlobalConfig.m_displayDriverType = EGpDisplayDriverType_SDL_GL2; - - g_gpGlobalConfig.m_audioDriverType = EGpAudioDriverType_SDL2; - - g_gpGlobalConfig.m_fontHandlerType = EGpFontHandlerType_None; - - EGpInputDriverType inputDrivers[] = - { - EGpInputDriverType_SDL2_Gamepad - }; - - g_gpGlobalConfig.m_inputDriverTypes = inputDrivers; - g_gpGlobalConfig.m_numInputDrivers = sizeof(inputDrivers) / sizeof(inputDrivers[0]); - - g_gpGlobalConfig.m_osGlobals = &g_gpXGlobals; - g_gpGlobalConfig.m_logger = logger; - g_gpGlobalConfig.m_systemServices = GpSystemServices_Web::GetInstance(); - g_gpGlobalConfig.m_allocator = alloc; - - GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_SDL_GL2, GpDriver_CreateDisplayDriver_SDL_GL2); - GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_SDL2, GpDriver_CreateAudioDriver_SDL); - GpInputDriverFactory::RegisterInputDriverFactory(EGpInputDriverType_SDL2_Gamepad, GpDriver_CreateInputDriver_SDL2_Gamepad); - - if (logger) - logger->Printf(IGpLogDriver::Category_Information, "SDL environment configured, starting up"); - - int returnCode = GpMain::Run(); - - if (logger) - logger->Printf(IGpLogDriver::Category_Information, "SDL environment exited with code %i, cleaning up", returnCode); - - SDL_Quit(); - - return returnCode; -} + FS.mkdir('/aerofoil_memfs'); + FS.mount(IDBFS, {}, '/aerofoil'); + FS.mount(MEMFS, {}, '/aerofoil_memfs'); + FS.syncfs(true, function (err) { + assert(!err); + wakeUp(); + }); + }); +}); + +int main(int argc, char* argv[]) +{ + IGpAllocator *alloc = GpAllocator_C::GetInstance(); + + InitFileSystem(); + + GpLogDriver_Web::Init(); + IGpLogDriver *logger = GpLogDriver_Web::GetInstance(); + +#if GP_ASYNCIFY_PARANOID + SDL_SetHint(SDL_HINT_EMSCRIPTEN_ASYNCIFY, "0"); +#endif + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) + return -1; + + if (logger) + logger->Printf(IGpLogDriver::Category_Information, "Starting filesystem..."); + + GpFileSystem_Web::GetInstance()->Init(); + + GpDriverCollection *drivers = GpAppInterface_Get()->PL_GetDriverCollection(); + + if (logger) + logger->Printf(IGpLogDriver::Category_Information, "Setting up drivers..."); + + drivers->SetDriver(GpFileSystem_Web::GetInstance()); + drivers->SetDriver(GpSystemServices_Web::GetInstance()); + drivers->SetDriver(GpLogDriver_Web::GetInstance()); + drivers->SetDriver(alloc); + + g_gpGlobalConfig.m_displayDriverType = EGpDisplayDriverType_SDL_GL2; + + g_gpGlobalConfig.m_audioDriverType = EGpAudioDriverType_SDL2; + + g_gpGlobalConfig.m_fontHandlerType = EGpFontHandlerType_None; + + EGpInputDriverType inputDrivers[] = + { + EGpInputDriverType_SDL2_Gamepad + }; + + g_gpGlobalConfig.m_inputDriverTypes = inputDrivers; + g_gpGlobalConfig.m_numInputDrivers = sizeof(inputDrivers) / sizeof(inputDrivers[0]); + + g_gpGlobalConfig.m_osGlobals = &g_gpXGlobals; + g_gpGlobalConfig.m_logger = logger; + g_gpGlobalConfig.m_systemServices = GpSystemServices_Web::GetInstance(); + g_gpGlobalConfig.m_allocator = alloc; + + GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_SDL_GL2, GpDriver_CreateDisplayDriver_SDL_GL2); + GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_SDL2, GpDriver_CreateAudioDriver_SDL); + GpInputDriverFactory::RegisterInputDriverFactory(EGpInputDriverType_SDL2_Gamepad, GpDriver_CreateInputDriver_SDL2_Gamepad); + + if (logger) + logger->Printf(IGpLogDriver::Category_Information, "SDL environment configured, starting up"); + + int returnCode = GpMain::Run(); + + if (logger) + logger->Printf(IGpLogDriver::Category_Information, "SDL environment exited with code %i, cleaning up", returnCode); + + SDL_Quit(); + + return returnCode; +} diff --git a/AerofoilWeb/Link.bat b/AerofoilWeb/Link.bat index 39524b1..4309d8a 100644 --- a/AerofoilWeb/Link.bat +++ b/AerofoilWeb/Link.bat @@ -4,5 +4,7 @@ set OUTPUT_DIR=bin rem set DEBUG_LEVEL_FLAGS=-g4 -O0 set DEBUG_LEVEL_FLAGS=-O3 +copy /Y FileSaverDotJS\dist\FileSaver.js bin\FileSaver.js + set FLAGS=-flto %DEBUG_LEVEL_FLAGS% -s USE_SDL=2 -s USE_ZLIB=1 -s ASYNCIFY -s ASYNCIFY_IGNORE_INDIRECT -s INITIAL_MEMORY=33554432 -s ASYNCIFY_ADVISE -lidbfs.js -s ASYNCIFY_IMPORTS=['InitFileSystem','FlushFileSystem'] --shell-file shell_minimal.html emcc obj/AerofoilWeb_Combined.o obj/AerofoilWeb_Resources.o obj/GpShell_Combined.o obj/AerofoilSDL_Combined.o obj/AerofoilPortable_Combined.o obj/GpApp_Combined.o obj/PortabilityLayer_Combined.o obj/MacRomanConversion.o -o %OUTPUT_DIR%/aerofoil.html %FLAGS% diff --git a/AerofoilWeb/shell_minimal.html b/AerofoilWeb/shell_minimal.html index d7b8edb..0cf894d 100644 --- a/AerofoilWeb/shell_minimal.html +++ b/AerofoilWeb/shell_minimal.html @@ -3,7 +3,7 @@ - Aerofoil Web Pre-Release + Aerofoil