2020-11-11 20:06:52 +00:00
|
|
|
#include "Pomme.h"
|
|
|
|
#include "PommeFiles.h"
|
2021-07-18 11:16:43 +00:00
|
|
|
#include "Memory/BlockDescriptor.h"
|
2021-05-19 19:15:35 +00:00
|
|
|
#include "Utilities/bigendianstreams.h"
|
2020-11-11 20:06:52 +00:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <fstream>
|
|
|
|
#include <iostream>
|
2021-07-18 11:16:43 +00:00
|
|
|
#include <cstring>
|
2020-12-26 16:02:46 +00:00
|
|
|
#include "CompilerSupport/filesystem.h"
|
2020-11-11 20:06:52 +00:00
|
|
|
|
2021-05-20 06:19:02 +00:00
|
|
|
#if _DEBUG
|
|
|
|
#include "PommeSound.h"
|
|
|
|
#endif
|
|
|
|
|
2020-11-11 20:06:52 +00:00
|
|
|
#define LOG POMME_GENLOG(POMME_DEBUG_RESOURCES, "RSRC")
|
|
|
|
|
|
|
|
using namespace Pomme;
|
|
|
|
using namespace Pomme::Files;
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// State
|
|
|
|
|
|
|
|
static OSErr lastResError = noErr;
|
|
|
|
|
|
|
|
static std::vector<ResourceFork> rezSearchStack;
|
|
|
|
|
|
|
|
static int rezSearchStackIndex = 0;
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Internal
|
|
|
|
|
|
|
|
static void ResourceAssert(bool condition, const char* message)
|
|
|
|
{
|
|
|
|
if (!condition)
|
|
|
|
{
|
|
|
|
throw std::runtime_error(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static ResourceFork& GetCurRF()
|
|
|
|
{
|
|
|
|
return rezSearchStack[rezSearchStackIndex];
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Resource file management
|
|
|
|
|
|
|
|
OSErr ResError(void)
|
|
|
|
{
|
|
|
|
return lastResError;
|
|
|
|
}
|
|
|
|
|
|
|
|
short FSpOpenResFile(const FSSpec* spec, char permission)
|
|
|
|
{
|
|
|
|
short slot;
|
|
|
|
|
|
|
|
lastResError = FSpOpenRF(spec, permission, &slot);
|
|
|
|
|
|
|
|
if (noErr != lastResError)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto f = Pomme::BigEndianIStream(Pomme::Files::GetStream(slot));
|
|
|
|
auto resForkOff = f.Tell();
|
|
|
|
|
|
|
|
// ----------------
|
|
|
|
// Load resource fork
|
|
|
|
|
|
|
|
rezSearchStack.emplace_back();
|
|
|
|
rezSearchStackIndex = int(rezSearchStack.size() - 1);
|
|
|
|
GetCurRF().fileRefNum = slot;
|
|
|
|
GetCurRF().resourceMap.clear();
|
|
|
|
|
|
|
|
// -------------------
|
|
|
|
// Resource Header
|
|
|
|
UInt32 dataSectionOff = f.Read<UInt32>() + resForkOff;
|
|
|
|
UInt32 mapSectionOff = f.Read<UInt32>() + resForkOff;
|
|
|
|
f.Skip(4); // UInt32 dataSectionLen
|
|
|
|
f.Skip(4); // UInt32 mapSectionLen
|
|
|
|
f.Skip(112 + 128); // system- (112) and app- (128) reserved data
|
|
|
|
|
|
|
|
ResourceAssert(f.Tell() == dataSectionOff, "FSpOpenResFile: Unexpected data offset");
|
|
|
|
|
|
|
|
f.Goto(mapSectionOff);
|
|
|
|
|
|
|
|
// map header
|
|
|
|
f.Skip(16 + 4 + 2); // junk
|
|
|
|
f.Skip(2); // UInt16 fileAttr
|
|
|
|
UInt32 typeListOff = f.Read<UInt16>() + mapSectionOff;
|
|
|
|
UInt32 resNameListOff = f.Read<UInt16>() + mapSectionOff;
|
|
|
|
|
|
|
|
// all resource types
|
|
|
|
int nResTypes = 1 + f.Read<UInt16>();
|
|
|
|
for (int i = 0; i < nResTypes; i++)
|
|
|
|
{
|
|
|
|
OSType resType = f.Read<OSType>();
|
|
|
|
int resCount = f.Read<UInt16>() + 1;
|
|
|
|
UInt32 resRefListOff = f.Read<UInt16>() + typeListOff;
|
|
|
|
|
|
|
|
// The guard will rewind the file cursor to the pos in the next iteration
|
|
|
|
auto guard1 = f.GuardPos();
|
|
|
|
|
|
|
|
f.Goto(resRefListOff);
|
|
|
|
|
|
|
|
for (int j = 0; j < resCount; j++)
|
|
|
|
{
|
|
|
|
SInt16 resID = f.Read<UInt16>();
|
|
|
|
UInt16 resNameRelativeOff = f.Read<UInt16>();
|
|
|
|
UInt32 resPackedAttr = f.Read<UInt32>();
|
|
|
|
f.Skip(4); // junk
|
|
|
|
|
|
|
|
// The guard will rewind the file cursor to the pos in the next iteration
|
|
|
|
auto guard2 = f.GuardPos();
|
|
|
|
|
|
|
|
// unpack attributes
|
|
|
|
Byte resFlags = (resPackedAttr & 0xFF000000) >> 24;
|
|
|
|
UInt32 resDataOff = (resPackedAttr & 0x00FFFFFF) + dataSectionOff;
|
|
|
|
|
|
|
|
// Check compressed flag
|
|
|
|
ResourceAssert(!(resFlags & 1), "FSpOpenResFile: Compressed resources not supported yet");
|
|
|
|
|
|
|
|
// Fetch name
|
|
|
|
std::string name;
|
|
|
|
if (resNameRelativeOff != 0xFFFF)
|
|
|
|
{
|
|
|
|
f.Goto(resNameListOff + resNameRelativeOff);
|
|
|
|
name = f.ReadPascalString();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch size
|
|
|
|
f.Goto(resDataOff);
|
|
|
|
SInt32 size = f.Read<SInt32>();
|
|
|
|
|
|
|
|
ResourceMetadata resMetadata;
|
|
|
|
resMetadata.forkRefNum = slot;
|
|
|
|
resMetadata.type = resType;
|
|
|
|
resMetadata.id = resID;
|
|
|
|
resMetadata.flags = resFlags;
|
|
|
|
resMetadata.dataOffset = resDataOff + 4;
|
|
|
|
resMetadata.size = size;
|
|
|
|
resMetadata.name = name;
|
|
|
|
GetCurRF().resourceMap[resType][resID] = resMetadata;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//PrintStack(__func__);
|
|
|
|
|
|
|
|
return slot;
|
|
|
|
}
|
|
|
|
|
2021-02-22 19:20:47 +00:00
|
|
|
short OpenResFile(const char* cName)
|
|
|
|
{
|
|
|
|
FSSpec spec;
|
|
|
|
FSMakeFSSpec(0, 0, cName, &spec);
|
|
|
|
return FSpOpenResFile(&spec, fsRdPerm);
|
|
|
|
}
|
|
|
|
|
2020-11-11 20:06:52 +00:00
|
|
|
void UseResFile(short refNum)
|
|
|
|
{
|
|
|
|
// See MoreMacintoshToolbox:1-69
|
|
|
|
|
|
|
|
lastResError = unimpErr;
|
|
|
|
|
|
|
|
ResourceAssert(refNum != 0, "UseResFile: Using the System file's resource fork is not implemented.");
|
|
|
|
ResourceAssert(refNum >= 0, "UseResFile: Illegal refNum");
|
|
|
|
ResourceAssert(IsStreamOpen(refNum), "UseResFile: Resource stream not open");
|
|
|
|
|
|
|
|
for (size_t i = 0; i < rezSearchStack.size(); i++)
|
|
|
|
{
|
|
|
|
if (rezSearchStack[i].fileRefNum == refNum)
|
|
|
|
{
|
|
|
|
lastResError = noErr;
|
|
|
|
rezSearchStackIndex = i;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cerr << "no RF open with refNum " << rfNumErr << "\n";
|
|
|
|
lastResError = rfNumErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
short CurResFile()
|
|
|
|
{
|
|
|
|
return GetCurRF().fileRefNum;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CloseResFile(short refNum)
|
|
|
|
{
|
|
|
|
ResourceAssert(refNum != 0, "CloseResFile: Closing the System file's resource fork is not implemented.");
|
|
|
|
ResourceAssert(refNum >= 0, "CloseResFile: Illegal refNum");
|
|
|
|
ResourceAssert(IsStreamOpen(refNum), "CloseResFile: Resource stream not open");
|
|
|
|
|
|
|
|
//UpdateResFile(refNum); // MMT:1-110
|
|
|
|
Pomme::Files::CloseStream(refNum);
|
|
|
|
|
|
|
|
auto it = rezSearchStack.begin();
|
|
|
|
while (it != rezSearchStack.end())
|
|
|
|
{
|
|
|
|
if (it->fileRefNum == refNum)
|
|
|
|
it = rezSearchStack.erase(it);
|
|
|
|
else
|
|
|
|
it++;
|
|
|
|
}
|
|
|
|
|
|
|
|
rezSearchStackIndex = std::min(rezSearchStackIndex, (int) rezSearchStack.size() - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
short Count1Resources(ResType theType)
|
|
|
|
{
|
|
|
|
lastResError = noErr;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
return (short) GetCurRF().resourceMap.at(theType).size();
|
|
|
|
}
|
|
|
|
catch (std::out_of_range&)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-18 11:16:43 +00:00
|
|
|
short Count1Types()
|
|
|
|
{
|
|
|
|
return (short) GetCurRF().resourceMap.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Get1IndType(ResType* theType, short index)
|
|
|
|
{
|
|
|
|
const auto& resourceMap = GetCurRF().resourceMap;
|
|
|
|
|
|
|
|
for (auto& it : resourceMap)
|
|
|
|
{
|
|
|
|
if (index == 1) // remember, index is 1-based here
|
|
|
|
{
|
|
|
|
*theType = it.first;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
index--;
|
|
|
|
}
|
|
|
|
|
|
|
|
*theType = 0;
|
|
|
|
}
|
|
|
|
|
2020-11-11 20:06:52 +00:00
|
|
|
Handle GetResource(ResType theType, short theID)
|
|
|
|
{
|
|
|
|
lastResError = noErr;
|
|
|
|
|
|
|
|
for (int i = rezSearchStackIndex; i >= 0; i--)
|
|
|
|
{
|
|
|
|
const auto& fork = rezSearchStack[i];
|
|
|
|
|
|
|
|
if (fork.resourceMap.end() == fork.resourceMap.find(theType))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
auto& resourcesOfType = fork.resourceMap.at(theType);
|
|
|
|
if (resourcesOfType.end() == resourcesOfType.find(theID))
|
|
|
|
continue;
|
|
|
|
|
2021-07-18 11:16:43 +00:00
|
|
|
// Found it!
|
2020-11-11 20:06:52 +00:00
|
|
|
const auto& meta = fork.resourceMap.at(theType).at(theID);
|
|
|
|
auto& forkStream = Pomme::Files::GetStream(rezSearchStack[i].fileRefNum);
|
2021-07-18 11:16:43 +00:00
|
|
|
|
|
|
|
// Allocate handle
|
2020-11-11 20:06:52 +00:00
|
|
|
Handle handle = NewHandle(meta.size);
|
2021-07-18 11:16:43 +00:00
|
|
|
|
|
|
|
// Set pointer to resource metadata
|
|
|
|
Pomme::Memory::BlockDescriptor::HandleToBlock(handle)->rezMeta = &meta;
|
|
|
|
|
2020-11-11 20:06:52 +00:00
|
|
|
forkStream.seekg(meta.dataOffset, std::ios::beg);
|
|
|
|
forkStream.read(*handle, meta.size);
|
2020-12-26 16:02:46 +00:00
|
|
|
|
2020-11-11 20:06:52 +00:00
|
|
|
return handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
lastResError = resNotFound;
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2021-07-18 11:16:43 +00:00
|
|
|
Handle Get1IndResource(ResType theType, short index)
|
|
|
|
{
|
|
|
|
lastResError = noErr;
|
|
|
|
|
|
|
|
const auto& idsToResources = GetCurRF().resourceMap.at(theType);
|
|
|
|
|
|
|
|
for (auto& it : idsToResources)
|
|
|
|
{
|
|
|
|
if (index == 1) // remember, index is 1-based here
|
|
|
|
{
|
|
|
|
return GetResource(theType, it.second.id);
|
|
|
|
}
|
|
|
|
|
|
|
|
index--;
|
|
|
|
}
|
|
|
|
|
|
|
|
lastResError = resNotFound;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GetResInfo(Handle theResource, short* theID, ResType* theType, char* name256)
|
|
|
|
{
|
|
|
|
lastResError = noErr;
|
|
|
|
|
|
|
|
if (!theResource)
|
|
|
|
{
|
|
|
|
lastResError = resNotFound;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto blockDescriptor = Pomme::Memory::BlockDescriptor::HandleToBlock(theResource);
|
|
|
|
|
|
|
|
if (!blockDescriptor->rezMeta)
|
|
|
|
{
|
|
|
|
lastResError = resNotFound;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (theID)
|
|
|
|
*theID = blockDescriptor->rezMeta->id;
|
|
|
|
|
|
|
|
if (theType)
|
|
|
|
*theType = blockDescriptor->rezMeta->type;
|
|
|
|
|
|
|
|
if (name256)
|
|
|
|
snprintf(name256, 256, "%s", blockDescriptor->rezMeta->name.c_str());
|
|
|
|
}
|
|
|
|
|
2020-11-11 20:06:52 +00:00
|
|
|
void ReleaseResource(Handle theResource)
|
|
|
|
{
|
|
|
|
DisposeHandle(theResource);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RemoveResource(Handle theResource)
|
|
|
|
{
|
|
|
|
DisposeHandle(theResource);
|
|
|
|
TODO();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AddResource(Handle theData, ResType theType, short theID, const char* name)
|
|
|
|
{
|
|
|
|
TODO();
|
|
|
|
}
|
|
|
|
|
2021-02-22 19:20:47 +00:00
|
|
|
void ChangedResource(Handle theResource)
|
|
|
|
{
|
|
|
|
TODO();
|
|
|
|
}
|
|
|
|
|
2020-11-11 20:06:52 +00:00
|
|
|
void WriteResource(Handle theResource)
|
|
|
|
{
|
|
|
|
TODO();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DetachResource(Handle theResource)
|
|
|
|
{
|
|
|
|
lastResError = noErr;
|
2021-07-18 11:16:43 +00:00
|
|
|
|
|
|
|
auto* blockDescriptor = Pomme::Memory::BlockDescriptor::HandleToBlock(theResource);
|
|
|
|
|
|
|
|
if (!blockDescriptor->rezMeta)
|
|
|
|
lastResError = resNotFound;
|
|
|
|
|
|
|
|
blockDescriptor->rezMeta = nullptr;
|
2020-11-11 20:06:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
long GetResourceSizeOnDisk(Handle theResource)
|
|
|
|
{
|
|
|
|
TODO();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
long SizeResource(Handle theResource)
|
|
|
|
{
|
|
|
|
return GetResourceSizeOnDisk(theResource);
|
|
|
|
}
|