mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-12-14 12:09:36 +00:00
Lots of stuff
This commit is contained in:
528
MiniRez/MiniRez.cpp
Normal file
528
MiniRez/MiniRez.cpp
Normal file
@@ -0,0 +1,528 @@
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
|
||||
#include "BytePack.h"
|
||||
#include "CFileStream.h"
|
||||
#include "IOStream.h"
|
||||
|
||||
#include "MacBinary2.h"
|
||||
#include "MacFileMem.h"
|
||||
|
||||
// Very simplified resource compiler
|
||||
|
||||
using namespace PortabilityLayer;
|
||||
|
||||
struct SimpleResource
|
||||
{
|
||||
char m_type[4];
|
||||
int m_resourceID;
|
||||
std::string m_name;
|
||||
std::vector<uint8_t> m_resourceData;
|
||||
size_t m_offsetInResData;
|
||||
uint8_t m_attributes;
|
||||
};
|
||||
|
||||
struct ResCatalogData
|
||||
{
|
||||
};
|
||||
|
||||
bool IsWhitespace(char c)
|
||||
{
|
||||
return c <= ' ';
|
||||
}
|
||||
|
||||
void AppendUInt16(std::vector<uint8_t> &vec, uint16_t value)
|
||||
{
|
||||
uint8_t encoded[2];
|
||||
BytePack::BigUInt16(encoded, value);
|
||||
vec.push_back(encoded[0]);
|
||||
vec.push_back(encoded[1]);
|
||||
}
|
||||
|
||||
void AppendInt16(std::vector<uint8_t> &vec, int16_t value)
|
||||
{
|
||||
AppendUInt16(vec, static_cast<uint16_t>(value));
|
||||
}
|
||||
|
||||
void AppendUInt32(std::vector<uint8_t> &vec, uint32_t value)
|
||||
{
|
||||
uint8_t encoded[4];
|
||||
BytePack::BigUInt32(encoded, value);
|
||||
vec.push_back(encoded[0]);
|
||||
vec.push_back(encoded[1]);
|
||||
vec.push_back(encoded[2]);
|
||||
vec.push_back(encoded[3]);
|
||||
}
|
||||
|
||||
uint32_t TypeToU32(const char *resType)
|
||||
{
|
||||
uint32_t v = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
v |= static_cast<uint8_t>(resType[i]) << (24 - i * 8);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
bool FindNextNonWhitespace(CFileStream &fs, char &nextChar)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
char c;
|
||||
if (!fs.Read(&c, 1))
|
||||
return false;
|
||||
|
||||
if (!IsWhitespace(c))
|
||||
{
|
||||
nextChar = c;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string HandleEscapes(const std::string &str)
|
||||
{
|
||||
const size_t len = str.size();
|
||||
|
||||
std::string processed;
|
||||
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
const char c = str[i];
|
||||
|
||||
if (c == '\\' && len - i >= 5 && str[i + 1] == '0' && str[i + 2] == 'x')
|
||||
{
|
||||
const char highNibbleChar = str[i + 3];
|
||||
const char lowNibbleChar = str[i + 4];
|
||||
|
||||
int highNibble = highNibbleChar - '0';
|
||||
int lowNibble = lowNibbleChar - '0';
|
||||
|
||||
const char reconstructed = (highNibble << 4) | lowNibble;
|
||||
processed.append(&reconstructed, 1);
|
||||
|
||||
i += 4;
|
||||
}
|
||||
else
|
||||
processed.append(&c, 1);
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
void DefError()
|
||||
{
|
||||
fprintf(stderr, "Malformed def");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: MiniRez <input.r> <output.bin>");
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *f = nullptr;
|
||||
if (fopen_s(&f, argv[1], "rb"))
|
||||
{
|
||||
fprintf(stderr, "Failed to open input file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
CFileStream fs(f, true, false, true);
|
||||
|
||||
int defsParsed = 0;
|
||||
|
||||
std::vector<SimpleResource*> resources;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
printf("%i defs parsed...\n", defsParsed);
|
||||
defsParsed++;
|
||||
UFilePos_t defStartOffset = fs.Tell();
|
||||
|
||||
char nextChar = 0;
|
||||
if (!FindNextNonWhitespace(fs, nextChar))
|
||||
break;
|
||||
|
||||
if (IsWhitespace(nextChar))
|
||||
continue;
|
||||
|
||||
if (nextChar == 'd')
|
||||
{
|
||||
char nextChars[3];
|
||||
if (fs.Read(nextChars, 3) != 3 || nextChars[0] != 'a' || nextChars[1] != 't' || nextChars[2] != 'a')
|
||||
{
|
||||
fprintf(stderr, "Unknown token");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!FindNextNonWhitespace(fs, nextChar) || nextChar != '\'')
|
||||
DefError();
|
||||
|
||||
char resType[5];
|
||||
if (!fs.Read(resType, 5) || resType[4] != '\'')
|
||||
DefError();
|
||||
|
||||
if (!FindNextNonWhitespace(fs, nextChar) || nextChar != '(')
|
||||
DefError();
|
||||
|
||||
int resID = 0;
|
||||
bool hasProperty = false;
|
||||
bool isNegative = false;
|
||||
for (;;)
|
||||
{
|
||||
if (!fs.Read(&nextChar, 1))
|
||||
DefError();
|
||||
|
||||
if (nextChar == ')')
|
||||
break;
|
||||
else if (nextChar == ',')
|
||||
{
|
||||
hasProperty = true;
|
||||
break;
|
||||
}
|
||||
else if (nextChar == '-')
|
||||
isNegative = true;
|
||||
else if (nextChar >= '0' && nextChar <= '9')
|
||||
resID = resID * 10 + (nextChar - '0');
|
||||
else
|
||||
DefError();
|
||||
}
|
||||
|
||||
std::string resName;
|
||||
std::string propName;
|
||||
std::vector<uint8_t> resData;
|
||||
|
||||
if (hasProperty)
|
||||
{
|
||||
char singleCharStr[2];
|
||||
singleCharStr[1] = '\0';
|
||||
|
||||
if (!FindNextNonWhitespace(fs, singleCharStr[0]))
|
||||
DefError();
|
||||
|
||||
|
||||
if (singleCharStr[0] == '\"')
|
||||
{
|
||||
hasProperty = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!fs.Read(singleCharStr, 1))
|
||||
DefError();
|
||||
|
||||
if (singleCharStr[0] == '"')
|
||||
break;
|
||||
|
||||
resName += singleCharStr;
|
||||
}
|
||||
|
||||
if (!FindNextNonWhitespace(fs, nextChar))
|
||||
DefError();
|
||||
|
||||
if (nextChar == ')')
|
||||
{
|
||||
}
|
||||
else if (nextChar == ',')
|
||||
{
|
||||
hasProperty = true;
|
||||
if (!FindNextNonWhitespace(fs, singleCharStr[0]))
|
||||
DefError();
|
||||
}
|
||||
else
|
||||
DefError();
|
||||
}
|
||||
|
||||
if (hasProperty)
|
||||
{
|
||||
propName = singleCharStr;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!fs.Read(singleCharStr, 1))
|
||||
DefError();
|
||||
|
||||
if (singleCharStr[0] == ')')
|
||||
break;
|
||||
|
||||
propName += singleCharStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!FindNextNonWhitespace(fs, nextChar) || nextChar != '{')
|
||||
DefError();
|
||||
|
||||
// Data item parse loop
|
||||
for (;;)
|
||||
{
|
||||
if (!FindNextNonWhitespace(fs, nextChar))
|
||||
DefError();
|
||||
|
||||
if (nextChar == '$')
|
||||
{
|
||||
// Res data bytes
|
||||
uint8_t b = 0;
|
||||
bool highNibble = true;
|
||||
|
||||
if (!fs.Read(&nextChar, 1) || nextChar != '\"')
|
||||
DefError();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!fs.Read(&nextChar, 1))
|
||||
DefError();
|
||||
|
||||
uint8_t fragment = 0;
|
||||
|
||||
if (nextChar == '\"')
|
||||
break;
|
||||
else if (nextChar >= '0' && nextChar <= '9')
|
||||
fragment = nextChar - '0';
|
||||
else if (nextChar >= 'a' && nextChar <= 'f')
|
||||
fragment = nextChar - 'a' + 0xa;
|
||||
else if (nextChar >= 'A' && nextChar <= 'F')
|
||||
fragment = nextChar - 'A' + 0xa;
|
||||
else if (nextChar == ' ')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
DefError();
|
||||
|
||||
if (highNibble)
|
||||
{
|
||||
b = fragment << 4;
|
||||
highNibble = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
resData.push_back(b | fragment);
|
||||
highNibble = true;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (nextChar == '}')
|
||||
break;
|
||||
else if (nextChar == '/')
|
||||
{
|
||||
if (!fs.Read(&nextChar, 1))
|
||||
DefError();
|
||||
|
||||
if (nextChar == '/')
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (!fs.Read(&nextChar, 1))
|
||||
DefError();
|
||||
|
||||
if (nextChar == '\r' || nextChar == '\n')
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (nextChar == '*')
|
||||
{
|
||||
char prevChar = nextChar;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!fs.Read(&nextChar, 1))
|
||||
DefError();
|
||||
|
||||
if (nextChar == '/' && prevChar == '*')
|
||||
break;
|
||||
|
||||
prevChar = nextChar;
|
||||
}
|
||||
}
|
||||
else
|
||||
DefError();
|
||||
}
|
||||
else
|
||||
DefError();
|
||||
}
|
||||
|
||||
if (!FindNextNonWhitespace(fs, nextChar) || nextChar != ';')
|
||||
DefError();
|
||||
|
||||
SimpleResource *res = new SimpleResource();
|
||||
res->m_name = HandleEscapes(resName);
|
||||
res->m_resourceID = resID;
|
||||
memcpy(res->m_type, resType, 4);
|
||||
res->m_resourceData.swap(resData);
|
||||
res->m_attributes = 0;
|
||||
|
||||
if (propName == "purgeable")
|
||||
res->m_attributes |= (1 << 5);
|
||||
|
||||
|
||||
resources.push_back(res);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Unknown token");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> resourceForkData;
|
||||
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
resourceForkData.push_back(0);
|
||||
|
||||
size_t resForkDataStart = resourceForkData.size();
|
||||
|
||||
std::vector<uint32_t> uniqueResTypes;
|
||||
std::vector<unsigned int> resTypeCounts;
|
||||
std::vector<std::string> uniqueResNames;
|
||||
|
||||
printf("Re-emitting defs...\n");
|
||||
for (size_t i = 0; i < resources.size(); i++)
|
||||
{
|
||||
SimpleResource *res = resources[i];
|
||||
|
||||
res->m_offsetInResData = resourceForkData.size() - resForkDataStart;
|
||||
|
||||
uint8_t packedLen[4];
|
||||
BytePack::BigUInt32(packedLen, static_cast<uint32_t>(res->m_resourceData.size()));
|
||||
|
||||
for (int j = 0; j < 4; j++)
|
||||
resourceForkData.push_back(packedLen[j]);
|
||||
|
||||
for (size_t j = 0; j < res->m_resourceData.size(); j++)
|
||||
resourceForkData.push_back(res->m_resourceData[j]);
|
||||
|
||||
uint32_t resTypeCode = TypeToU32(res->m_type);
|
||||
std::vector<uint32_t>::iterator uniqueResTypeIndex = std::find(uniqueResTypes.begin(), uniqueResTypes.end(), resTypeCode);
|
||||
|
||||
if (uniqueResTypeIndex == uniqueResTypes.end())
|
||||
{
|
||||
uniqueResTypes.push_back(resTypeCode);
|
||||
resTypeCounts.push_back(1);
|
||||
}
|
||||
else
|
||||
resTypeCounts[uniqueResTypeIndex - uniqueResTypes.begin()]++;
|
||||
|
||||
if (!res->m_name.empty())
|
||||
{
|
||||
if (std::find(uniqueResNames.begin(), uniqueResNames.end(), res->m_name) == uniqueResNames.end())
|
||||
uniqueResNames.push_back(res->m_name);
|
||||
}
|
||||
}
|
||||
|
||||
const size_t resDataSize = resourceForkData.size() - resForkDataStart;
|
||||
const size_t resMapPos = resourceForkData.size();
|
||||
|
||||
// Reserved space for resource header copy (16), handle to next res map (4), file ref number (2)
|
||||
for (size_t j = 0; j < 22; j++)
|
||||
resourceForkData.push_back(0);
|
||||
|
||||
uint16_t resForkAttributes = 0; // We don't use any of these
|
||||
|
||||
const size_t typeListEntrySize = 8;
|
||||
const size_t refListEntrySize = 12;
|
||||
|
||||
const size_t resourceTypeListStartLoc = 28;
|
||||
const size_t resourceTypeListSize = 2 + uniqueResTypes.size() * typeListEntrySize;
|
||||
const size_t resourceRefListStartLoc = resourceTypeListStartLoc + resourceTypeListSize;
|
||||
const size_t resourceNameListStartLoc = resourceRefListStartLoc + resources.size() * refListEntrySize;
|
||||
|
||||
AppendUInt16(resourceForkData, resForkAttributes);
|
||||
AppendUInt16(resourceForkData, static_cast<uint16_t>(resourceTypeListStartLoc));
|
||||
AppendUInt16(resourceForkData, static_cast<uint16_t>(resourceNameListStartLoc));
|
||||
AppendUInt16(resourceForkData, static_cast<uint16_t>(uniqueResTypes.size() - 1));
|
||||
|
||||
std::vector<size_t> refListStartForType;
|
||||
refListStartForType.resize(resTypeCounts.size());
|
||||
|
||||
refListStartForType[0] = 0;
|
||||
for (size_t i = 1; i < refListStartForType.size(); i++)
|
||||
refListStartForType[i] = refListStartForType[i - 1] + resTypeCounts[i - 1];
|
||||
|
||||
std::vector<size_t> nameListStarts;
|
||||
nameListStarts.resize(uniqueResNames.size());
|
||||
|
||||
nameListStarts[0] = 0;
|
||||
for (size_t i = 1; i < nameListStarts.size(); i++)
|
||||
nameListStarts[i] = nameListStarts[i - 1] + uniqueResNames[i - 1].size() + 1;
|
||||
|
||||
// Write resource type list
|
||||
for (size_t i = 0; i < uniqueResTypes.size(); i++)
|
||||
{
|
||||
AppendUInt32(resourceForkData, uniqueResTypes[i]);
|
||||
AppendUInt16(resourceForkData, resTypeCounts[i] - 1);
|
||||
AppendUInt16(resourceForkData, static_cast<uint16_t>(refListStartForType[i] * refListEntrySize + resourceTypeListSize));
|
||||
}
|
||||
|
||||
// Write reference lists
|
||||
for (size_t ti = 0; ti < uniqueResTypes.size(); ti++)
|
||||
{
|
||||
uint32_t resType = uniqueResTypes[ti];
|
||||
|
||||
for (size_t i = 0; i < resources.size(); i++)
|
||||
{
|
||||
const SimpleResource *res = resources[i];
|
||||
if (TypeToU32(res->m_type) != resType)
|
||||
continue;
|
||||
|
||||
AppendInt16(resourceForkData, res->m_resourceID);
|
||||
if (res->m_name.empty())
|
||||
AppendInt16(resourceForkData, -1);
|
||||
else
|
||||
{
|
||||
const std::vector<std::string>::iterator nameIt = std::find(uniqueResNames.begin(), uniqueResNames.end(), res->m_name);
|
||||
const size_t nameIndex = nameIt - uniqueResNames.begin();
|
||||
|
||||
AppendUInt16(resourceForkData, static_cast<uint16_t>(nameListStarts[nameIndex]));
|
||||
}
|
||||
|
||||
resourceForkData.push_back(res->m_attributes);
|
||||
|
||||
const size_t resDataStart = res->m_offsetInResData;
|
||||
|
||||
resourceForkData.push_back((resDataStart >> 16) & 0xff);
|
||||
resourceForkData.push_back((resDataStart >> 8) & 0xff);
|
||||
resourceForkData.push_back((resDataStart >> 0) & 0xff);
|
||||
|
||||
for (int j = 0; j < 4; j++)
|
||||
resourceForkData.push_back(0); // Reserved
|
||||
}
|
||||
}
|
||||
|
||||
// Write name lists
|
||||
for (size_t ni = 0; ni < uniqueResNames.size(); ni++)
|
||||
{
|
||||
const std::string &name = uniqueResNames[ni];
|
||||
const size_t nameLen = name.size();
|
||||
|
||||
resourceForkData.push_back(static_cast<uint8_t>(nameLen));
|
||||
|
||||
for (size_t i = 0; i < nameLen; i++)
|
||||
resourceForkData.push_back(name[i]);
|
||||
}
|
||||
|
||||
const size_t resMapSize = resourceForkData.size() - resMapPos;
|
||||
|
||||
BytePack::BigUInt32(&resourceForkData[0], static_cast<uint32_t>(resForkDataStart));
|
||||
BytePack::BigUInt32(&resourceForkData[4], static_cast<uint32_t>(resMapPos));
|
||||
BytePack::BigUInt32(&resourceForkData[8], static_cast<uint32_t>(resDataSize));
|
||||
BytePack::BigUInt32(&resourceForkData[12], static_cast<uint32_t>(resMapSize));
|
||||
|
||||
FILE *outF = nullptr;
|
||||
if (fopen_s(&outF, argv[2], "wb"))
|
||||
{
|
||||
fprintf(stderr, "Failed to open output file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fwrite(&resourceForkData[0], 1, resourceForkData.size(), outF);
|
||||
fclose(outF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user