mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-23 06:53:43 +00:00
Add support for unpackaged resources to speed up loads on Android, i.e. so we don't have to decompress entire GPAs to load a single resource.
This commit is contained in:
@@ -25,6 +25,11 @@ static const char *gs_forbiddenNames[] =
|
||||
"LPT9",
|
||||
};
|
||||
|
||||
static bool IsCharForbidden(char c)
|
||||
{
|
||||
return (c < ' ' || c == '<' || c == '>' || c == ':' || c == '\"' || c == '/' || c == '\\' || c == '|' || c == '?' || c == '*' || c > '~' || c == '$' || c == '#');
|
||||
}
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
GpArcResourceTypeTag GpArcResourceTypeTag::Encode(const ResTypeID &tag)
|
||||
@@ -42,7 +47,7 @@ namespace PortabilityLayer
|
||||
{
|
||||
char c = chars[i];
|
||||
|
||||
bool isForbidden = (c < ' ' || c == '<' || c == '>' || c == ':' || c == '\"' || c == '/' || c == '\\' || c == '|' || c == '?' || c == '*' || c > '~' || c == '$');
|
||||
bool isForbidden = IsCharForbidden(c);
|
||||
|
||||
if (i == 3)
|
||||
{
|
||||
@@ -81,4 +86,57 @@ namespace PortabilityLayer
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
bool GpArcResourceTypeTag::Load(const char *str)
|
||||
{
|
||||
size_t l = strlen(str);
|
||||
if (l < sizeof(m_id))
|
||||
{
|
||||
memcpy(m_id, str, l);
|
||||
m_id[l] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GpArcResourceTypeTag::Decode(ResTypeID &outTag)
|
||||
{
|
||||
char decodedChars[4];
|
||||
size_t parseOffset = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
char parseChar = m_id[parseOffset];
|
||||
if (parseChar == '\0')
|
||||
return false;
|
||||
|
||||
if (parseChar == '$')
|
||||
{
|
||||
char nibbles[2] = { m_id[parseOffset + 1], m_id[parseOffset + 2] };
|
||||
parseOffset += 2;
|
||||
|
||||
int decodedNibbles[2];
|
||||
|
||||
for (int n = 0; n < 2; n++)
|
||||
{
|
||||
char nibble = nibbles[n];
|
||||
if (nibble >= '0' && nibble <= '9')
|
||||
decodedNibbles[n] = nibble - '0';
|
||||
else if (nibble >= 'a' && nibble <= 'f')
|
||||
decodedNibbles[n] = nibble - 'a' + 0xa;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
decodedChars[i] = (decodedNibbles[0] << 4) | (decodedNibbles[1]);
|
||||
}
|
||||
else
|
||||
decodedChars[i] = parseChar;
|
||||
|
||||
parseOffset++;
|
||||
}
|
||||
|
||||
outTag = ResTypeID(decodedChars);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -9,5 +9,8 @@ namespace PortabilityLayer
|
||||
char m_id[13];
|
||||
|
||||
static GpArcResourceTypeTag Encode(const ResTypeID &tag);
|
||||
|
||||
bool Load(const char *str);
|
||||
bool Decode(ResTypeID &outTag);
|
||||
};
|
||||
}
|
||||
|
@@ -16,17 +16,32 @@ namespace PortabilityLayer
|
||||
public:
|
||||
virtual bool FileExists(VirtualDirectory_t virtualDirectory, const char *path) = 0;
|
||||
virtual bool FileLocked(VirtualDirectory_t virtualDirectory, const char *path, bool *exists) = 0;
|
||||
virtual GpIOStream *OpenFile(VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition) = 0;
|
||||
virtual GpIOStream *OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) = 0;
|
||||
virtual bool DeleteFile(VirtualDirectory_t virtualDirectory, const char *path, bool &existed) = 0;
|
||||
virtual HostDirectoryCursor *ScanDirectory(VirtualDirectory_t virtualDirectory) = 0;
|
||||
virtual HostDirectoryCursor *ScanDirectoryNested(VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) = 0;
|
||||
HostDirectoryCursor *ScanDirectory(VirtualDirectory_t virtualDirectory);
|
||||
|
||||
virtual bool ValidateFilePath(const char *path, size_t pathLen) const = 0;
|
||||
virtual bool ValidateFilePathUnicodeChar(uint32_t ch) const = 0;
|
||||
|
||||
virtual bool IsVirtualDirectoryLooseResources(VirtualDirectory_t virtualDir) const = 0;
|
||||
|
||||
static HostFileSystem *GetInstance();
|
||||
static void SetInstance(HostFileSystem *instance);
|
||||
|
||||
GpIOStream *OpenFile(VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition);
|
||||
|
||||
private:
|
||||
static HostFileSystem *ms_instance;
|
||||
};
|
||||
}
|
||||
|
||||
inline GpIOStream *PortabilityLayer::HostFileSystem::OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition)
|
||||
{
|
||||
return this->OpenFileNested(virtualDirectory, &path, 1, writeAccess, createDisposition);
|
||||
}
|
||||
|
||||
inline PortabilityLayer::HostDirectoryCursor *PortabilityLayer::HostFileSystem::ScanDirectory(VirtualDirectory_t virtualDirectory)
|
||||
{
|
||||
return this->ScanDirectoryNested(virtualDirectory, nullptr, 0);
|
||||
}
|
||||
|
@@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef __PL_HOST_SYSTEM_SERVICES_H__
|
||||
#define __PL_HOST_SYSTEM_SERVICES_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -34,5 +32,3 @@ namespace PortabilityLayer
|
||||
static HostSystemServices *ms_instance;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
class ResourceArchive;
|
||||
struct IResourceArchive;
|
||||
}
|
||||
|
||||
struct DrawSurface;
|
||||
@@ -52,7 +52,7 @@ private:
|
||||
~AnimationPackage();
|
||||
|
||||
THandle<BitmapImage> *m_images;
|
||||
PortabilityLayer::ResourceArchive *m_resArchive;
|
||||
PortabilityLayer::IResourceArchive *m_resArchive;
|
||||
size_t m_numImages;
|
||||
|
||||
uint32_t m_frameRateNumerator;
|
||||
|
@@ -1,8 +1,10 @@
|
||||
#include "ResourceManager.h"
|
||||
|
||||
#include "BinarySearch.h"
|
||||
#include "BMPFormat.h"
|
||||
#include "FileManager.h"
|
||||
#include "GPArchive.h"
|
||||
#include "HostDirectoryCursor.h"
|
||||
#include "HostFileSystem.h"
|
||||
#include "HostMemoryBuffer.h"
|
||||
#include "GpIOStream.h"
|
||||
@@ -21,6 +23,7 @@
|
||||
#include "ZipFile.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ResourceValidationRules
|
||||
{
|
||||
@@ -97,7 +100,7 @@ namespace
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
struct MMHandleBlock;
|
||||
class ResourceArchive;
|
||||
struct IResourceArchive;
|
||||
|
||||
class ResourceManagerImpl final : public ResourceManager
|
||||
{
|
||||
@@ -108,9 +111,9 @@ namespace PortabilityLayer
|
||||
void Shutdown() override;
|
||||
|
||||
THandle<void> GetAppResource(const ResTypeID &resTypeID, int16_t resID) const override;
|
||||
ResourceArchive *GetAppResourceArchive() const override;
|
||||
IResourceArchive *GetAppResourceArchive() const override;
|
||||
|
||||
ResourceArchive *LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const override;
|
||||
IResourceArchive *LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const override;
|
||||
PLError_t CreateBlankResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) override;
|
||||
|
||||
void DissociateHandle(MMHandleBlock *hdl) const override;
|
||||
@@ -119,9 +122,11 @@ namespace PortabilityLayer
|
||||
static ResourceManagerImpl *GetInstance();
|
||||
|
||||
private:
|
||||
void UnloadAndDestroyResourceFile(ResourceArchive *rf);
|
||||
void UnloadAndDestroyResourceFile(IResourceArchive *rf);
|
||||
|
||||
ResourceArchive *m_appResArchive;
|
||||
IResourceArchive *LoadResDirectory(VirtualDirectory_t virtualDir, const PLPasStr &filename) const;
|
||||
|
||||
IResourceArchive *m_appResArchive;
|
||||
|
||||
static ResourceManagerImpl ms_instance;
|
||||
};
|
||||
@@ -157,18 +162,36 @@ namespace PortabilityLayer
|
||||
return hdl->m_rmSelfRef;
|
||||
}
|
||||
|
||||
void ResourceManagerImpl::UnloadAndDestroyResourceFile(ResourceArchive *rf)
|
||||
void ResourceManagerImpl::UnloadAndDestroyResourceFile(IResourceArchive *rf)
|
||||
{
|
||||
rf->Destroy();
|
||||
}
|
||||
|
||||
IResourceArchive *ResourceManagerImpl::LoadResDirectory(VirtualDirectory_t virtualDir, const PLPasStr &filename) const
|
||||
{
|
||||
ResourceArchiveDirectory *archive = ResourceArchiveDirectory::Create(virtualDir, filename);
|
||||
if (!archive)
|
||||
return nullptr;
|
||||
|
||||
if (!archive->Init())
|
||||
{
|
||||
archive->Destroy();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return archive;
|
||||
}
|
||||
|
||||
ResourceManagerImpl *ResourceManagerImpl::GetInstance()
|
||||
{
|
||||
return &ms_instance;
|
||||
}
|
||||
|
||||
ResourceArchive *ResourceManagerImpl::LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const
|
||||
IResourceArchive *ResourceManagerImpl::LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const
|
||||
{
|
||||
if (PortabilityLayer::HostFileSystem::GetInstance()->IsVirtualDirectoryLooseResources(virtualDir))
|
||||
return LoadResDirectory(virtualDir, filename);
|
||||
|
||||
GpIOStream *fStream = nullptr;
|
||||
if (FileManager::GetInstance()->RawOpenFileResources(virtualDir, filename, EFilePermission_Read, true, GpFileCreationDispositions::kOpenExisting, fStream) != PLErrors::kNone)
|
||||
return nullptr;
|
||||
@@ -180,7 +203,7 @@ namespace PortabilityLayer
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ResourceArchive *archive = ResourceArchive::Create(proxy, fStream);
|
||||
IResourceArchive *archive = ResourceArchiveZipFile::Create(proxy, fStream);
|
||||
if (!archive)
|
||||
{
|
||||
proxy->Destroy();
|
||||
@@ -227,7 +250,7 @@ namespace PortabilityLayer
|
||||
return m_appResArchive->LoadResource(resType, resID);
|
||||
}
|
||||
|
||||
ResourceArchive *ResourceManagerImpl::GetAppResourceArchive() const
|
||||
IResourceArchive *ResourceManagerImpl::GetAppResourceArchive() const
|
||||
{
|
||||
return m_appResArchive;
|
||||
}
|
||||
@@ -238,6 +261,31 @@ namespace PortabilityLayer
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
const char *ResourceArchiveBase::GetFileExtensionForResType(const ResTypeID &resTypeID, int &outValidationRule)
|
||||
{
|
||||
const char *extension = ".bin";
|
||||
outValidationRule = ResourceValidationRules::kNone;
|
||||
|
||||
if (resTypeID == ResTypeID('snd '))
|
||||
{
|
||||
extension = ".wav";
|
||||
outValidationRule = ResourceValidationRules::kWAV;
|
||||
}
|
||||
else if (resTypeID == ResTypeID('Date') || resTypeID == ResTypeID('PICT'))
|
||||
{
|
||||
extension = kPICTExtension;
|
||||
outValidationRule = ResourceValidationRules::kBMP;
|
||||
}
|
||||
else if (resTypeID == ResTypeID('STR#'))
|
||||
extension = ".txt";
|
||||
else if (resTypeID == ResTypeID('DITL') || resTypeID == ResTypeID('muvi'))
|
||||
extension = ".json";
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
ResourceArchiveRef::ResourceArchiveRef()
|
||||
: m_handle(nullptr)
|
||||
, m_size(0)
|
||||
@@ -245,7 +293,9 @@ namespace PortabilityLayer
|
||||
{
|
||||
}
|
||||
|
||||
ResourceArchive *ResourceArchive::Create(ZipFileProxy *zipFileProxy, GpIOStream *stream)
|
||||
// ===========================================================================================
|
||||
|
||||
ResourceArchiveZipFile *ResourceArchiveZipFile::Create(ZipFileProxy *zipFileProxy, GpIOStream *stream)
|
||||
{
|
||||
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
|
||||
|
||||
@@ -262,39 +312,28 @@ namespace PortabilityLayer
|
||||
new (refs + i) ResourceArchiveRef();
|
||||
}
|
||||
|
||||
void *storage = mm->Alloc(sizeof(ResourceArchive));
|
||||
void *storage = mm->Alloc(sizeof(ResourceArchiveZipFile));
|
||||
if (!storage)
|
||||
{
|
||||
mm->Release(refs);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new (storage) ResourceArchive(zipFileProxy, stream, refs);
|
||||
return new (storage) ResourceArchiveZipFile(zipFileProxy, stream, refs);
|
||||
}
|
||||
|
||||
void ResourceArchive::Destroy()
|
||||
void ResourceArchiveZipFile::Destroy()
|
||||
{
|
||||
this->~ResourceArchive();
|
||||
this->~ResourceArchiveZipFile();
|
||||
PortabilityLayer::MemoryManager::GetInstance()->Release(this);
|
||||
}
|
||||
|
||||
THandle<void> ResourceArchive::LoadResource(const ResTypeID &resTypeID, int id)
|
||||
THandle<void> ResourceArchiveZipFile::LoadResource(const ResTypeID &resTypeID, int id)
|
||||
{
|
||||
return GetResource(resTypeID, id, true);
|
||||
}
|
||||
|
||||
bool ResourceArchive::GetResourceSize(const ResTypeID &resTypeID, int id, size_t &outSize) const
|
||||
{
|
||||
size_t index = 0;
|
||||
int validationRule = 0;
|
||||
if (!IndexResource(resTypeID, id, index, validationRule))
|
||||
return false;
|
||||
|
||||
outSize = m_zipFileProxy->GetFileSize(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceArchive::HasAnyResourcesOfType(const ResTypeID &resTypeID) const
|
||||
bool ResourceArchiveZipFile::HasAnyResourcesOfType(const ResTypeID &resTypeID) const
|
||||
{
|
||||
char resPrefix[6];
|
||||
resTypeID.ExportAsChars(resPrefix);
|
||||
@@ -304,7 +343,7 @@ namespace PortabilityLayer
|
||||
return m_zipFileProxy->HasPrefix(resPrefix);
|
||||
}
|
||||
|
||||
bool ResourceArchive::FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const
|
||||
bool ResourceArchiveZipFile::FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const
|
||||
{
|
||||
char resPrefix[6];
|
||||
resTypeID.ExportAsChars(resPrefix);
|
||||
@@ -409,25 +448,9 @@ namespace PortabilityLayer
|
||||
return haveAny;
|
||||
}
|
||||
|
||||
bool ResourceArchive::IndexResource(const ResTypeID &resTypeID, int id, size_t &outIndex, int &outValidationRule) const
|
||||
bool ResourceArchiveZipFile::IndexResource(const ResTypeID &resTypeID, int id, size_t &outIndex, int &outValidationRule) const
|
||||
{
|
||||
const char *extension = ".bin";
|
||||
outValidationRule = ResourceValidationRules::kNone;
|
||||
|
||||
if (resTypeID == ResTypeID('snd '))
|
||||
{
|
||||
extension = ".wav";
|
||||
outValidationRule = ResourceValidationRules::kWAV;
|
||||
}
|
||||
else if (resTypeID == ResTypeID('Date') || resTypeID == ResTypeID('PICT'))
|
||||
{
|
||||
extension = kPICTExtension;
|
||||
outValidationRule = ResourceValidationRules::kBMP;
|
||||
}
|
||||
else if (resTypeID == ResTypeID('STR#'))
|
||||
extension = ".txt";
|
||||
else if (resTypeID == ResTypeID('DITL') || resTypeID == ResTypeID('muvi'))
|
||||
extension = ".json";
|
||||
const char *extension = GetFileExtensionForResType(resTypeID, outValidationRule);
|
||||
|
||||
char resourceFile[64];
|
||||
|
||||
@@ -439,7 +462,7 @@ namespace PortabilityLayer
|
||||
return m_zipFileProxy->IndexFile(resourceFile, outIndex);
|
||||
}
|
||||
|
||||
THandle<void> ResourceArchive::GetResource(const ResTypeID &resTypeID, int id, bool load)
|
||||
THandle<void> ResourceArchiveZipFile::GetResource(const ResTypeID &resTypeID, int id, bool load)
|
||||
{
|
||||
int validationRule = 0;
|
||||
size_t index = 0;
|
||||
@@ -485,14 +508,14 @@ namespace PortabilityLayer
|
||||
return THandle<void>(handle);
|
||||
}
|
||||
|
||||
ResourceArchive::ResourceArchive(ZipFileProxy *zipFileProxy, GpIOStream *stream, ResourceArchiveRef *resourceHandles)
|
||||
ResourceArchiveZipFile::ResourceArchiveZipFile(ZipFileProxy *zipFileProxy, GpIOStream *stream, ResourceArchiveRef *resourceHandles)
|
||||
: m_zipFileProxy(zipFileProxy)
|
||||
, m_stream(stream)
|
||||
, m_resourceHandles(resourceHandles)
|
||||
{
|
||||
}
|
||||
|
||||
ResourceArchive::~ResourceArchive()
|
||||
ResourceArchiveZipFile::~ResourceArchiveZipFile()
|
||||
{
|
||||
MemoryManager *mm = MemoryManager::GetInstance();
|
||||
|
||||
@@ -511,4 +534,333 @@ namespace PortabilityLayer
|
||||
m_zipFileProxy->Destroy();
|
||||
m_stream->Close();
|
||||
}
|
||||
|
||||
// ========================================================================================
|
||||
|
||||
ResourceArchiveDirectory *ResourceArchiveDirectory::Create(VirtualDirectory_t directory, const PLPasStr &subdirectory)
|
||||
{
|
||||
void *storage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(ResourceArchiveDirectory));
|
||||
if (!storage)
|
||||
return nullptr;
|
||||
|
||||
return new (storage) ResourceArchiveDirectory(directory, subdirectory);
|
||||
}
|
||||
|
||||
void ResourceArchiveDirectory::Destroy()
|
||||
{
|
||||
this->~ResourceArchiveDirectory();
|
||||
PortabilityLayer::MemoryManager::GetInstance()->Release(this);
|
||||
}
|
||||
|
||||
THandle<void> ResourceArchiveDirectory::LoadResource(const ResTypeID &resTypeID, int id)
|
||||
{
|
||||
return GetResource(resTypeID, id, true);
|
||||
}
|
||||
|
||||
bool ResourceArchiveDirectory::HasAnyResourcesOfType(const ResTypeID &resTypeID) const
|
||||
{
|
||||
int16_t scratch;
|
||||
return FindFirstResourceOfType(resTypeID, scratch);
|
||||
}
|
||||
|
||||
bool ResourceArchiveDirectory::FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const
|
||||
{
|
||||
int32_t resID32 = resTypeID.ExportAsInt32();
|
||||
|
||||
const ResTypeEntry *firstTypeEntry = *m_resTypes;
|
||||
const ResTypeEntry *lastTypeEntry = firstTypeEntry + m_numResourceTypes;
|
||||
|
||||
const ResTypeEntry *entry = BinarySearch(firstTypeEntry, lastTypeEntry, resID32, ResourceArchiveDirectory::ResTypeSearchPredicate);
|
||||
|
||||
if (entry == lastTypeEntry)
|
||||
return false;
|
||||
|
||||
outID = (*m_resIDs)[entry->m_firstRes];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceArchiveDirectory::Init()
|
||||
{
|
||||
PortabilityLayer::HostFileSystem *fs = PortabilityLayer::HostFileSystem::GetInstance();
|
||||
|
||||
const char *typePaths[1] = { this->m_subdirectory };
|
||||
|
||||
PortabilityLayer::HostDirectoryCursor *typeDirCursor = fs->ScanDirectoryNested(m_directory, typePaths, 1);
|
||||
if (!typeDirCursor)
|
||||
return false;
|
||||
|
||||
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
|
||||
|
||||
m_resTypes = THandle<ResTypeEntry>(mm->AllocHandle(0));
|
||||
if (!m_resTypes)
|
||||
return false;
|
||||
|
||||
m_resIDs = THandle<int16_t>(mm->AllocHandle(0));
|
||||
if (!m_resIDs)
|
||||
return false;
|
||||
|
||||
size_t resTypeCapacity = 0;
|
||||
size_t resIDCapacity = 0;
|
||||
|
||||
const char *typeScanFilename = nullptr;
|
||||
while (typeDirCursor->GetNext(typeScanFilename))
|
||||
{
|
||||
GpArcResourceTypeTag resourceTypeTag;
|
||||
if (!resourceTypeTag.Load(typeScanFilename))
|
||||
continue;
|
||||
|
||||
ResTypeID resTypeID;
|
||||
if (!resourceTypeTag.Decode(resTypeID))
|
||||
continue;
|
||||
|
||||
int32_t dirResType = resTypeID.ExportAsInt32();
|
||||
|
||||
ResTypeEntry rte;
|
||||
rte.m_resTypeID = dirResType;
|
||||
rte.m_firstRes = m_numResources;
|
||||
rte.m_lastRes = m_numResources;
|
||||
|
||||
const char *idScanFilenames[2] = { this->m_subdirectory, typeScanFilename };
|
||||
PortabilityLayer::HostDirectoryCursor *typeIDCursor = fs->ScanDirectoryNested(m_directory, idScanFilenames, 2);
|
||||
if (!typeIDCursor)
|
||||
continue;
|
||||
|
||||
const char *idScanFilename = nullptr;
|
||||
while (typeIDCursor->GetNext(idScanFilename))
|
||||
{
|
||||
int resID = 0;
|
||||
bool isNegative = false;
|
||||
|
||||
for (size_t chi = 0; idScanFilename[chi] != '.' && idScanFilename[chi] != '\0'; chi++)
|
||||
{
|
||||
char ch = idScanFilename[chi];
|
||||
if (ch == '-')
|
||||
isNegative = true;
|
||||
else if (ch >= '0' && ch <= '9')
|
||||
{
|
||||
resID *= 10;
|
||||
int digit = ch - '0';
|
||||
if (isNegative)
|
||||
resID -= digit;
|
||||
else
|
||||
resID += digit;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_numResources == resIDCapacity)
|
||||
{
|
||||
const size_t oldCapacity = resIDCapacity;
|
||||
|
||||
resIDCapacity *= 2;
|
||||
if (resIDCapacity == 0)
|
||||
resIDCapacity = 1;
|
||||
|
||||
if (!mm->ResizeHandle(m_resIDs.MMBlock(), sizeof(int16_t) * resIDCapacity))
|
||||
{
|
||||
typeIDCursor->Destroy();
|
||||
typeDirCursor->Destroy();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
(*m_resIDs)[m_numResources] = resID;
|
||||
m_numResources++;
|
||||
}
|
||||
|
||||
typeIDCursor->Destroy();
|
||||
rte.m_lastRes = m_numResources;
|
||||
|
||||
if (m_numResourceTypes == resTypeCapacity)
|
||||
{
|
||||
const size_t oldCapacity = resTypeCapacity;
|
||||
|
||||
resTypeCapacity *= 2;
|
||||
if (resTypeCapacity == 0)
|
||||
resTypeCapacity = 1;
|
||||
|
||||
if (!mm->ResizeHandle(m_resTypes.MMBlock(), sizeof(ResTypeEntry) * resTypeCapacity))
|
||||
{
|
||||
typeDirCursor->Destroy();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
(*m_resTypes)[m_numResourceTypes] = rte;
|
||||
m_numResourceTypes++;
|
||||
}
|
||||
|
||||
mm->ResizeHandle(m_resTypes.MMBlock(), sizeof(ResTypeEntry) * m_numResourceTypes);
|
||||
mm->ResizeHandle(m_resIDs.MMBlock(), sizeof(int16_t) * m_numResources);
|
||||
|
||||
ResTypeEntry *resTypes = *m_resTypes;
|
||||
int16_t *resIDs = *m_resIDs;
|
||||
|
||||
std::sort(resTypes, resTypes + m_numResourceTypes, ResourceArchiveDirectory::ResTypeEntrySortPredicate);
|
||||
|
||||
for (size_t i = 0; i < m_numResourceTypes; i++)
|
||||
{
|
||||
int16_t *resIDStart = resIDs + resTypes[i].m_firstRes;
|
||||
int16_t *resIDEnd = resIDs + resTypes[i].m_lastRes;
|
||||
|
||||
std::sort(resIDStart, resIDEnd);
|
||||
}
|
||||
|
||||
m_resourceHandles = static_cast<ResourceArchiveRef*>(mm->Alloc(sizeof(ResourceArchiveRef) * m_numResources));
|
||||
if (!m_resourceHandles)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < m_numResources; i++)
|
||||
new (m_resourceHandles + i) ResourceArchiveRef();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceArchiveDirectory::IndexResource(const ResTypeID &resTypeID, int id, size_t &outIndex) const
|
||||
{
|
||||
int32_t resID32 = resTypeID.ExportAsInt32();
|
||||
|
||||
const ResTypeEntry *firstTypeEntry = *m_resTypes;
|
||||
const ResTypeEntry *lastTypeEntry = firstTypeEntry + m_numResourceTypes;
|
||||
|
||||
const ResTypeEntry *entry = BinarySearch(firstTypeEntry, lastTypeEntry, resID32, ResourceArchiveDirectory::ResTypeSearchPredicate);
|
||||
|
||||
if (entry == lastTypeEntry)
|
||||
return false;
|
||||
|
||||
const int16_t *resIDs = *m_resIDs;
|
||||
const int16_t *firstRes = resIDs + entry->m_firstRes;
|
||||
const int16_t *lastRes = resIDs + entry->m_lastRes;
|
||||
|
||||
const int16_t *idLoc = BinarySearch(firstRes, lastRes, static_cast<int16_t>(id), ResourceArchiveDirectory::ResIDSearchPredicate);
|
||||
if (idLoc == lastRes)
|
||||
return false;
|
||||
|
||||
outIndex = static_cast<size_t>(idLoc - resIDs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
THandle<void> ResourceArchiveDirectory::GetResource(const ResTypeID &resTypeID, int id, bool load)
|
||||
{
|
||||
|
||||
int validationRule = 0;
|
||||
size_t index = 0;
|
||||
if (!IndexResource(resTypeID, id, index))
|
||||
return THandle<void>();
|
||||
|
||||
ResourceArchiveRef *ref = m_resourceHandles + index;
|
||||
|
||||
MMHandleBlock *handle = nullptr;
|
||||
if (ref->m_handle != nullptr)
|
||||
handle = ref->m_handle;
|
||||
else
|
||||
{
|
||||
handle = MemoryManager::GetInstance()->AllocHandle(0);
|
||||
if (!handle)
|
||||
return THandle<void>();
|
||||
|
||||
handle->m_rmSelfRef = ref;
|
||||
ref->m_handle = handle;
|
||||
ref->m_resID = static_cast<int16_t>(id);
|
||||
ref->m_size = 0;
|
||||
}
|
||||
|
||||
if (handle->m_contents == nullptr && load)
|
||||
{
|
||||
int validationRule = 0;
|
||||
const char *extension = GetFileExtensionForResType(resTypeID, validationRule);
|
||||
|
||||
GpArcResourceTypeTag resTypeTag = GpArcResourceTypeTag::Encode(resTypeID);
|
||||
char fileName[32];
|
||||
|
||||
snprintf(fileName, sizeof(fileName) - 1, "%i%s", id, extension);
|
||||
|
||||
const char *paths[3] = { m_subdirectory, resTypeTag.m_id, fileName };
|
||||
|
||||
GpIOStream *ioStream = PortabilityLayer::HostFileSystem::GetInstance()->OpenFileNested(m_directory, paths, 3, false, GpFileCreationDispositions::kOpenExisting);
|
||||
if (!ioStream)
|
||||
return THandle<void>();
|
||||
|
||||
size_t size = ioStream->Size();
|
||||
|
||||
void *contents = MemoryManager::GetInstance()->Alloc(size);
|
||||
handle->m_contents = contents;
|
||||
handle->m_size = size;
|
||||
ref->m_size = size;
|
||||
|
||||
bool readOK = (ioStream->Read(contents, size));
|
||||
ioStream->Close();
|
||||
|
||||
if (!readOK || (validationRule != ResourceValidationRules::kNone && !ValidateResource(contents, ref->m_size, static_cast<ResourceValidationRule_t>(validationRule))))
|
||||
{
|
||||
MemoryManager::GetInstance()->Release(contents);
|
||||
handle->m_contents = nullptr;
|
||||
handle->m_size = 0;
|
||||
ref->m_size = 0;
|
||||
|
||||
return THandle<void>();
|
||||
}
|
||||
}
|
||||
|
||||
return THandle<void>(handle);
|
||||
}
|
||||
|
||||
int ResourceArchiveDirectory::ResTypeSearchPredicate(int32_t resTypeID, const ResTypeEntry &entry)
|
||||
{
|
||||
if (resTypeID < entry.m_resTypeID)
|
||||
return -1;
|
||||
if (resTypeID > entry.m_resTypeID)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ResourceArchiveDirectory::ResIDSearchPredicate(int16_t resTypeID, int16_t entry)
|
||||
{
|
||||
if (resTypeID < entry)
|
||||
return -1;
|
||||
if (resTypeID > entry)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ResourceArchiveDirectory::ResTypeEntrySortPredicate(const ResTypeEntry &a, const ResTypeEntry &b)
|
||||
{
|
||||
return a.m_resTypeID < b.m_resTypeID;
|
||||
}
|
||||
|
||||
ResourceArchiveDirectory::ResourceArchiveDirectory(VirtualDirectory_t directory, const PLPasStr &subdirectory)
|
||||
: m_directory(directory)
|
||||
, m_numResourceTypes(0)
|
||||
, m_resourceHandles(nullptr)
|
||||
, m_numResources(0)
|
||||
{
|
||||
memcpy(m_subdirectory, subdirectory.UChars(), subdirectory.Length());
|
||||
m_subdirectory[subdirectory.Length()] = '\0';
|
||||
}
|
||||
|
||||
ResourceArchiveDirectory::~ResourceArchiveDirectory()
|
||||
{
|
||||
MemoryManager *mm = MemoryManager::GetInstance();
|
||||
|
||||
const size_t numHandles = m_numResources;
|
||||
|
||||
if (m_resourceHandles)
|
||||
{
|
||||
for (size_t i = 0; i < numHandles; i++)
|
||||
{
|
||||
ResourceArchiveRef &ref = m_resourceHandles[numHandles - 1 - i];
|
||||
if (ref.m_handle)
|
||||
mm->ReleaseHandle(ref.m_handle);
|
||||
|
||||
ref.~ResourceArchiveRef();
|
||||
}
|
||||
}
|
||||
|
||||
mm->Release(m_resourceHandles);
|
||||
|
||||
m_resIDs.Dispose();
|
||||
m_resTypes.Dispose();
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ namespace PortabilityLayer
|
||||
bool operator!=(const ResTypeID &other) const;
|
||||
|
||||
void ExportAsChars(char *chars) const;
|
||||
int32_t ExportAsInt32() const;
|
||||
|
||||
private:
|
||||
char m_id[4];
|
||||
@@ -70,6 +71,11 @@ namespace PortabilityLayer
|
||||
{
|
||||
memcpy(chars, m_id, 4);
|
||||
}
|
||||
|
||||
inline int32_t ResTypeID::ExportAsInt32() const
|
||||
{
|
||||
return ResTypeIDCodec::Decode(m_id);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "VirtualDirectory.h"
|
||||
#include "PascalStr.h"
|
||||
#include "PLErrorCodes.h"
|
||||
#include "PLHandle.h"
|
||||
|
||||
@@ -25,21 +26,78 @@ namespace PortabilityLayer
|
||||
int16_t m_resID;
|
||||
};
|
||||
|
||||
class ResourceArchive final
|
||||
struct IResourceArchive
|
||||
{
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
virtual THandle<void> LoadResource(const ResTypeID &resTypeID, int id) = 0;
|
||||
|
||||
virtual bool HasAnyResourcesOfType(const ResTypeID &resTypeID) const = 0;
|
||||
virtual bool FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const = 0;
|
||||
};
|
||||
|
||||
class ResourceArchiveBase : public IResourceArchive
|
||||
{
|
||||
public:
|
||||
static ResourceArchive *Create(ZipFileProxy *zipFileProxy, GpIOStream *stream);
|
||||
void Destroy();
|
||||
static const char *GetFileExtensionForResType(const ResTypeID &resTypeID, int &outValidationRule);
|
||||
};
|
||||
|
||||
THandle<void> LoadResource(const ResTypeID &resTypeID, int id);
|
||||
bool GetResourceSize(const ResTypeID &resTypeID, int id, size_t &outSize) const;
|
||||
class ResourceArchiveDirectory final : public ResourceArchiveBase
|
||||
{
|
||||
public:
|
||||
static ResourceArchiveDirectory *Create(VirtualDirectory_t directory, const PLPasStr &subdirectory);
|
||||
void Destroy() override;
|
||||
|
||||
bool HasAnyResourcesOfType(const ResTypeID &resTypeID) const;
|
||||
bool FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const;
|
||||
THandle<void> LoadResource(const ResTypeID &resTypeID, int id) override;
|
||||
|
||||
bool HasAnyResourcesOfType(const ResTypeID &resTypeID) const override;
|
||||
bool FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const override;
|
||||
|
||||
bool Init();
|
||||
|
||||
private:
|
||||
ResourceArchive(ZipFileProxy *zipFileProxy, GpIOStream *stream, ResourceArchiveRef *resourceHandles);
|
||||
~ResourceArchive();
|
||||
ResourceArchiveDirectory(VirtualDirectory_t directory, const PLPasStr &subdirectory);
|
||||
~ResourceArchiveDirectory();
|
||||
|
||||
struct ResTypeEntry
|
||||
{
|
||||
int32_t m_resTypeID;
|
||||
size_t m_firstRes;
|
||||
size_t m_lastRes;
|
||||
};
|
||||
|
||||
bool IndexResource(const ResTypeID &resTypeID, int id, size_t &outIndex) const;
|
||||
THandle<void> GetResource(const ResTypeID &resTypeID, int id, bool load);
|
||||
|
||||
static int ResTypeSearchPredicate(int32_t resTypeID, const ResTypeEntry &entry);
|
||||
static int ResIDSearchPredicate(int16_t resTypeID, int16_t entry);
|
||||
static bool ResTypeEntrySortPredicate(const ResTypeEntry &a, const ResTypeEntry &b);
|
||||
|
||||
VirtualDirectory_t m_directory;
|
||||
char m_subdirectory[256];
|
||||
|
||||
THandle<ResTypeEntry> m_resTypes;
|
||||
size_t m_numResourceTypes;
|
||||
|
||||
THandle<int16_t> m_resIDs;
|
||||
ResourceArchiveRef *m_resourceHandles;
|
||||
size_t m_numResources;
|
||||
};
|
||||
|
||||
class ResourceArchiveZipFile final : public ResourceArchiveBase
|
||||
{
|
||||
public:
|
||||
static ResourceArchiveZipFile *Create(ZipFileProxy *zipFileProxy, GpIOStream *stream);
|
||||
void Destroy() override;
|
||||
|
||||
THandle<void> LoadResource(const ResTypeID &resTypeID, int id) override;
|
||||
|
||||
bool HasAnyResourcesOfType(const ResTypeID &resTypeID) const override;
|
||||
bool FindFirstResourceOfType(const ResTypeID &resTypeID, int16_t &outID) const override;
|
||||
|
||||
private:
|
||||
ResourceArchiveZipFile(ZipFileProxy *zipFileProxy, GpIOStream *stream, ResourceArchiveRef *resourceHandles);
|
||||
~ResourceArchiveZipFile();
|
||||
|
||||
bool IndexResource(const ResTypeID &resTypeID, int id, size_t &outIndex, int &outValidationRule) const;
|
||||
|
||||
@@ -57,9 +115,9 @@ namespace PortabilityLayer
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual THandle<void> GetAppResource(const ResTypeID &resTypeID, int16_t resID) const = 0;
|
||||
virtual ResourceArchive *GetAppResourceArchive() const = 0;
|
||||
virtual IResourceArchive *GetAppResourceArchive() const = 0;
|
||||
|
||||
virtual ResourceArchive *LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const = 0;
|
||||
virtual IResourceArchive *LoadResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) const = 0;
|
||||
virtual PLError_t CreateBlankResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename) = 0;
|
||||
|
||||
virtual void DissociateHandle(MMHandleBlock *hdl) const = 0;
|
||||
|
@@ -3,7 +3,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
{
|
||||
ScanlineMaskBuilder::ScanlineMaskBuilder()
|
||||
: m_spans(nullptr)
|
||||
, m_numSpans(0)
|
||||
@@ -11,11 +11,11 @@ namespace PortabilityLayer
|
||||
, m_longestSpan(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ScanlineMaskBuilder::~ScanlineMaskBuilder()
|
||||
{
|
||||
if (m_spans)
|
||||
realloc(m_spans, 0);
|
||||
(void)realloc(m_spans, 0);
|
||||
}
|
||||
|
||||
bool ScanlineMaskBuilder::AppendSpan(size_t span)
|
||||
|
Reference in New Issue
Block a user