Parse project chunks

This still doesn't show the project on-screen when opening, but it does
parse the chunks inside the project file.
This commit is contained in:
Dietrich Epp 2023-05-06 22:24:50 -04:00
parent 4cf55bffbc
commit 4a99288be2
3 changed files with 110 additions and 7 deletions

View File

@ -17,6 +17,8 @@ typedef enum ErrorCode {
kErrCouldNotReadProject, kErrCouldNotReadProject,
// The project file is damaged. // The project file is damaged.
kErrProjectDamaged, kErrProjectDamaged,
// The project data is too large.
kErrProjectLarge,
// The project file is from an unknown version of SyncFiles. // The project file is from an unknown version of SyncFiles.
kErrProjectUnknownVersion, kErrProjectUnknownVersion,
// Could not query volume parameters. // Could not query volume parameters.

View File

@ -48,6 +48,7 @@ boundary. This is just to make it easier to read hexdumps.
#define kVersion 0x10000 #define kVersion 0x10000
#define kMaxChunkCount 32 #define kMaxChunkCount 32
#define kMaxChunkSize (4 * 1024)
// clang-format off // clang-format off
@ -79,6 +80,9 @@ struct ProjectChunk {
UInt32 crc32; UInt32 crc32;
}; };
static const UInt32 kProjectAliasChunks[2] = {'LOCA', 'REMA'};
static const UInt32 kProjectPathChunks[2] = {'LOCP', 'REMP'};
// Dimensions of controls. // Dimensions of controls.
enum { enum {
kVBorder = 10, kVBorder = 10,
@ -211,6 +215,73 @@ static void ProjectReadError(ProjectHandle project, ErrorCode err, OSErr osErr)
ShowError(kErrCouldNotReadProject, err, osErr, name); ShowError(kErrCouldNotReadProject, err, osErr, name);
} }
typedef enum ProjectChunkStatus {
kChunkNotFound,
kChunkFound,
kChunkError,
} ProjectChunkStatus;
static ProjectChunkStatus ProjectReadChunk(ProjectHandle project, int nchunks,
struct ProjectChunk **chunks,
UInt32 chunkID, Handle *dataPtr,
long *sizePtr)
{
struct ProjectChunk *chunk, *end;
UInt32 offset, size, crc32, gotCRC32;
long count;
Handle h;
OSErr osErr;
short fileRef;
chunk = *chunks;
end = chunk + nchunks;
for (; chunk < end; chunk++) {
if (chunk->id == chunkID) {
goto found;
}
}
return kChunkNotFound;
found:
offset = chunk->offset;
size = chunk->size;
crc32 = chunk->crc32;
if (size > kMaxChunkSize) {
ProjectReadError(project, kErrProjectLarge, 0);
return kChunkError;
}
h = NewHandle(size);
if (h == NULL) {
ProjectReadError(project, kErrOutOfMemory, MemError());
return kChunkError;
}
if (size > 0) {
fileRef = (*project)->fileRef;
osErr = SetFPos(fileRef, fsFromStart, offset);
if (osErr != 0) {
DisposeHandle(h);
ProjectReadError(project, kErrNone, osErr);
return kChunkError;
}
count = size;
osErr = FSRead(fileRef, &count, *h);
if (osErr != 0) {
DisposeHandle(h);
ProjectReadError(project, kErrNone, osErr);
return kChunkError;
}
}
gotCRC32 = CRC32Update(0, *h, size);
if (crc32 != gotCRC32) {
DisposeHandle(h);
ProjectReadError(project, kErrProjectDamaged, 0);
return kChunkError;
}
*dataPtr = h;
*sizePtr = size;
return kChunkFound;
}
// ProjectRead reads the project from disk. Returns true on success. // ProjectRead reads the project from disk. Returns true on success.
static Boolean ProjectRead(ProjectHandle project) static Boolean ProjectRead(ProjectHandle project)
{ {
@ -220,8 +291,10 @@ static Boolean ProjectRead(ProjectHandle project)
long count, size; long count, size;
ErrorCode err; ErrorCode err;
OSErr osErr; OSErr osErr;
int nchunks; int nchunks, i;
UInt32 headerCRC, gotCRC; UInt32 headerCRC, gotCRC;
Handle chunkData;
ProjectChunkStatus chunkStatus;
chunks = NULL; chunks = NULL;
err = kErrNone; err = kErrNone;
@ -238,11 +311,14 @@ static Boolean ProjectRead(ProjectHandle project)
goto error; goto error;
} }
if (count < sizeof(header) || if (count < sizeof(header) ||
memcmp(header.magic, kMagic, sizeof(kMagic)) != 0 || memcmp(header.magic, kMagic, sizeof(kMagic)) != 0) {
header.chunkCount > kMaxChunkCount) {
err = kErrProjectDamaged; err = kErrProjectDamaged;
goto error; goto error;
} }
if (header.chunkCount > kMaxChunkCount) {
err = kErrProjectLarge;
goto error;
}
headerCRC = header.crc32; headerCRC = header.crc32;
header.crc32 = 0; header.crc32 = 0;
gotCRC = CRC32Update(0, &header, sizeof(header)); gotCRC = CRC32Update(0, &header, sizeof(header));
@ -271,7 +347,28 @@ static Boolean ProjectRead(ProjectHandle project)
goto error; goto error;
} }
// TODO: read project data for (i = 0; i < 2; i++) {
chunkStatus =
ProjectReadChunk(project, nchunks, chunks, kProjectAliasChunks[i],
&chunkData, &size);
if (chunkStatus == kChunkError) {
goto errorSilent;
}
if (chunkStatus == kChunkFound) {
(*project)->dirs[i].alias = (AliasHandle)chunkData;
// FIXME: Resolve the alias.
continue;
}
chunkStatus = ProjectReadChunk(
project, nchunks, chunks, kProjectPathChunks[i], &chunkData, &size);
if (chunkStatus == kChunkError) {
goto errorSilent;
}
if (chunkStatus == kChunkFound) {
(*project)->dirs[i].pathLength = size;
(*project)->dirs[i].path = chunkData;
}
}
if (chunks != NULL) { if (chunks != NULL) {
DisposeHandle((Handle)chunks); DisposeHandle((Handle)chunks);
@ -284,6 +381,12 @@ error:
} }
ProjectReadError(project, err, osErr); ProjectReadError(project, err, osErr);
return false; return false;
errorSilent:
if (chunks != NULL) {
DisposeHandle((Handle)chunks);
}
return false;
} }
void ProjectOpen(FSSpec *spec, ScriptCode script) void ProjectOpen(FSSpec *spec, ScriptCode script)
@ -366,9 +469,6 @@ static OSErr ProjectWriteHeader(short refNum, int nchunks,
return err; return err;
} }
static const UInt32 kProjectAliasChunks[2] = {'LOCA', 'REMA'};
static const UInt32 kProjectPathChunks[2] = {'LOCP', 'REMP'};
static OSErr ProjectFlush(short refNum) static OSErr ProjectFlush(short refNum)
{ {
ParamBlockRec pb; ParamBlockRec pb;

View File

@ -149,6 +149,7 @@ resource 'STR#' (rSTRS_Errors) {{
"Could not save project “^1”.", "Could not save project “^1”.",
"Could not read project “^1”.", "Could not read project “^1”.",
"The project file is damaged.", "The project file is damaged.",
"The project data is too large.",
"The project file is from an unknown version of SyncFiles.", "The project file is from an unknown version of SyncFiles.",
"Could not query volume parameters.", "Could not query volume parameters.",
"Could not create alias.", "Could not create alias.",