RGSSAD: Add RGSS3 archive reader

Everything except the entry table parsing is the same as RGSS1.
This commit is contained in:
Jonas Kulla 2014-07-24 00:43:04 +02:00
parent 6726493ee7
commit 2f831aea60
3 changed files with 168 additions and 42 deletions

View File

@ -340,6 +340,7 @@ FileSystem::FileSystem(const char *argv0,
PHYSFS_registerArchiver(&RGSS1_Archiver); PHYSFS_registerArchiver(&RGSS1_Archiver);
PHYSFS_registerArchiver(&RGSS2_Archiver); PHYSFS_registerArchiver(&RGSS2_Archiver);
PHYSFS_registerArchiver(&RGSS3_Archiver);
if (allowSymlinks) if (allowSymlinks)
PHYSFS_permitSymbolicLinks(1); PHYSFS_permitSymbolicLinks(1);

View File

@ -79,14 +79,14 @@ readUint32(PHYSFS_Io *io, uint32_t &result)
return (count == 4); return (count == 4);
} }
#define RGSS_HEADER_1 0x53534752 #define RGSS_HEADER "RGSSAD"
#define RGSS_HEADER_2 0x01004441
#define RGSS_MAGIC 0xDEADCAFE #define RGSS_MAGIC 0xDEADCAFE
#define PHYSFS_ALLOC(type) \ #define PHYSFS_ALLOC(type) \
static_cast<type*>(PHYSFS_getAllocator()->Malloc(sizeof(type))) static_cast<type*>(PHYSFS_getAllocator()->Malloc(sizeof(type)))
#define IO_READ(io, dest, size) (io->read(io, dest, size) == size)
static inline uint32_t static inline uint32_t
advanceMagic(uint32_t &magic) advanceMagic(uint32_t &magic)
{ {
@ -278,22 +278,65 @@ static const PHYSFS_Io RGSS_IoTemplate =
RGSS_ioDestroy RGSS_ioDestroy
}; };
static void
processDirectories(RGSS_archiveData *data, BoostSet<std::string> &topLevel,
char *nameBuf, uint32_t nameLen)
{
/* Check for top level entries */
for (uint32_t i = 0; i < nameLen; ++i)
{
bool slash = nameBuf[i] == '/';
if (!slash && i+1 < nameLen)
continue;
if (slash)
nameBuf[i] = '\0';
topLevel.insert(nameBuf);
if (slash)
nameBuf[i] = '/';
}
/* Check for more entries */
for (uint32_t i = nameLen; i > 0; i--)
if (nameBuf[i] == '/')
{
nameBuf[i] = '\0';
const char *dir = nameBuf;
const char *entry = &nameBuf[i+1];
BoostSet<std::string> &entryList = data->dirHash[dir];
entryList.insert(entry);
}
}
static bool
verifyHeader(PHYSFS_Io *io, char version)
{
char header[8];
if (!IO_READ(io, header, sizeof(header)))
return false;
if (strcmp(header, RGSS_HEADER))
return false;
if (header[7] != version)
return false;
return true;
}
static void* static void*
RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite) RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite)
{ {
if (forWrite) if (forWrite)
return 0; return 0;
/* Check header */ /* Version 1 */
uint32_t header1, header2; if (!verifyHeader(io, 1))
if (!readUint32(io, header1))
return 0;
if (!readUint32(io, header2))
return 0;
if (header1 != RGSS_HEADER_1 || header2 != RGSS_HEADER_2)
return 0; return 0;
RGSS_archiveData *data = new RGSS_archiveData; RGSS_archiveData *data = new RGSS_archiveData;
@ -337,35 +380,7 @@ RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite)
entry.startMagic = magic; entry.startMagic = magic;
data->entryHash.insert(nameBuf, entry); data->entryHash.insert(nameBuf, entry);
processDirectories(data, topLevel, nameBuf, nameLen);
/* Check for top level entries */
for (i = 0; i < nameLen; ++i)
{
bool slash = nameBuf[i] == '/';
if (!slash && i+1 < nameLen)
continue;
if (slash)
nameBuf[i] = '\0';
topLevel.insert(nameBuf);
if (slash)
nameBuf[i] = '/';
}
/* Check for more entries */
for (i = nameLen; i > 0; i--)
if (nameBuf[i] == '/')
{
nameBuf[i] = '\0';
const char *dir = nameBuf;
const char *entry = &nameBuf[i+1];
BoostSet<std::string> &entryList = data->dirHash[dir];
entryList.insert(entry);
}
io->seek(io, entry.offset + entry.size); io->seek(io, entry.offset + entry.size);
} }
@ -507,3 +522,112 @@ const PHYSFS_Archiver RGSS2_Archiver =
RGSS_stat, RGSS_stat,
RGSS_closeArchive RGSS_closeArchive
}; };
static bool
readUint32AndXor(PHYSFS_Io *io, uint32_t &result, uint32_t key)
{
if (!readUint32(io, result))
return false;
result ^= key;
return true;
}
static void*
RGSS3_openArchive(PHYSFS_Io *io, const char *, int forWrite)
{
if (forWrite)
return 0;
/* Version 3 */
if (!verifyHeader(io, 3))
return 0;
uint32_t baseMagic;
if (!readUint32(io, baseMagic))
return 0;
baseMagic = (baseMagic * 9) + 3;
RGSS_archiveData *data = new RGSS_archiveData;
data->archiveIo = io;
/* Top level entry list */
BoostSet<std::string> &topLevel = data->dirHash[""];
while (true)
{
uint32_t offset, size, magic, nameLen;
if (!readUint32AndXor(io, offset, baseMagic))
goto error;
/* Zero offset means entry list has ended */
if (offset == 0)
break;
if (!readUint32AndXor(io, size, baseMagic))
goto error;
if (!readUint32AndXor(io, magic, baseMagic))
goto error;
if (!readUint32AndXor(io, nameLen, baseMagic))
goto error;
char nameBuf[512];
if (!IO_READ(io, nameBuf, nameLen))
goto error;
uint32_t i;
for (i = 0; i < nameLen; ++i)
{
nameBuf[i] ^= ((baseMagic >> 8*(i%4)) & 0xFF);
if (nameBuf[i] == '\\')
nameBuf[i] = '/';
}
nameBuf[i] = '\0';
RGSS_entryData entry;
entry.offset = offset;
entry.size = size;
entry.startMagic = magic;
data->entryHash.insert(nameBuf, entry);
processDirectories(data, topLevel, nameBuf, nameLen);
continue;
error:
delete data;
return 0;
}
return data;
}
const PHYSFS_Archiver RGSS3_Archiver =
{
0,
{
"RGSS3A",
"RGSS3 encrypted archive format",
"", /* Author */
"", /* Website */
0 /* symlinks not supported */
},
RGSS3_openArchive,
RGSS_enumerateFiles,
RGSS_openRead,
RGSS_noop1, /* openWrite */
RGSS_noop1, /* openAppend */
RGSS_noop2, /* remove */
RGSS_noop2, /* mkdir */
RGSS_stat,
RGSS_closeArchive
};

View File

@ -26,5 +26,6 @@
extern const PHYSFS_Archiver RGSS1_Archiver; extern const PHYSFS_Archiver RGSS1_Archiver;
extern const PHYSFS_Archiver RGSS2_Archiver; extern const PHYSFS_Archiver RGSS2_Archiver;
extern const PHYSFS_Archiver RGSS3_Archiver;
#endif // RGSSAD_H #endif // RGSSAD_H