Merge branch 'Rez'

Conflicts:
	MakeAPPL/main.cc
This commit is contained in:
Wolfgang Thaller 2014-10-29 00:49:56 +01:00
commit 4351647dba
30 changed files with 2915 additions and 207 deletions

View File

@ -69,4 +69,5 @@ add_subdirectory(Launcher)
else()
add_subdirectory(MakeAPPL)
add_subdirectory(ASFilter)
add_subdirectory(Rez)
endif()

50
MakeAPPL/BinaryIO.cc Normal file
View File

@ -0,0 +1,50 @@
#include "BinaryIO.h"
#include <iostream>
#include <cassert>
#include "ResType.h"
void byte(std::ostream& out, int byte)
{
out.put((unsigned char)byte);
}
void word(std::ostream& out, int word)
{
byte(out,(word >> 8) & 0xFF);
byte(out,word & 0xFF);
}
void ostype(std::ostream& out, ResType type)
{
longword(out, type);
}
void longword(std::ostream& out, int longword)
{
byte(out,(longword >> 24) & 0xFF);
byte(out,(longword >> 16) & 0xFF);
byte(out,(longword >> 8) & 0xFF);
byte(out,longword & 0xFF);
}
int byte(std::istream& in)
{
return in.get() & 0xFF;
}
int word(std::istream& in)
{
int a = byte(in);
int b = byte(in);
return (a << 8) | b;
}
ResType ostype(std::istream& in)
{
return longword(in);
}
int longword(std::istream& in)
{
int a = byte(in);
int b = byte(in);
int c = byte(in);
int d = byte(in);
return (a << 24) | (b << 16) | (c << 8) | d;
}

19
MakeAPPL/BinaryIO.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef BINARYIO_H
#define BINARYIO_H
#include <iosfwd>
#include <string>
class ResType;
void byte(std::ostream& out, int byte);
void word(std::ostream& out, int word);
void ostype(std::ostream& out, ResType type);
void longword(std::ostream& out, int longword);
int byte(std::istream& in);
int word(std::istream& in);
ResType ostype(std::istream& in);
int longword(std::istream& in);
#endif // BINARYIO_H

View File

@ -16,7 +16,11 @@
# along with Retro68. If not, see <http://www.gnu.org/licenses/>.
cmake_minimum_required(VERSION 2.8)
set(CMAKE_CXX_FLAGS "--std=c++0x")
add_library(ResourceFiles ResourceFiles.h ResourceFiles.cc BinaryIO.h BinaryIO.cc ResType.h ResType.cc)
target_include_directories(ResourceFiles PUBLIC .)
add_executable(MakeAPPL main.cc)
target_link_libraries(MakeAPPL ResourceFiles)
install(TARGETS MakeAPPL RUNTIME DESTINATION bin)

58
MakeAPPL/ResType.cc Normal file
View File

@ -0,0 +1,58 @@
#include "ResType.h"
#include <iostream>
#include <cassert>
ResType::ResType(const std::string &str)
{
auto p = str.begin();
auto e = str.end();
assert(str.size() == 4);
x = 0;
while(p != e)
{
x <<= 8;
x |= (*p) & 0xFF;
++p;
}
}
ResType::ResType(const char *s)
{
auto p = s;
auto e = s + 4;
assert(s[0] && s[1] && s[2] && s[3] && !s[4]);
x = 0;
while(p != e)
{
x <<= 8;
x |= (*p) & 0xFF;
++p;
}
}
ResType::operator std::string()
{
char c1 = static_cast<char>(x >> 24);
char c2 = static_cast<char>(x >> 16);
char c3 = static_cast<char>(x >> 8);
char c4 = static_cast<char>(x);
return std::string{ c1, c2, c3, c4 };
}
std::ostream &operator<<(std::ostream &out, ResType t)
{
char c1 = static_cast<char>((int)t >> 24);
char c2 = static_cast<char>((int)t >> 16);
char c3 = static_cast<char>((int)t >> 8);
char c4 = static_cast<char>((int)t);
out << "'" << c1 << c2 << c3 << c4 << "'";
return out;
}

35
MakeAPPL/ResType.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef RESTYPE_H
#define RESTYPE_H
#include <string>
#include <iosfwd>
class ResType
{
int x;
public:
ResType() : x(0) {}
ResType(int x) : x(x) {}
ResType(const std::string& s);
ResType(const char* s);
operator int() const { return x; }
bool operator<(ResType y) const { return x < y.x; }
operator std::string();
};
std::ostream& operator<<(std::ostream& out, ResType t);
struct ResRef : public std::pair<ResType, int>
{
ResRef() : std::pair<ResType, int>(ResType(), 0) {}
ResRef(ResType t, int id) : std::pair<ResType, int>(t,id) {}
ResType& type() { return first; }
ResType type() const { return first; }
int& id() { return second; }
int id() const { return second; }
};
#endif // RESTYPE_H

133
MakeAPPL/ResourceFiles.cc Normal file
View File

@ -0,0 +1,133 @@
#include "ResourceFiles.h"
#include "BinaryIO.h"
#include <iostream>
#include <map>
#include <vector>
void Resources::addResources(const Resources& res)
{
for(auto& rr : res.resources)
resources.insert(rr);
// resources.insert(resources.end(),res.resources.begin(), res.resources.end());
}
void Resources::writeFork(std::ostream& out) const
{
std::streampos start = out.tellp();
longword(out,0x100);
longword(out,0);
longword(out,0);
longword(out,0);
out.seekp(start + std::streampos(0x100));
std::map< std::string, std::map<int, int> > resourceInfos;
std::streampos datastart = out.tellp();
for(auto& rr : resources)
{
const Resource& r = rr.second;
const std::string& data = r.getData();
resourceInfos[ r.getType() ][ r.getID() ] = out.tellp() - datastart;
longword(out, data.size());
out << data;
}
std::streampos dataend = out.tellp();
// while(out.tellp() % 0x100)
// out.put(0);
std::streampos resmap = out.tellp();
out.seekp(16+4+2+2, std::ios::cur);
word(out,16+4+2+2+2+2); // offset to resource type list
std::streampos resnameOffset = out.tellp();
word(out,0);
std::streampos typelist = out.tellp();
word(out,resourceInfos.size() - 1);
for(std::map< std::string, std::map<int, int> >::iterator p = resourceInfos.begin();
p != resourceInfos.end(); ++p)
{
if(p->second.size())
{
ostype(out,p->first);
word(out,p->second.size()-1);
word(out,0); // replaced later
}
}
int typeIndex = 0;
for(std::map< std::string, std::map<int, int> >::iterator p = resourceInfos.begin();
p != resourceInfos.end(); ++p)
{
if(p->second.size())
{
std::streampos pos = out.tellp();
out.seekp((int)typelist + 2 + 8 * typeIndex + 6);
word(out, pos - typelist);
out.seekp(pos);
typeIndex++;
for(std::map<int,int>::iterator q = p->second.begin(); q != p->second.end(); ++q)
{
word(out,q->first);
word(out,-1);
longword(out,q->second);
longword(out,0);
}
}
}
std::streampos resnames = out.tellp();
out.seekp(resnameOffset);
word(out, resnames - resmap);
out.seekp(resnames);
std::streampos end = out.tellp();
out.seekp(start + std::streampos(4));
longword(out, resmap - start);
longword(out, dataend - start - std::streampos(0x100));
longword(out, end - resmap);
out.seekp(end);
}
Resources::Resources(std::istream &in)
{
std::streampos start = in.tellg();
int resdataOffset = longword(in);
int resmapOffset = longword(in);
in.seekg(start + std::streampos(resmapOffset + 16 + 4 + 2 + 2));
int typeListOffset = word(in);
int nameListOffset = word(in);
int nTypes = (word(in) + 1) & 0xFFFF;
for(int i = 0; i < nTypes; i++)
{
in.seekg(start + std::streampos(resmapOffset + typeListOffset + 2 + i * 8));
std::string type = ostype(in);
int nRes = (word(in) + 1) & 0xFFFF;
int refListOffset = word(in);
for(int j = 0; j < nRes; j++)
{
in.seekg(start + std::streampos(resmapOffset + typeListOffset + refListOffset + j * 12));
int id = word(in);
int nameOffset = word(in);
int attr = byte(in);
int off1 = byte(in);
int off2 = byte(in);
int off3 = byte(in);
int offset = (off1 << 16) | (off2 << 8) | off3;
std::string name;
if(nameOffset != 0xFFFF)
{
in.seekg(start + std::streampos(resmapOffset + nameListOffset + nameOffset));
int nameLen = byte(in);
char buf[256];
in.read(buf, nameLen);
name = std::string(buf, nameLen);
}
in.seekg(start + std::streampos(resdataOffset + offset));
int size = longword(in);
std::vector<char> tmp(size);
in.read(tmp.data(), size);
std::string data(tmp.data(), size);
addResource(Resource(type, id, data, name, attr));
}
}
}

44
MakeAPPL/ResourceFiles.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef RESOURCEFILES_H
#define RESOURCEFILES_H
#include <string>
#include <map>
#include "ResType.h"
class Resource
{
ResType type;
int id;
std::string name;
std::string data;
int attr;
public:
Resource() {}
Resource(ResType type, int id, std::string data, std::string name = "", int attr = 0)
: type(type), id(id), name(name), data(data), attr(attr) {}
const std::string& getData() const { return data; }
inline ResType getType() const { return type; }
inline int getID() const { return id; }
inline ResRef getTypeAndID() const { return ResRef(type, id); }
};
class Fork
{
public:
virtual void writeFork(std::ostream& out) const { }
virtual ~Fork() {}
};
class Resources : public Fork
{
std::map<ResRef, Resource> resources;
public:
Resources() {}
Resources(std::istream& in);
void writeFork(std::ostream& out) const;
void addResource(Resource res) { resources[res.getTypeAndID()] = res; }
void addResources(const Resources& res);
};
#endif // RESOURCEFILES_H

View File

@ -31,6 +31,8 @@
#ifdef __APPLE__
#include <sys/xattr.h>
#endif
#include "ResourceFiles.h"
#include "BinaryIO.h"
std::string commandPath;
@ -49,212 +51,6 @@ void wrapMacBinary(std::string macBinaryFile, std::string diskImagePath)
std::system((commandPath + "hcopy -m " + macBinaryFile + " :").c_str());
}
class Resource
{
std::string type;
int id;
std::string name;
std::string data;
int attr;
public:
Resource(std::string type, int id, std::string data, std::string name = "", int attr = 0)
: type(type), id(id), data(data), name(name), attr(attr) {}
const std::string& getData() const { return data; }
inline std::string getType() const { return type; }
inline int getID() const { return id; }
};
class Fork
{
public:
virtual void writeFork(std::ostream& out) const { }
virtual ~Fork() {}
};
class Resources : public Fork
{
std::vector<Resource> resources;
public:
Resources() {}
Resources(std::istream& in);
void writeFork(std::ostream& out) const;
void addResource(Resource res) { resources.push_back(res); }
void addResources(const Resources& res);
};
void byte(std::ostream& out, int byte)
{
out.put((unsigned char)byte);
}
void word(std::ostream& out, int word)
{
byte(out,(word >> 8) & 0xFF);
byte(out,word & 0xFF);
}
void ostype(std::ostream& out, std::string type)
{
assert(type.size() == 4);
out << type;
}
void longword(std::ostream& out, int longword)
{
byte(out,(longword >> 24) & 0xFF);
byte(out,(longword >> 16) & 0xFF);
byte(out,(longword >> 8) & 0xFF);
byte(out,longword & 0xFF);
}
int byte(std::istream& in)
{
return in.get() & 0xFF;
}
int word(std::istream& in)
{
int a = byte(in);
int b = byte(in);
return (a << 8) | b;
}
std::string ostype(std::istream& in)
{
char s[5];
in.read(s,4);
s[4] = 0;
return s;
}
int longword(std::istream& in)
{
int a = byte(in);
int b = byte(in);
int c = byte(in);
int d = byte(in);
return (a << 24) | (b << 16) | (c << 8) | d;
}
void Resources::addResources(const Resources& res)
{
resources.insert(resources.end(),res.resources.begin(), res.resources.end());
}
void Resources::writeFork(std::ostream& out) const
{
std::streampos start = out.tellp();
longword(out,0x100);
longword(out,0);
longword(out,0);
longword(out,0);
out.seekp(start + std::streampos(0x100));
std::map< std::string, std::map<int, int> > resourceInfos;
std::streampos datastart = out.tellp();
for(std::vector<Resource>::const_iterator p = resources.begin(); p != resources.end(); ++p)
{
const std::string& data = p->getData();
resourceInfos[ p->getType() ][ p->getID() ] = out.tellp() - datastart;
longword(out, data.size());
out << data;
}
std::streampos dataend = out.tellp();
// while(out.tellp() % 0x100)
// out.put(0);
std::streampos resmap = out.tellp();
out.seekp(16+4+2+2, std::ios::cur);
word(out,16+4+2+2+2+2); // offset to resource type list
std::streampos resnameOffset = out.tellp();
word(out,0);
std::streampos typelist = out.tellp();
word(out,resourceInfos.size() - 1);
for(std::map< std::string, std::map<int, int> >::iterator p = resourceInfos.begin();
p != resourceInfos.end(); ++p)
{
if(p->second.size())
{
ostype(out,p->first);
word(out,p->second.size()-1);
word(out,0); // replaced later
}
}
int typeIndex = 0;
for(std::map< std::string, std::map<int, int> >::iterator p = resourceInfos.begin();
p != resourceInfos.end(); ++p)
{
if(p->second.size())
{
std::streampos pos = out.tellp();
out.seekp((int)typelist + 2 + 8 * typeIndex + 6);
word(out, pos - typelist);
out.seekp(pos);
typeIndex++;
for(std::map<int,int>::iterator q = p->second.begin(); q != p->second.end(); ++q)
{
word(out,q->first);
word(out,-1);
longword(out,q->second);
longword(out,0);
}
}
}
std::streampos resnames = out.tellp();
out.seekp(resnameOffset);
word(out, resnames - resmap);
out.seekp(resnames);
std::streampos end = out.tellp();
out.seekp(start + std::streampos(4));
longword(out, resmap - start);
longword(out, dataend - start - std::streampos(0x100));
longword(out, end - resmap);
out.seekp(end);
}
Resources::Resources(std::istream &in)
{
std::streampos start = in.tellg();
int resdataOffset = longword(in);
int resmapOffset = longword(in);
in.seekg(start + std::streampos(resmapOffset + 16 + 4 + 2 + 2));
int typeListOffset = word(in);
int nameListOffset = word(in);
int nTypes = (word(in) + 1) & 0xFFFF;
for(int i = 0; i < nTypes; i++)
{
in.seekg(start + std::streampos(resmapOffset + typeListOffset + 2 + i * 8));
std::string type = ostype(in);
int nRes = (word(in) + 1) & 0xFFFF;
int refListOffset = word(in);
for(int j = 0; j < nRes; j++)
{
in.seekg(start + std::streampos(resmapOffset + typeListOffset + refListOffset + j * 12));
int id = word(in);
int nameOffset = word(in);
int attr = byte(in);
int off1 = byte(in);
int off2 = byte(in);
int off3 = byte(in);
int offset = (off1 << 16) | (off2 << 8) | off3;
std::string name;
if(nameOffset != 0xFFFF)
{
in.seekg(start + std::streampos(resmapOffset + nameListOffset + nameOffset));
int nameLen = byte(in);
char buf[256];
in.read(buf, nameLen);
name = std::string(buf, nameLen);
}
in.seekg(start + std::streampos(resdataOffset + offset));
int size = longword(in);
std::vector<char> tmp(size);
in.read(tmp.data(), size);
std::string data(tmp.data(), size);
addResource(Resource(type, id, data, name, attr));
}
}
}
// CRC 16 table lookup array
static unsigned short CRC16Table[256] =
@ -446,7 +242,7 @@ int main(int argc, char *argv[])
std::string fn = argv[i++];
std::string flt = readfile(fn);
rsrc.addResource(Resource("CODE", 0,
rsrc.addResource(Resource(ResType("CODE"), 0,
fromhex(
"00000028 00000000 00000008 00000020"
"0000 3F3C 0001 A9F0"

71
Rez/CMakeLists.txt Normal file
View File

@ -0,0 +1,71 @@
# Copyright 2012 Wolfgang Thaller.
#
# This file is part of Retro68.
#
# Retro68 is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Retro68 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Retro68. If not, see <http://www.gnu.org/licenses/>.
cmake_minimum_required(VERSION 2.8)
set(CMAKE_CXX_FLAGS "--std=c++11 -Wall")
find_package(Boost COMPONENTS wave filesystem system thread regex program_options)
if(Boost_FOUND)
find_package(BISON REQUIRED)
include_directories(. ${CMAKE_CURRENT_BINARY_DIR})
add_custom_command(
DEPENDS RezParser.yy
COMMAND ${BISON_EXECUTABLE}
ARGS -o ${CMAKE_CURRENT_BINARY_DIR}/RezParser.generated.cc
${CMAKE_CURRENT_SOURCE_DIR}/RezParser.yy --graph
COMMENT "Generating parser.cpp"
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/RezParser.generated.cc ${CMAKE_CURRENT_BINARY_DIR}/RezParser.generated.hh
)
add_library(RezLib
RezParser.yy RezParser.generated.hh RezParser.generated.cc
RezLexer.h
RezLexer.cc
RezLexerWaveToken.h
RezLexerNextToken.cc
RezWorld.cc
RezWorld.h
ResourceDefinitions.cc
ResourceDefinitions.h
Expression.cc
Expression.h
ResourceCompiler.cc
ResourceCompiler.h
ResSpec.h
)
target_link_libraries(RezLib ResourceFiles ${Boost_LIBRARIES})
add_executable(Rez
Rez.cc
)
target_link_libraries(Rez RezLib ResourceFiles ${Boost_LIBRARIES})
install(TARGETS Rez RUNTIME DESTINATION bin)
add_subdirectory(Test)
endif(Boost_FOUND)

211
Rez/Expression.cc Normal file
View File

@ -0,0 +1,211 @@
#include "Expression.h"
#include "ResourceCompiler.h"
#include <cassert>
#include <iostream>
#include <fstream>
int Expression::evaluateInt(ResourceCompiler *ctx)
{
throw TypeError();
}
std::string Expression::evaluateString(ResourceCompiler *ctx)
{
throw TypeError();
}
Expression::~Expression()
{
}
StringExpr::~StringExpr()
{
}
std::string StringExpr::evaluateString(ResourceCompiler *ctx)
{
return str;
}
IntExpr::~IntExpr()
{
}
int IntExpr::evaluateInt(ResourceCompiler *ctx)
{
return val;
}
void CompoundExpr::addItem(ExprPtr item)
{
items.push_back(item);
}
CompoundExpr::~CompoundExpr()
{
}
BinaryExpr::~BinaryExpr()
{
}
int BinaryExpr::evaluateInt(ResourceCompiler *ctx)
{
switch(op)
{
case BinaryOp::XOR:
return a->evaluateInt(ctx) ^ b->evaluateInt(ctx);
case BinaryOp::OR:
return a->evaluateInt(ctx) | b->evaluateInt(ctx);
case BinaryOp::AND:
return a->evaluateInt(ctx) & b->evaluateInt(ctx);
case BinaryOp::SHIFTLEFT:
return a->evaluateInt(ctx) << b->evaluateInt(ctx);
case BinaryOp::SHIFTRIGHT:
return a->evaluateInt(ctx) >> b->evaluateInt(ctx);
case BinaryOp::EQUAL:
return a->evaluateInt(ctx) == b->evaluateInt(ctx);
case BinaryOp::NOTEQUAL:
return a->evaluateInt(ctx) != b->evaluateInt(ctx);
case BinaryOp::PLUS:
return a->evaluateInt(ctx) + b->evaluateInt(ctx);
case BinaryOp::MINUS:
return a->evaluateInt(ctx) - b->evaluateInt(ctx);
case BinaryOp::MULTIPLY:
return a->evaluateInt(ctx) * b->evaluateInt(ctx);
case BinaryOp::DIVIDE:
return a->evaluateInt(ctx) / b->evaluateInt(ctx);
default:
throw TypeError();
break;
}
}
std::string BinaryExpr::evaluateString(ResourceCompiler *ctx)
{
switch(op)
{
case BinaryOp::CONCAT:
return a->evaluateString(ctx) + b->evaluateString(ctx);
default:
throw TypeError();
break;
}
}
UnaryExpr::~UnaryExpr()
{
}
int UnaryExpr::evaluateInt(ResourceCompiler *ctx)
{
switch(op)
{
case UnaryOp::MINUS:
return -a->evaluateInt(ctx);
case UnaryOp::COMPLEMENT:
return ~a->evaluateInt(ctx);
}
}
IdentifierExpr::IdentifierExpr(std::string id)
: id(id)
{
}
void IdentifierExpr::addArgument(ExprPtr e)
{
arguments.push_back(e);
}
ExprPtr IdentifierExpr::lookup(ResourceCompiler *ctx)
{
Subscripts sub;
for(auto arg : arguments)
sub.addSubscript(arg->evaluateInt(ctx));
ExprPtr val = ctx->lookupIdentifier(id, sub);
assert(val);
return val;
}
int IdentifierExpr::evaluateInt(ResourceCompiler *ctx)
{
if(ctx->isPrePass())
return 0;
return lookup(ctx)->evaluateInt(ctx);
}
std::string IdentifierExpr::evaluateString(ResourceCompiler *ctx)
{
return lookup(ctx)->evaluateString(ctx);
}
CaseExpr::CaseExpr(const std::string &tag, CompoundExprPtr expr)
: tag(tag), expr(expr)
{
}
int CountOfExpr::evaluateInt(ResourceCompiler *ctx)
{
assert(arg->arguments.size() == 0);
return ctx->getArrayCount(arg->id);
}
int ArrayIndexExpr::evaluateInt(ResourceCompiler *ctx)
{
assert(arg->arguments.size() == 0);
return ctx->getArrayIndex(arg->id);
}
std::string ReadExpr::evaluateString(ResourceCompiler *ctx)
{
std::string filename = arg->evaluateString(ctx);
std::ifstream instream(filename);
// ### TODO: check error
return std::string(std::istreambuf_iterator<char>(instream.rdbuf()),
std::istreambuf_iterator<char>());
}
int UnimplementedExpr::evaluateInt(ResourceCompiler *ctx)
{
std::cerr << msg << std::endl;
return 0;
}
std::string UnimplementedExpr::evaluateString(ResourceCompiler *ctx)
{
std::cerr << msg << std::endl;
return "";
}
PeekExpr::PeekExpr(ExprPtr addr, ExprPtr offset, ExprPtr size)
: addr(addr), offset(offset), size(size)
{
}
PeekExpr::PeekExpr(ExprPtr addr, int size)
: addr(addr),
offset(std::make_shared<IntExpr>(0)),
size(std::make_shared<IntExpr>(size))
{
}
int PeekExpr::evaluateInt(ResourceCompiler *ctx)
{
int p = addr->evaluateInt(ctx) + offset->evaluateInt(ctx);
int s = size->evaluateInt(ctx);
return ctx->peek(p, s);
}

162
Rez/Expression.h Normal file
View File

@ -0,0 +1,162 @@
#ifndef EXPRESSION_H
#define EXPRESSION_H
#include <memory>
#include <vector>
class ResourceCompiler;
class Expression;
class CompoundExpr;
class IdentifierExpr;
class CaseExpr;
typedef std::shared_ptr<Expression> ExprPtr;
typedef std::shared_ptr<CompoundExpr> CompoundExprPtr;
typedef std::shared_ptr<IdentifierExpr> IdentifierExprPtr;
typedef std::shared_ptr<CaseExpr> CaseExprPtr;
enum class BinaryOp
{
XOR, OR, AND, SHIFTLEFT, SHIFTRIGHT, EQUAL, NOTEQUAL, PLUS, MINUS, MULTIPLY, DIVIDE, CONCAT
};
enum class UnaryOp
{
MINUS, COMPLEMENT
};
class TypeError
{
};
class Expression
{
public:
virtual int evaluateInt(ResourceCompiler *ctx);
virtual std::string evaluateString(ResourceCompiler *ctx);
virtual ~Expression();
};
class StringExpr : public Expression
{
std::string str;
public:
StringExpr(const std::string& str) : str(str) {}
~StringExpr();
virtual std::string evaluateString(ResourceCompiler *ctx);
};
class IntExpr : public Expression
{
int val;
public:
IntExpr(int val) : val(val) {}
~IntExpr();
virtual int evaluateInt(ResourceCompiler *ctx);
};
class CompoundExpr : public Expression
{
std::vector<ExprPtr> items;
public:
void addItem(ExprPtr item);
ExprPtr getItem(int i) const { return items[i]; }
int size() const { return items.size(); }
~CompoundExpr();
};
class CaseExpr : public Expression
{
std::string tag;
CompoundExprPtr expr;
friend class SwitchField;
public:
CaseExpr(const std::string& tag, CompoundExprPtr expr);
};
class BinaryExpr : public Expression
{
BinaryOp op;
ExprPtr a, b;
public:
BinaryExpr(BinaryOp op, ExprPtr a, ExprPtr b)
: op(op), a(a), b(b) {}
~BinaryExpr();
virtual int evaluateInt(ResourceCompiler *ctx);
virtual std::string evaluateString(ResourceCompiler *ctx);
};
class UnaryExpr : public Expression
{
UnaryOp op;
ExprPtr a;
public:
UnaryExpr(UnaryOp op, ExprPtr a)
: op(op), a(a) {}
~UnaryExpr();
virtual int evaluateInt(ResourceCompiler *ctx);
};
class IdentifierExpr : public Expression
{
public:
std::string id;
std::vector<ExprPtr> arguments;
IdentifierExpr(std::string id);
void addArgument(ExprPtr e);
ExprPtr lookup(ResourceCompiler *ctx);
virtual int evaluateInt(ResourceCompiler *ctx);
virtual std::string evaluateString(ResourceCompiler *ctx);
};
class CountOfExpr : public Expression
{
IdentifierExprPtr arg;
public:
CountOfExpr(IdentifierExprPtr arg) : arg(arg) {}
virtual int evaluateInt(ResourceCompiler *ctx);
};
class ArrayIndexExpr : public Expression
{
IdentifierExprPtr arg;
public:
ArrayIndexExpr(IdentifierExprPtr arg) : arg(arg) {}
virtual int evaluateInt(ResourceCompiler *ctx);
};
class ReadExpr : public Expression
{
ExprPtr arg;
public:
ReadExpr(ExprPtr arg) : arg(arg) {}
virtual std::string evaluateString(ResourceCompiler *ctx);
};
class UnimplementedExpr : public Expression
{
std::string msg;
public:
UnimplementedExpr(std::string msg) : msg(msg) {}
virtual int evaluateInt(ResourceCompiler *ctx);
virtual std::string evaluateString(ResourceCompiler *ctx);
};
class PeekExpr : public Expression
{
ExprPtr addr;
ExprPtr offset;
ExprPtr size;
public:
PeekExpr(ExprPtr addr, ExprPtr offset, ExprPtr size);
PeekExpr(ExprPtr addr, int size);
virtual int evaluateInt(ResourceCompiler *ctx);
};
#endif // EXPRESSION_H

12
Rez/README.md Normal file
View File

@ -0,0 +1,12 @@
Rez Resource Compiler
=====================
A reimplementation of the classic Rez resource compiler.
Known Bugs & Limitations
-----------------
* `$ABCD` alternate syntax for hex numbers is not supported
* hex strings not supported
* Other than in Apple Rez, the preprocessor is case sensitive.

24
Rez/ResSpec.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef REZSPEC_H
#define REZSPEC_H
#include "ResType.h"
#include <string>
class ResSpec : public ResRef
{
int attr_;
std::string name_;
public:
ResSpec() {}
ResSpec(ResType type, int id, int attr = 0, std::string name = "")
: ResRef(type, id), attr_(attr), name_(name)
{}
int& attr() { return attr_; }
int attr() const { return attr_; }
std::string& name() { return name_; }
const std::string& name() const { return name_; }
};
#endif // REZSPEC_H

191
Rez/ResourceCompiler.cc Normal file
View File

@ -0,0 +1,191 @@
#include "ResourceCompiler.h"
#include <iostream>
#include "ResourceDefinitions.h"
ResourceCompiler::ResourceCompiler(
TypeDefinitionPtr type, CompoundExprPtr body, bool verboseFlag)
: typeDefinition(type),
body(body),
currentField(nullptr)
{
this->verboseFlag = verboseFlag;
}
BinaryOutput::BinaryOutput()
: verboseFlag(false)
{
reset(true);
}
void BinaryOutput::reset(bool prePass)
{
currentOffset = 0;
if(!prePass)
prePassData = std::move(data);
data.clear();
this->prePass = prePass;
}
std::string BinaryOutput::resourceData()
{
return std::string(data.begin(), data.end());
}
void BinaryOutput::write(int nBits, int value)
{
if(verboseFlag)
std::cout << "[" << nBits << " bits] = " << std::hex << value << std::dec << std::endl;
unsigned mask = 1 << (nBits-1);
for(int i = 0; i < nBits; i++)
{
bool bit = (value & mask) != 0;
if(currentOffset % 8 == 0)
data.push_back(bit ? 0x80 : 0);
else if(bit)
data.back() |= (0x80 >> (currentOffset % 8));
++currentOffset;
mask >>= 1;
}
//currentOffset += nBits;
}
int BinaryOutput::peek(int bitPos, int size)
{
unsigned bytePos = bitPos / 8;
unsigned endBytePos = (bitPos + size - 1) / 8 + 1;
unsigned bitPosInByte = bitPos % 8;
unsigned outPos = 32 - size;
unsigned val = 0;
for(unsigned i = bytePos; i != endBytePos; ++i)
{
unsigned byte;
if(i < data.size())
byte = data[i];
else if(i < prePassData.size())
byte = prePassData[i];
else
byte = 0;
unsigned read = byte << (bitPosInByte + 24);
val |= (read >> outPos);
outPos += 8 - bitPosInByte;
bitPosInByte = 0;
}
return val;
}
ExprPtr ResourceCompiler::lookupIdentifier(std::string name, const Subscripts &sub)
{
if(currentField)
{
if(ExprPtr val = currentField->lookupNamedValue(name))
{
return val;
}
}
auto p = labelValues.find(std::make_pair(name, sub));
if(p != labelValues.end())
return p->second;
std::cerr << "ID lookup failed: " << name << std::endl;
return nullptr;
}
void ResourceCompiler::defineLabel(const std::string &name)
{
labelValues[std::make_pair(name,currentSubscripts)] = std::make_shared<IntExpr>(currentOffset);
}
void ResourceCompiler::compile()
{
if(verboseFlag) std::cout << "(first pass)\n";
reset(true);
typeDefinition->compile(body, this, true);
if(verboseFlag) std::cout << "(second pass)\n";
reset(false);
typeDefinition->compile(body, this, false);
if(verboseFlag) std::cout << "(done)\n";
}
int ResourceCompiler::getArrayCount(const std::string &name)
{
Subscripts sub = currentSubscripts;
for(;;)
{
auto p = arrayCounts.find(std::make_pair(name, sub));
if(p != arrayCounts.end())
return p->second;
if(sub.empty())
return 0; /* ### */
sub.popSubscript();
}
}
int ResourceCompiler::getArrayIndex(const std::string &arrayName)
{
return curArrayIndices[arrayName];
}
void ResourceCompiler::beginArrayScope(std::string &arrayName, int index)
{
if(arrayName != "")
{
curArrayIndices[arrayName] = index;
int& count = arrayCounts[std::make_pair(arrayName, currentSubscripts)];
if(count < index)
count = index;
arrayCounts[std::make_pair(arrayName, Subscripts())] = count;
//std::cout << "count for " << arrayName << " is " << count << std::endl;
}
currentSubscripts.addSubscript(index);
}
Subscripts::Subscripts()
{
}
Subscripts::~Subscripts()
{
}
void Subscripts::addSubscript(int x)
{
subscripts.push_back(x);
}
void Subscripts::popSubscript()
{
subscripts.pop_back();
}
bool Subscripts::operator<(const Subscripts &other) const
{
if(subscripts.size() < other.subscripts.size())
return true;
if(other.subscripts.size() < subscripts.size())
return false;
for(int i = 0, n = subscripts.size(); i < n; i++)
{
if(subscripts[i] < other.subscripts[i])
return true;
else if(subscripts[i] > other.subscripts[i])
return false;
}
return false;
}

94
Rez/ResourceCompiler.h Normal file
View File

@ -0,0 +1,94 @@
#ifndef RESOURCECOMPILER_H
#define RESOURCECOMPILER_H
#include "Expression.h"
#include "ResourceDefinitions.h"
class Field;
class Subscripts
{
std::vector<int> subscripts;
public:
Subscripts();
~Subscripts();
void addSubscript(int x);
void popSubscript();
bool operator<(const Subscripts& other) const;
bool empty() const { return subscripts.empty(); }
};
class BinaryOutput
{
protected:
int currentOffset;
std::vector<unsigned char> data;
std::vector<unsigned char> prePassData;
bool verboseFlag;
bool prePass;
public:
BinaryOutput();
void reset(bool prePass);
std::string resourceData();
void reserve(int nBits) { write(nBits, 0); }
void write(int nBits, int value);
int tell() { return currentOffset; }
int peek(int bitPos, int size);
bool isPrePass() { return prePass; }
};
class ResourceCompiler : public BinaryOutput
{
TypeDefinitionPtr typeDefinition;
CompoundExprPtr body;
std::map<std::pair<std::string, Subscripts>, ExprPtr> labelValues;
std::map<std::pair<std::string, Subscripts>, int> arrayCounts;
std::map<std::string, int> curArrayIndices;
Field* currentField;
Subscripts currentSubscripts;
void beginArrayScope(std::string& arrayName, int index);
public:
ResourceCompiler(TypeDefinitionPtr type, CompoundExprPtr body, bool verboseFlag);
ExprPtr lookupIdentifier(std::string name, const Subscripts& sub = Subscripts());
void defineLabel(const std::string& name);
void compile();
int getArrayCount(const std::string& arrayName);
int getArrayIndex(const std::string& arrayName);
class FieldScope
{
ResourceCompiler *compiler;
public:
FieldScope(ResourceCompiler* compiler, Field *field)
: compiler(compiler) { compiler->currentField = field; }
~FieldScope() { compiler->currentField = nullptr; }
};
class ArrayScope
{
ResourceCompiler *compiler;
public:
ArrayScope(ResourceCompiler* compiler, std::string& arrayName, int index)
: compiler(compiler) { compiler->beginArrayScope(arrayName, index); }
~ArrayScope() { compiler->currentSubscripts.popSubscript(); }
};
};
#endif // RESOURCECOMPILER_H

327
Rez/ResourceDefinitions.cc Normal file
View File

@ -0,0 +1,327 @@
#include "ResourceDefinitions.h"
#include <ostream>
#include <cassert>
#include "ResourceCompiler.h"
std::ostream &operator<<(std::ostream &out, TypeSpec ts)
{
out << ts.getType();
if(ts.hasID())
out << " (" << ts.getID() << ")";
return out;
}
FieldList::~FieldList()
{
}
void FieldList::addField(FieldPtr field)
{
fields.push_back(field);
}
void FieldList::addLabel(std::string name)
{
addField(std::make_shared<LabelField>(name));
}
void FieldList::compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass)
{
CompoundExprPtr compound = std::dynamic_pointer_cast<CompoundExpr>(expr);
assert(compound);
int i = 0;
for(FieldPtr f : fields)
{
if(f->needsValue())
f->compile(compound->getItem(i++), compiler, prePass);
else
f->compile(nullptr, compiler, prePass);
}
}
void SimpleField::addNamedValue(std::string n)
{
if(lastNamedValue)
addNamedValue(n, std::make_shared<BinaryExpr>(
BinaryOp::PLUS, lastNamedValue, std::make_shared<IntExpr>(1)));
else
addNamedValue(n, std::make_shared<IntExpr>(0));
}
void SimpleField::addNamedValue(std::string n, ExprPtr val)
{
namedValues[n] = val;
lastNamedValue = val;
}
ExprPtr SimpleField::lookupNamedValue(std::string n)
{
auto p = namedValues.find(n);
if(p != namedValues.end())
return p->second;
else
return nullptr;
}
bool SimpleField::needsValue()
{
return !value;
}
void SimpleField::compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass)
{
switch(type)
{
case Type::bitstring:
case Type::boolean:
case Type::byte:
case Type::integer:
case Type::longint:
compileInt(expr, compiler, prePass);
break;
case Type::string:
case Type::wstring:
case Type::pstring:
case Type::char_:
compileString(expr, compiler, prePass);
break;
case Type::rect:
case Type::point:
compileCompound(expr, compiler, prePass);
break;
}
}
void SimpleField::compileString(ExprPtr expr, ResourceCompiler *compiler, bool prePass)
{
std::string str;
{
ResourceCompiler::FieldScope scope(compiler, this);
str = (value ? value : expr)->evaluateString(compiler);
}
if(arrayCount || type == Type::char_)
{
unsigned requestedSize = type == Type::char_ ? 1 : arrayCount->evaluateInt(compiler);
if(requestedSize < str.size())
str.erase(str.begin() + requestedSize, str.end());
else if(requestedSize > str.size())
str.insert(str.end(),requestedSize - str.size(), '\0');
}
int count = str.size();
if(type == Type::pstring)
{
if(count > 255)
{
str.erase(str.begin() + 255, str.end());
count = 255;
}
compiler->write(8, count);
}
else if(type == Type::wstring)
{
if(count > 65535)
{
str.erase(str.begin() + 65535, str.end());
count = 65535;
}
compiler->write(16, count);
}
for(char c : str)
compiler->write(8, c);
}
void SimpleField::compileInt(ExprPtr expr, ResourceCompiler *compiler, bool prePass)
{
int bitSize = 0;
switch(type)
{
case Type::bitstring:
bitSize = arrayCount->evaluateInt(compiler);
break;
case Type::boolean:
bitSize = 1;
break;
case Type::byte:
bitSize = 8;
break;
case Type::integer:
bitSize = 16;
break;
case Type::longint:
bitSize = 32;
break;
default:
assert(false);
}
int actualValue = 0;
ResourceCompiler::FieldScope scope(compiler, this);
actualValue = (value ? value : expr)->evaluateInt(compiler);
compiler->write(bitSize, actualValue);
}
void SimpleField::compileCompound(ExprPtr expr, ResourceCompiler *compiler, bool prePass)
{
ExprPtr val = value ? value : expr;
if(IdentifierExprPtr id = std::dynamic_pointer_cast<IdentifierExpr>(val))
{
ResourceCompiler::FieldScope scope(compiler, this);
val = id->lookup(compiler);
}
int count = 0;
switch(type)
{
case Type::rect:
count = 4;
break;
case Type::point:
count = 2;
break;
default:
assert(false);
}
CompoundExprPtr compound = std::dynamic_pointer_cast<CompoundExpr>(val);
assert(compound);
assert(compound->size() == count);
for(int i = 0; i < count; i++)
{
int x = compound->getItem(i)->evaluateInt(compiler);
compiler->write(16, x);
}
}
ArrayField::ArrayField(std::string name, ExprPtr count)
: name(name), arrayCount(count)
{
}
void ArrayField::compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass)
{
CompoundExprPtr compound = std::dynamic_pointer_cast<CompoundExpr>(expr);
assert(compound);
int i = 0;
int n = compound->size();
int iterations = 0;
while(i < n)
{
++iterations;
ResourceCompiler::ArrayScope scope(compiler, name, iterations);
for(FieldPtr f : fields)
{
if(f->needsValue())
{
assert(i < n);
f->compile(compound->getItem(i++), compiler, prePass);
}
else
f->compile(nullptr, compiler, prePass);
}
}
if(!prePass && arrayCount)
{
int expected = arrayCount->evaluateInt(compiler);
assert(expected == iterations);
}
}
LabelField::LabelField(std::string name)
: name(name)
{
}
bool LabelField::needsValue()
{
return false;
}
void LabelField::compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass)
{
compiler->defineLabel(name);
}
void SwitchField::addCase(const std::string name, FieldListPtr alternative)
{
cases[name] = alternative;
}
void SwitchField::compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass)
{
CaseExprPtr caseExpr = std::dynamic_pointer_cast<CaseExpr>(expr);
assert(caseExpr);
FieldListPtr caseDefinition = cases[caseExpr->tag];
assert(caseDefinition);
caseDefinition->compile(caseExpr->expr, compiler, prePass);
}
FillAlignField::FillAlignField(FillAlignField::Type type, bool isAlign, ExprPtr count)
: type(type), count(count), isAlign(isAlign)
{
}
bool FillAlignField::needsValue()
{
return false;
}
void FillAlignField::compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass)
{
int bitSize;
switch(type)
{
case Type::bit: bitSize = 1; break;
case Type::nibble: bitSize = 4; break;
case Type::byte: bitSize = 8; break;
case Type::word: bitSize = 16; break;
case Type::long_: bitSize = 32; break;
}
int actualCount = 1;
if(count)
actualCount = count->evaluateInt(compiler);
for(int i = 0; i < actualCount; i++)
{
int n;
if(isAlign)
{
int mask = bitSize - 1;
int pos = compiler->tell();
n = ((pos + mask) & ~mask) - pos;
}
else
n = bitSize;
compiler->write(n, 0);
}
}

166
Rez/ResourceDefinitions.h Normal file
View File

@ -0,0 +1,166 @@
#ifndef RESOURCEDEFINITIONS_H
#define RESOURCEDEFINITIONS_H
#include <iosfwd>
#include <memory>
#include <map>
#include "Expression.h"
#include "ResType.h"
class TypeSpec
{
ResType type;
int id;
public:
static const int noID = 65536;
TypeSpec() : id(noID) {}
TypeSpec(ResType type) : type(type), id(noID) {}
TypeSpec(ResType type, int id) : type(type), id(id) {}
ResType getType() const { return type; }
int getID() const { return id; }
bool hasID() const { return id != noID; }
bool operator<(TypeSpec y) const
{
if(type < y.type)
return true;
else if(y.type < type)
return false;
else
return id < y.id;
}
};
std::ostream& operator<<(std::ostream& out, TypeSpec ts);
class ResourceCompiler;
class Field
{
public:
virtual bool needsValue() { return true; }
virtual void compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass) = 0;
virtual ExprPtr lookupNamedValue(std::string) { return nullptr; }
};
typedef std::shared_ptr<Field> FieldPtr;
class SimpleField : public Field
{
public:
enum class Type
{
boolean, byte, integer, longint, rect, point, char_,
pstring, wstring, string, bitstring
};
enum class Attrs
{
none = 0, hex = 1, key = 2, unsigned_ = 4, literal = 8
};
Type type;
Attrs attrs = Attrs::none;
ExprPtr arrayCount;
ExprPtr value;
std::map<std::string, ExprPtr> namedValues;
ExprPtr lastNamedValue;
void addNamedValue(std::string n);
void addNamedValue(std::string n, ExprPtr val);
ExprPtr lookupNamedValue(std::string);
virtual bool needsValue();
virtual void compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass);
private:
void compileString(ExprPtr expr, ResourceCompiler *compiler, bool prePass);
void compileInt(ExprPtr expr, ResourceCompiler *compiler, bool prePass);
void compileCompound(ExprPtr expr, ResourceCompiler *compiler, bool prePass);
};
typedef std::shared_ptr<SimpleField> SimpleFieldPtr;
class FillAlignField : public Field
{
public:
enum class Type
{
bit, nibble, byte, word, long_
};
FillAlignField(Type type, bool isAlign, ExprPtr count = ExprPtr());
virtual bool needsValue();
virtual void compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass);
private:
Type type;
ExprPtr count;
bool isAlign;
};
inline SimpleField::Attrs operator|(SimpleField::Attrs a, SimpleField::Attrs b)
{
return SimpleField::Attrs( int(a) | int(b) );
}
class LabelField : public Field
{
std::string name;
public:
LabelField(std::string name);
virtual bool needsValue();
virtual void compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass);
};
typedef std::shared_ptr<LabelField> LabelFieldPtr;
class FieldList : public Field
{
protected:
std::vector<FieldPtr> fields;
public:
virtual ~FieldList();
void addField(FieldPtr field);
void addLabel(std::string name);
virtual void compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass);
};
typedef std::shared_ptr<FieldList> FieldListPtr;
class ArrayField : public FieldList
{
std::string name;
ExprPtr arrayCount;
public:
ArrayField(std::string name /* or empty */, ExprPtr count /* may be null*/);
virtual void compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass);
};
typedef std::shared_ptr<ArrayField> ArrayFieldPtr;
class SwitchField : public Field
{
std::map<std::string, FieldListPtr> cases;
public:
void addCase(const std::string name, FieldListPtr alternative);
virtual void compile(ExprPtr expr, ResourceCompiler *compiler, bool prePass);
};
typedef std::shared_ptr<SwitchField> SwitchFieldPtr;
class TypeDefinition : public FieldList
{
};
typedef std::shared_ptr<TypeDefinition> TypeDefinitionPtr;
#endif // RESOURCEDEFINITIONS_H

113
Rez/Rez.cc Normal file
View File

@ -0,0 +1,113 @@
#include <iostream>
#include "boost/program_options.hpp"
#include "boost/filesystem.hpp"
#include "boost/filesystem/fstream.hpp"
#include "RezParser.generated.hh"
#include "RezLexer.h"
#include "RezWorld.h"
#include "ResourceFiles.h"
#include "BinaryIO.h"
namespace po = boost::program_options;
namespace fs = boost::filesystem;
static po::options_description desc;
static void usage()
{
std::cerr << "Usage: " << "Rez [options] input-file\n";
std::cerr << desc << std::endl;
}
int main(int argc, const char *argv[])
{
desc.add_options()
("help,h", "show this help message")
("output,o", po::value<std::string>()->default_value("rez.output.rsrc"), "output file")
("append,a", "append to existing output file")
("type,t", po::value<std::string>()->default_value("rsrc"), "output file finder type code")
("creator,c", po::value<std::string>()->default_value("RSED"), "output file finder creator code")
("define,D", po::value<std::vector<std::string>>(), "predefine preprocessor symbol")
("include,I", po::value<std::vector<std::string>>(), "add include file path")
("debug,d", "debug logging")
;
po::options_description hidden, alldesc;
hidden.add_options()
("input", po::value<std::vector<std::string>>(), "input file" )
;
alldesc.add(desc).add(hidden);
po::variables_map options;
try
{
auto parsed = po::command_line_parser(argc, argv)
.options(alldesc)
.positional(po::positional_options_description().add("input", -1))
.style(po::command_line_style::default_style |
po::command_line_style::allow_long_disguise)
.run();
po::store(parsed, options);
}
catch(po::error& e)
{
std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
usage();
return 1;
}
po::notify(options);
if(options.count("help") || !options.count("input"))
{
usage();
return 0;
}
RezWorld world;
if(options.count("debug"))
world.verboseFlag = true;
std::string outfile = options["output"].as<std::string>();
fs::path dataPath = outfile;
fs::create_directory(dataPath.parent_path() / ".rsrc");
fs::create_directory(dataPath.parent_path() / ".finf");
fs::path rsrcPath = dataPath.parent_path() / ".rsrc" / dataPath.filename();
fs::path finfPath = dataPath.parent_path() / ".finf" / dataPath.filename();
if(options.count("append"))
{
fs::ifstream rsrcIn(rsrcPath);
world.getResources().addResources(Resources(rsrcIn));
}
for(std::string fn : options["input"].as<std::vector<std::string>>())
{
RezLexer lexer(fn);
for(std::string define : options["define"].as<std::vector<std::string>>())
lexer.addDefine(define);
for(std::string path : options["include"].as<std::vector<std::string>>())
lexer.addIncludePath(path);
RezParser parser(lexer, world);
parser.parse();
}
{
fs::ofstream dataOut(dataPath);
fs::ofstream rsrcOut(rsrcPath);
fs::ofstream finfOut(finfPath);
world.getResources().writeFork(rsrcOut);
ostype(finfOut, options["type"].as<std::string>());
ostype(finfOut, options["creator"].as<std::string>());
for(int i = 8; i < 32; i++)
byte(finfOut, 0);
}
return 0;
}

144
Rez/RezLexer.cc Normal file
View File

@ -0,0 +1,144 @@
#include "RezLexer.h"
#include <boost/wave.hpp>
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>
#include <boost/wave/token_ids.hpp>
#include <boost/regex.hpp>
#include "RezLexerWaveToken.h"
namespace wave = boost::wave;
using namespace boost::wave;
static std::string readContents(std::istream&& instream)
{
instream.unsetf(std::ios::skipws);
return std::string(std::istreambuf_iterator<char>(instream.rdbuf()),
std::istreambuf_iterator<char>());
}
static std::string preFilter(std::string str)
{
boost::regex endif("#endif[^\r\n]*");
str = boost::regex_replace(str, endif, "#endif");
boost::regex dollar_escape("\\\\\\$([a-zA-Z0-9][a-zA-Z0-9])");
str = boost::regex_replace(str, dollar_escape, "\\\\0x$1");
return str;
}
struct load_file_to_string_filtered
{
template <typename IterContextT>
class inner
{
public:
template <typename PositionT>
static void init_iterators(IterContextT &iter_ctx,
PositionT const &act_pos, language_support language)
{
typedef typename IterContextT::iterator_type iterator_type;
// read in the file
std::ifstream instream(iter_ctx.filename.c_str());
if (!instream.is_open()) {
BOOST_WAVE_THROW_CTX(iter_ctx.ctx, preprocess_exception,
bad_include_file, iter_ctx.filename.c_str(), act_pos);
return;
}
iter_ctx.instring = preFilter(readContents(std::move(instream)));
iter_ctx.first = iterator_type(
iter_ctx.instring.begin(), iter_ctx.instring.end(),
PositionT(iter_ctx.filename), language);
iter_ctx.last = iterator_type();
}
private:
std::string instring;
};
};
typedef wave::cpplexer::lex_iterator<
wave::cpplexer::lex_token<> >
lex_iterator_type;
typedef wave::context<
std::string::iterator, lex_iterator_type,
load_file_to_string_filtered>
context_type;
typedef context_type::iterator_type pp_iterator_type;
struct RezLexer::Priv
{
std::string input;
context_type ctx;
pp_iterator_type iter;
Priv(std::string data, std::string name)
: input(data), ctx(input.begin(), input.end(), name.c_str())
{
}
};
RezLexer::RezLexer(std::string filename)
: RezLexer(filename, readContents(std::ifstream(filename)))
{
}
RezLexer::RezLexer(std::string filename, const std::string &data)
{
pImpl.reset(new Priv(preFilter(data), filename));
pImpl->ctx.add_include_path("/home/wolfgang/Projects/Retro68/RIncludes");
pImpl->ctx.add_macro_definition("DeRez=0");
pImpl->ctx.add_macro_definition("Rez=1");
pImpl->ctx.add_macro_definition("true=1");
pImpl->ctx.add_macro_definition("false=0");
pImpl->iter = pImpl->ctx.begin();
}
RezLexer::~RezLexer()
{
}
void RezLexer::addDefine(std::string str)
{
pImpl->ctx.add_macro_definition(str);
}
void RezLexer::addIncludePath(std::string path)
{
pImpl->ctx.add_include_path(path.c_str());
}
bool RezLexer::atEnd()
{
return pImpl->iter == pImpl->ctx.end();
}
RezLexer::WaveToken RezLexer::nextWave()
{
if(pImpl->iter == pImpl->ctx.end())
return WaveToken();
else
{
WaveToken tok = *pImpl->iter++;
return tok;
}
}
RezLexer::WaveToken RezLexer::peekWave()
{
return pImpl->iter == pImpl->ctx.end() ? WaveToken() : *pImpl->iter;
}

32
Rez/RezLexer.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef REZLEXER_H
#define REZLEXER_H
#include <memory>
class RezSymbol;
class RezLexer
{
struct Priv;
std::unique_ptr<Priv> pImpl;
std::string curFile;
class WaveToken;
bool atEnd();
WaveToken nextWave();
WaveToken peekWave();
public:
RezLexer(std::string filename);
RezLexer(std::string filename, const std::string& data);
~RezLexer();
RezSymbol nextToken();
void addDefine(std::string str);
void addIncludePath(std::string path);
};
#endif // REZLEXER_H

290
Rez/RezLexerNextToken.cc Normal file
View File

@ -0,0 +1,290 @@
#include "RezLexer.h"
#include "RezLexerWaveToken.h"
#include "RezParser.generated.hh"
#include <unordered_map>
#include <boost/regex.hpp>
using namespace boost::wave;
static int readInt(const char *str, const char *end = NULL, int baseOverride = 0)
{
int x = 0;
int base = 10;
if(baseOverride)
base = baseOverride;
else if(*str == '0')
{
base = 8;
++str;
if(*str == 'x' || *str == 'X')
{
base = 16;
++str;
}
if(*str == 'b' || *str == 'B')
{
base = 2;
++str;
}
}
else if(*str == 'b' || *str == 'B')
{
base = 2;
++str;
}
while(str != end && *str)
{
x *= base;
if(*str >= 'a' && *str <= 'z')
x += *str - 'a' + 10;
else if(*str >= 'A' && *str <= 'Z')
x += *str - 'A' + 10;
else if(*str >= '0' && *str <= '9')
x += *str - '0';
str++;
}
return x;
}
static int readCharLit(const char *str)
{
const char *p = str + 1;
const char *e = str + strlen(str) - 1;
if(e - p != 4)
std::cout << "warning: CHAR LITERAL " << str << "\n";
int x = 0;
while(p != e)
{
x <<= 8;
x |= (*p) & 0xFF;
++p;
}
return x;
}
static std::string readStringLit(const char *str)
{
const char *p = str + 1;
const char *e = str + strlen(str) - 1;
std::ostringstream out;
while(p != e)
{
if(*p == '\\')
{
++p;
if(p != e)
{
switch(*p)
{
case 'n':
out << '\n'; ++p;
break;
case 'r':
out << '\r'; ++p;
break;
case 't':
out << '\t'; ++p;
break;
case '0':
case '1':
case '2':
case '3':
if(p + 3 > e)
continue;
if(p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
{
if(p + 4 > e)
continue;
out << (char)readInt(p+2, p+4, 16);
p += 4;
}
else
{
out << (char)readInt(p, p+3, 8);
p += 3;
}
break;
case '$':
{
if(p + 3 > e)
continue;
out << (char)readInt(p+1, p+3, 16);
p += 3;
}
break;
}
}
}
else
{
out << *p++;
}
}
return out.str();
}
RezSymbol RezLexer::nextToken()
{
for(auto tok = nextWave(); tok != T_EOI && tok != T_EOF; tok = nextWave())
{
if(IS_CATEGORY(tok, WhiteSpaceTokenType))
continue;
else if(IS_CATEGORY(tok, EOLTokenType))
continue;
else if(tok == T_PP_LINE)
{
while(tok != T_EOI && tok != T_EOF && !IS_CATEGORY(tok, EOLTokenType))
tok = nextWave();
continue;
}
else
{
//std::cout << "{" << std::hex << (token_id)tok << std::dec << "|" << tok.get_value() << "}\n";
auto pos = tok.get_position();
curFile = pos.get_file().c_str();
auto yypos = yy::position(&curFile, pos.get_line(), pos.get_column());
yy::location loc(yypos);
if(tok == (UnknownTokenType | '"'))
{
return RezParser::make_STRINGLIT("Hello, world.", loc);
}
else if(IS_CATEGORY(tok, IdentifierTokenType) || IS_CATEGORY(tok, KeywordTokenType) || IS_CATEGORY(tok, BoolLiteralTokenType))
{
typedef decltype(&RezParser::make_TYPE) memfun;
#define KEYWORD(upper, lower) \
{ lower, &RezParser::make_ ## upper }
static std::unordered_map<std::string, memfun> keywords = {
KEYWORD(TYPE, "type"),
KEYWORD(RESOURCE, "resource"),
KEYWORD(DATA, "data"),
KEYWORD(READ, "read"),
KEYWORD(INCLUDE, "include"),
KEYWORD(CHANGE, "change"),
KEYWORD(DELETE, "delete"),
KEYWORD(ARRAY,"array"),
KEYWORD(SWITCH, "switch"),
KEYWORD(CASE, "case"),
KEYWORD(AS, "as"),
KEYWORD(FILL,"fill"),
KEYWORD(ALIGN, "align"),
KEYWORD(HEX,"hex"),
KEYWORD(KEY, "key"),
KEYWORD(WIDE,"wide"),
KEYWORD(UNSIGNED, "unsigned"),
KEYWORD(LITERAL, "literal"),
KEYWORD(BOOLEAN, "boolean"),
KEYWORD(BIT, "bit"),
KEYWORD(NIBBLE, "nibble"),
KEYWORD(BYTE, "byte"),
KEYWORD(CHAR, "char"),
KEYWORD(WORD, "word"),
KEYWORD(INTEGER, "integer"),
KEYWORD(LONG, "long"),
KEYWORD(LONGINT, "longint"),
KEYWORD(PSTRING, "pstring"),
KEYWORD(PSTRING, "wstring"),
KEYWORD(STRING, "string"),
KEYWORD(POINT, "point"),
KEYWORD(RECT, "rect"),
KEYWORD(BITSTRING, "bitstring"),
KEYWORD(INTEGER, "int"),
KEYWORD(DOLLAR, "$"),
KEYWORD(FUN_COUNTOF, "$$countof"),
KEYWORD(FUN_ARRAYINDEX, "$$arrayindex"),
KEYWORD(FUN_READ, "$$read"),
KEYWORD(FUN_BITFIELD, "$$bitfield"),
KEYWORD(FUN_WORD, "$$word"),
KEYWORD(FUN_BYTE, "$$byte"),
KEYWORD(FUN_LONG, "$$long"),
};
std::string s = tok.get_value().c_str();
std::string lower = s;
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
auto p = keywords.find(lower);
if(p == keywords.end())
{
//std::cout << "id: " << s << std::endl;
return RezParser::make_IDENTIFIER(lower, loc);
}
else
{
//std::cout << "key: " << s << std::endl;
return (*p->second)(loc);
}
}
else if(tok == T_INTLIT)
{
if(tok.get_value() == "0")
{
auto tok2 = peekWave();
while(tok2 != T_EOI && tok2 != T_EOF && IS_CATEGORY(tok2, WhiteSpaceTokenType))
nextWave(), tok2 = peekWave();
//std::cout << "!" << std::hex << (token_id)tok2 << std::dec << "|" << tok2.get_value() << "!\n";
static boost::regex binlit("[bB][01]+");
if(tok2 == T_IDENTIFIER && boost::regex_match(tok2.get_value().c_str(), binlit))
tok = nextWave();
}
return RezParser::make_INTLIT(readInt(tok.get_value().c_str()), loc);
}
else
{
#define NOVAL_TOK(name) \
case T_ ## name: /*std::cout << #name << std::endl;*/ return RezParser::make_ ## name(loc)
switch(token_id(tok))
{
case T_INTLIT: return RezParser::make_INTLIT(readInt(tok.get_value().c_str()), loc);
case T_CHARLIT: return RezParser::make_CHARLIT(readCharLit(tok.get_value().c_str()), loc);
case T_STRINGLIT: return RezParser::make_STRINGLIT(readStringLit(tok.get_value().c_str()), loc);
NOVAL_TOK(LEFTBRACE);
NOVAL_TOK(RIGHTBRACE);
NOVAL_TOK(LEFTBRACKET);
NOVAL_TOK(RIGHTBRACKET);
NOVAL_TOK(LEFTPAREN);
NOVAL_TOK(RIGHTPAREN);
NOVAL_TOK(SEMICOLON);
NOVAL_TOK(COMMA);
NOVAL_TOK(PLUS);
NOVAL_TOK(MINUS);
NOVAL_TOK(DIVIDE);
NOVAL_TOK(STAR);
NOVAL_TOK(ASSIGN);
NOVAL_TOK(COLON);
NOVAL_TOK(SHIFTLEFT);
NOVAL_TOK(SHIFTRIGHT);
NOVAL_TOK(EQUAL);
NOVAL_TOK(NOTEQUAL);
NOVAL_TOK(AND);
NOVAL_TOK(OR);
NOVAL_TOK(XOR);
NOVAL_TOK(COMPL);
default:
return RezParser::make_BADTOKEN(tok.get_value().c_str(), loc);
}
}
}
}
return RezSymbol();
}

15
Rez/RezLexerWaveToken.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef REZLEXERWAVETOKEN_H
#define REZLEXERWAVETOKEN_H
#include "RezLexer.h"
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>
class RezLexer::WaveToken : public boost::wave::cpplexer::lex_token<>
{
public:
WaveToken() = default;
WaveToken(const boost::wave::cpplexer::lex_token<> & o) : boost::wave::cpplexer::lex_token<>(o) {}
};
#endif // REZLEXERWAVETOKEN_H

462
Rez/RezParser.yy Normal file
View File

@ -0,0 +1,462 @@
%require "3.0.2"
%defines
%define parser_class_name {RezParser}
%skeleton "lalr1.cc"
%locations;
%define api.token.constructor
%define api.value.type variant
%define parse.assert
%token<std::string> IDENTIFIER;
%token<int> CHARLIT;
%token<std::string> STRINGLIT;
%token<int> INTLIT;
%token<std::string> BADTOKEN;
%token LEFTBRACE "{";
%token RIGHTBRACE "}";
%token LEFTBRACKET "[";
%token RIGHTBRACKET "]";
%token LEFTPAREN "(";
%token RIGHTPAREN ")";
%token SEMICOLON ";";
%token COMMA ",";
%token PLUS "+";
%token MINUS "-";
%token DIVIDE "/";
%token STAR "*";
%token ASSIGN "=";
%token COLON ":";
%token SHIFTLEFT "<<";
%token SHIFTRIGHT ">>";
%token EQUAL "==";
%token NOTEQUAL "!=";
%token AND "&";
%token OR "|";
%token XOR "^";
%token COMPL "~";
%token DOLLAR "$";
%token TYPE "type";
%token RESOURCE "resource";
%token DATA "data";
%token READ "read";
%token INCLUDE "include";
%token CHANGE "change";
%token DELETE "delete";
%token ARRAY "array";
%token SWITCH "switch";
%token CASE "case";
%token AS "as";
%token FILL "fill";
%token ALIGN "align";
%token HEX "hex";
%token KEY "key";
%token WIDE "wide";
%token LITERAL "literal";
%token UNSIGNED "unsigned";
%token BOOLEAN "boolean";
%token BIT "bit";
%token NIBBLE "nibble";
%token BYTE "byte";
%token CHAR "char";
%token WORD "word";
%token INTEGER "integer";
%token LONG "long";
%token LONGINT "longint";
%token PSTRING "pstring";
%token WSTRING "wstring";
%token STRING "string";
%token POINT "point";
%token RECT "rect";
%token BITSTRING "bitstring";
%token FUN_COUNTOF "$$countof";
%token FUN_ARRAYINDEX "$$arrayindex";
%token FUN_READ "$$read";
%token FUN_BITFIELD "$$bitfield";
%token FUN_WORD "$$word";
%token FUN_BYTE "$$byte";
%token FUN_LONG "$$long";
/*
%left "|";
%left "^";
%left "&";
%left "==" "!=";
%left ">>" "<<";
%left "+" "-";
%left "*" "/";
*/
%param { RezLexer& lexer }
%param { RezWorld& world }
%code requires {
#include "ResourceDefinitions.h"
#include "Expression.h"
#include "ResSpec.h"
#define YY_NULLPTR nullptr
class RezLexer;
class RezWorld;
}
%code provides {
using yy::RezParser;
//using RezSymbol = yy::RezParser::symbol_type;
class RezSymbol : public yy::RezParser::symbol_type
{
public:
RezSymbol() = default;
RezSymbol(const yy::RezParser::symbol_type& x) : yy::RezParser::symbol_type(x) {}
};
}
%code {
#include "RezLexer.h"
#include "RezWorld.h"
#include "ResourceCompiler.h"
static yy::RezParser::symbol_type yylex(RezLexer& lexer, RezWorld&)
{
return lexer.nextToken();
}
void yy::RezParser::error(const location_type& loc, std::string const& err)
{
std::cerr << loc << ": " << err << std::endl;
}
static std::string fromHex(std::string hex)
{
std::string bin;
int nibble;
bool haveNibble = false;
for(std::string::iterator p = hex.begin(); p != hex.end(); ++p)
{
if(std::isspace(*p))
continue;
assert(isdigit(*p) || (tolower(*p) >= 'a' && tolower(*p) <= 'f'));
int digit;
if(isdigit(*p))
digit = *p - '0';
else
digit = tolower(*p) - 'a' + 0xA;
if(haveNibble)
{
bin += (char) ((nibble << 4) | digit);
haveNibble = false;
}
else
{
nibble = digit;
haveNibble = true;
}
}
return bin;
}
}
%%
%start rez;
rez : %empty
| rez type_definition ";"
| rez resource ";"
| rez data ";"
;
type_definition : "type" type_spec
{
TypeDefinitionPtr td = std::make_shared<TypeDefinition>();
world.addTypeDefinition($type_spec, td);
world.fieldLists.push(td);
}
"{" field_definitions "}"
{ world.fieldLists.pop(); if(world.verboseFlag) std::cout << "TYPE " << $2 << std::endl; }
| "type" type_spec "as" type_spec
{ if(world.verboseFlag) std::cout << "TYPE " << $2 << std::endl; }
;
%type <ResType> res_type;
res_type : CHARLIT { $$ = ResType($1); } ;
%type <TypeSpec> type_spec;
type_spec : res_type { $$ = TypeSpec($res_type); }
| res_type "(" INTLIT ")" { $$ = TypeSpec($res_type, $INTLIT); }
;
field_definitions : %empty
| field_definitions IDENTIFIER ":" { world.fieldLists.top()->addLabel($2); }
| field_definitions ";"
| field_definitions field_definition ";" { world.fieldLists.top()->addField($2); }
;
%type <FieldPtr> field_definition;
field_definition: simple_field_definition { $$ = $1; }
| array_definition { $$ = $1; }
| switch_definition { $$ = $1; }
| fill_statement { $$ = $1; }
| align_statement { $$ = $1; }
;
%type <SimpleFieldPtr> simple_field_definition;
simple_field_definition: field_attributes simpletype array_count_opt value_spec_opt
{
$$ = std::make_shared<SimpleField>();
$$->attrs = $field_attributes;
$$->type = $simpletype;
$$->arrayCount = $array_count_opt;
$$->value = $value_spec_opt;
}
| simple_field_definition IDENTIFIER
{ $$ = $1; $$->addNamedValue($IDENTIFIER); }
| simple_field_definition IDENTIFIER "=" value
{ $$ = $1; $$->addNamedValue($IDENTIFIER, $value); }
| simple_field_definition "," IDENTIFIER
{ $$ = $1; $$->addNamedValue($IDENTIFIER); }
| simple_field_definition "," IDENTIFIER "=" value
{ $$ = $1; $$->addNamedValue($IDENTIFIER, $value); }
;
%type <ExprPtr> array_count array_count_opt value_spec_opt value resource_item;
%type <ExprPtr> expression expression1 expression2 ;
%type <ExprPtr> expression3 expression4 expression5 ;
%type <ExprPtr> expression6 expression7 expression8;
value_spec_opt : %empty { $$ = nullptr; } | "=" value { $$ = $2; } ;
%type <SimpleField::Type> simpletype;
simpletype : "boolean" { $$ = SimpleField::Type::boolean; }
| "byte" { $$ = SimpleField::Type::byte; }
| "integer" { $$ = SimpleField::Type::integer; }
| "longint" { $$ = SimpleField::Type::longint; }
| "rect" { $$ = SimpleField::Type::rect; }
| "point" { $$ = SimpleField::Type::point; }
| "char" { $$ = SimpleField::Type::char_; }
| "pstring" { $$ = SimpleField::Type::pstring; }
| "wstring" { $$ = SimpleField::Type::wstring; }
| "string" { $$ = SimpleField::Type::string; }
| "bitstring" { $$ = SimpleField::Type::bitstring; }
;
%type <FieldPtr> fill_statement align_statement;
fill_statement : "fill" fill_unit array_count_opt
{ $$ = std::make_shared<FillAlignField>($fill_unit, false, $array_count_opt); }
;
align_statement : "align" fill_unit
{ $$ = std::make_shared<FillAlignField>($fill_unit, true); }
;
%type <FillAlignField::Type> fill_unit;
fill_unit : "bit" { $$ = FillAlignField::Type::bit; }
| "nibble" { $$ = FillAlignField::Type::nibble; }
| "byte" { $$ = FillAlignField::Type::byte; }
| "word" { $$ = FillAlignField::Type::word; }
| "long" { $$ = FillAlignField::Type::long_; }
;
%type <FieldPtr> array_definition;
array_definition:
array_attributes "array" array_name_opt array_count_opt
{
ArrayFieldPtr af = std::make_shared<ArrayField>($array_name_opt, $array_count_opt);
world.fieldLists.push(af);
}
"{" field_definitions "}"
{
$$ = world.fieldLists.top();
world.fieldLists.pop();
}
;
array_count : "[" expression "]" { $$ = $2; }
array_count_opt : %empty { $$ = nullptr; } | array_count { $$ = $1; };
%type <std::string> array_name_opt;
array_name_opt : %empty { $$ = ""; } | IDENTIFIER { $$ = $1; } ;
array_attributes: %empty | "wide" ;
%type <SimpleField::Attrs> field_attributes field_attribute;
field_attributes: %empty { $$ = SimpleField::Attrs::none; }
| field_attributes field_attribute { $$ = $1 | $2; }
;
field_attribute : "hex" { $$ = SimpleField::Attrs::hex; }
| "key" { $$ = SimpleField::Attrs::key; }
| "unsigned" { $$ = SimpleField::Attrs::unsigned_; }
| "literal" { $$ = SimpleField::Attrs::literal; }
;
%type <FieldPtr> switch_definition;
switch_definition:
"switch"
{ world.switches.push(std::make_shared<SwitchField>()); }
"{"
switch_cases
"}"
{
$$ = world.switches.top();
world.switches.pop();
}
;
switch_cases : %empty | switch_cases switch_case ;
switch_case : "case" IDENTIFIER ":"
{
world.fieldLists.push(std::make_shared<FieldList>());
}
field_definitions
{
world.switches.top()->addCase($IDENTIFIER, world.fieldLists.top());
world.fieldLists.pop();
}
;
value : expression { $$ = $1; }
| "{" resource_body "}" { $$ = $2; }
| string_expression { $$ = $1; }
;
expression : expression1 { $$ = $1; }
| expression "^" expression1 { $$ = std::make_shared<BinaryExpr>(BinaryOp::XOR, $1, $3); }
;
expression1 : expression2 { $$ = $1; }
| expression1 "&" expression2 { $$ = std::make_shared<BinaryExpr>(BinaryOp::AND, $1, $3); }
;
expression2 : expression3 { $$ = $1; }
| expression2 "|" expression3 { $$ = std::make_shared<BinaryExpr>(BinaryOp::OR, $1, $3); }
;
expression3 : expression4 { $$ = $1; }
| expression3 "==" expression4 { $$ = std::make_shared<BinaryExpr>(BinaryOp::EQUAL, $1, $3); }
| expression3 "!=" expression4 { $$ = std::make_shared<BinaryExpr>(BinaryOp::NOTEQUAL, $1, $3); }
;
expression4 : expression5 { $$ = $1; }
| expression4 ">>" expression5 { $$ = std::make_shared<BinaryExpr>(BinaryOp::SHIFTRIGHT, $1, $3); }
| expression4 "<<" expression5 { $$ = std::make_shared<BinaryExpr>(BinaryOp::SHIFTLEFT, $1, $3); }
;
expression5 : expression6 { $$ = $1; }
| expression5 "+" expression6 { $$ = std::make_shared<BinaryExpr>(BinaryOp::PLUS, $1, $3); }
| expression5 "-" expression6 { $$ = std::make_shared<BinaryExpr>(BinaryOp::MINUS, $1, $3); }
;
expression6 : expression7 { $$ = $1; }
| expression6 "*" expression7 { $$ = std::make_shared<BinaryExpr>(BinaryOp::MULTIPLY, $1, $3); }
| expression6 "/" expression7 { $$ = std::make_shared<BinaryExpr>(BinaryOp::DIVIDE, $1, $3); }
;
expression7 : expression8 { $$ = $1; }
| "-" expression7 { $$ = std::make_shared<UnaryExpr>(UnaryOp::MINUS, $2); }
| "+" expression7 { $$ = $2; }
| "~" expression7 { $$ = std::make_shared<UnaryExpr>(UnaryOp::COMPLEMENT, $2); }
;
expression8 : INTLIT { $$ = std::make_shared<IntExpr>($1); }
| CHARLIT { $$ = std::make_shared<IntExpr>($1); }
| identifier_expression { $$ = $1; }
| "(" expression ")" { $$ = $2; }
| "$$countof" "(" identifier_expression ")"
{ $$ = std::make_shared<CountOfExpr>($identifier_expression); }
| "$$arrayindex" "(" identifier_expression ")"
{ $$ = std::make_shared<ArrayIndexExpr>($identifier_expression); }
| "$$bitfield" "(" expression "," expression "," expression ")"
{ $$ = std::make_shared<PeekExpr>($3, $5, $7); }
| "$$word" "(" expression ")"
{ $$ = std::make_shared<PeekExpr>($3, 16); }
| "$$byte" "(" expression ")"
{ $$ = std::make_shared<PeekExpr>($3, 8); }
| "$$long" "(" expression ")"
{ $$ = std::make_shared<PeekExpr>($3, 32); }
;
%type <IdentifierExprPtr> identifier_expression;
identifier_expression : IDENTIFIER { $$ = std::make_shared<IdentifierExpr>($1); }
| IDENTIFIER
{ world.functionCalls.push(std::make_shared<IdentifierExpr>($1)); }
"[" function_argument_list1 "]"
{ $$ = world.functionCalls.top(); world.functionCalls.pop(); }
;
function_argument_list : %empty | function_argument_list1 ;
function_argument_list1 : expression
{ world.functionCalls.top()->addArgument($expression); }
| function_argument_list "," expression
{ world.functionCalls.top()->addArgument($expression); }
;
%type <ExprPtr> string_expression string_expression1;
string_expression : string_expression1 { $$ = $1; }
| string_expression string_expression1
{ $$ = std::make_shared<BinaryExpr>(BinaryOp::CONCAT, $1, $2); }
;
%type <std::string> stringlit;
stringlit : STRINGLIT { $$ = $1; }
| DOLLAR STRINGLIT { $$ = fromHex($2); }
;
string_expression1 : stringlit { $$ = std::make_shared<StringExpr>($1); }
| "$$read" "(" string_expression ")"
{ $$ = std::make_shared<ReadExpr>($string_expression); }
;
resource : "resource" res_spec "{" resource_body "}"
{
world.addResource($res_spec, $resource_body);
}
;
%type <ResSpec> res_spec;
res_spec : res_type "(" expression resource_attributes ")"
{ $$ = $resource_attributes( ResSpec($res_type, $expression->evaluateInt(nullptr)) ); }
%type <std::function<ResSpec(ResSpec)>> resource_attributes ;
resource_attributes : %empty { $$ = [](ResSpec s){ return s; }; }
| resource_attributes "," IDENTIFIER { $$ = $1; }
| resource_attributes "," string_expression { $$ = $1; }
;
%type <CompoundExprPtr> resource_body resource_body1;
resource_body : %empty { $$ = std::make_shared<CompoundExpr>(); }
| resource_body1 { $$ = $1; }
;
resource_body1 : resource_item { $$ = std::make_shared<CompoundExpr>(); $$->addItem($1); }
| resource_body1 "," resource_item { $$ = $1; $$->addItem($3); }
| resource_body1 ";" resource_item { $$ = $1; $$->addItem($3); }
| resource_body1 ";" { $$ = $1; }
;
resource_item : value { $$ = $1; }
| IDENTIFIER "{" resource_body "}" { $$ = std::make_shared<CaseExpr>($IDENTIFIER, $resource_body); }
;
data : "data" res_spec "{" string_expression "}"
{
world.addData($res_spec, $string_expression->evaluateString(nullptr));
}
;
%%

45
Rez/RezWorld.cc Normal file
View File

@ -0,0 +1,45 @@
#include "RezWorld.h"
#include "ResourceCompiler.h"
#include "ResourceFiles.h"
#include <iostream>
RezWorld::RezWorld()
: verboseFlag(false)
{
}
void RezWorld::addTypeDefinition(TypeSpec spec, TypeDefinitionPtr type)
{
types[spec] = type;
}
TypeDefinitionPtr RezWorld::getTypeDefinition(ResType type, int id)
{
auto p = types.find(TypeSpec(type, id));
if(p != types.end())
return p->second;
p = types.find(TypeSpec(type));
if(p != types.end())
return p->second;
return nullptr;
}
void RezWorld::addResource(ResSpec spec, CompoundExprPtr body)
{
if(verboseFlag)
std::cout << "RESOURCE " << spec.type() << "(" << spec.id() << ", " << "\"" << spec.name() << "\"" << spec.attr() << ")" << std::endl;
TypeDefinitionPtr def = getTypeDefinition(spec.type(), spec.id());
ResourceCompiler compiler(def, body, verboseFlag);
compiler.compile();
resources.addResource(Resource(spec.type(), spec.id(), compiler.resourceData(), spec.name(), spec.attr()));
}
void RezWorld::addData(ResSpec spec, const std::string &data)
{
if(verboseFlag)
std::cout << "DATA " << spec.type() << "(" << spec.id() << ", " << "\"" << spec.name() << "\"" << spec.attr() << ")" << std::endl;
resources.addResource(Resource(spec.type(), spec.id(), data, spec.name(), spec.attr()));
}

37
Rez/RezWorld.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef REZWORLD_H
#define REZWORLD_H
#include <map>
#include <stack>
#include <string>
#include "ResourceDefinitions.h"
#include "Expression.h"
#include "ResourceFiles.h"
#include "ResSpec.h"
class RezWorld
{
friend class RezParser;
std::map<TypeSpec, TypeDefinitionPtr> types;
std::stack<FieldListPtr> fieldLists;
std::stack<IdentifierExprPtr> functionCalls;
std::stack<SwitchFieldPtr> switches;
Resources resources;
public:
RezWorld();
void addTypeDefinition(TypeSpec spec, TypeDefinitionPtr type);
TypeDefinitionPtr getTypeDefinition(ResType type, int id);
void addResource(ResSpec spec, CompoundExprPtr body);
void addData(ResSpec spec, const std::string& data);
Resources& getResources() { return resources; }
bool verboseFlag;
};
#endif // REZWORLD_H

33
Rez/Test.r Normal file
View File

@ -0,0 +1,33 @@
/*#include "Types.r"
*/
#include "/home/wolfgang/Projects/Retro68/CExamples/Sample.r"
type 'TEST' {
integer zero, one, two, answer = 42, missed;
longint;
integer = (after - before) / 8;
integer = $$CountOf(foo);
before:
array foo {
integer = $$ArrayIndex(foo);
integer;
integer;
};
string;
after:
;
};
resource 'TEST' (128) {
answer,
0x1234,
{ 1, 2; 3, 4; },
"Hello, "
"world: "
$"Abcd 1234";
};

4
Rez/Test/CMakeLists.txt Normal file
View File

@ -0,0 +1,4 @@
set(CMAKE_CXX_FLAGS "--std=c++11 -Wall -Wno-multichar")
find_package(Boost COMPONENTS unit_test_framework)
add_executable(RezUnitTests UnitTests.cc)
target_link_libraries(RezUnitTests RezLib ${Boost_LIBRARIES})

134
Rez/Test/UnitTests.cc Normal file
View File

@ -0,0 +1,134 @@
#define BOOST_TEST_MODULE UnitTests
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include "RezLexer.h"
#include "RezParser.generated.hh"
#include "ResourceCompiler.h"
#include <iostream>
BOOST_AUTO_TEST_SUITE(LexSuite)
#define CHECKSYM(TOKEN, TYPE, VAL) \
do { \
RezSymbol t = lex.nextToken(); \
BOOST_CHECK_EQUAL(t.token(), TOKEN); \
if(t.token() == TOKEN) \
BOOST_CHECK_EQUAL(t.value.as<TYPE>(), VAL); \
} while(0)
#define CHECKSYM_(TOKEN) \
do { \
RezSymbol t = lex.nextToken(); \
BOOST_CHECK_EQUAL(t.token(), TOKEN); \
} while(0)
BOOST_AUTO_TEST_CASE(basicInt)
{
RezLexer lex("test", "123 0x456 0xaBcd9\n");
CHECKSYM(RezParser::token::INTLIT, int, 123);
CHECKSYM(RezParser::token::INTLIT, int, 0x456);
CHECKSYM(RezParser::token::INTLIT, int, 0xabcd9);
CHECKSYM_(0);
}
BOOST_AUTO_TEST_CASE(alternateHex)
{
RezLexer lex("test", "$456 $aBcd9\n");
CHECKSYM(RezParser::token::INTLIT, int, 0x456);
CHECKSYM(RezParser::token::INTLIT, int, 0xabcd9);
CHECKSYM_(0);
}
BOOST_AUTO_TEST_CASE(noNewlineAtEOF)
{
RezLexer lex("test", "123 456");
CHECKSYM(RezParser::token::INTLIT, int, 123);
CHECKSYM(RezParser::token::INTLIT, int, 456);
CHECKSYM_(0);
}
BOOST_AUTO_TEST_CASE(strings)
{
RezLexer lex("test", R"rez(
"Hello, world."
"Foo \n"
"\r Quux"
"\001\002\003"
"\0x42\0x43"
"Blah \$5F"
)rez" "\n");
CHECKSYM(RezParser::token::STRINGLIT, std::string, "Hello, world.");
CHECKSYM(RezParser::token::STRINGLIT, std::string, "Foo \n");
CHECKSYM(RezParser::token::STRINGLIT, std::string, "\r Quux");
CHECKSYM(RezParser::token::STRINGLIT, std::string, "\001\002\003");
CHECKSYM(RezParser::token::STRINGLIT, std::string, "\x42\x43");
CHECKSYM(RezParser::token::STRINGLIT, std::string, "Blah \x5F");
CHECKSYM_(0);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(BinarySuite)
BOOST_AUTO_TEST_CASE(bytes)
{
BinaryOutput out;
out.write(8, 'a');
out.write(8, 'b');
out.write(8, 'c');
BOOST_CHECK_EQUAL(out.resourceData(), "abc");
}
BOOST_AUTO_TEST_CASE(multibyte)
{
BinaryOutput out;
out.write(32, 'abcd');
BOOST_CHECK_EQUAL(out.resourceData(), "abcd");
}
BOOST_AUTO_TEST_CASE(subbyte)
{
BinaryOutput out;
out.write(4, 6);
out.write(4, 1);
out.write(2, 1);
out.write(2, 2);
out.write(4, 2);
out.write(3, 3);
out.write(2, 0);
out.write(3, 3);
BOOST_CHECK_EQUAL(out.resourceData(), "abc");
}
BOOST_AUTO_TEST_CASE(peek)
{
BinaryOutput out;
for(char c : "Hello, world.")
if(c != 0)
out.write(8, c);
BOOST_CHECK_EQUAL(out.resourceData(), "Hello, world.");
BOOST_CHECK_EQUAL(out.peek(0,8), 'H');
BOOST_CHECK_EQUAL(out.peek(32,8), 'o');
BOOST_CHECK_EQUAL(out.peek(0,32), 'Hell');
BOOST_CHECK_EQUAL(out.peek(40,32), ', wo');
BOOST_CHECK_EQUAL(out.peek(1,8), 'H' * 2);
BOOST_CHECK_EQUAL(out.peek(2,8), 0x21);
BOOST_CHECK_EQUAL(out.peek(2,30), 'Hell' & 0x3FFFFFFF);
BOOST_CHECK_EQUAL(out.peek(2,32), ('Hell' & 0x3FFFFFFF) << 2 | ('o' >> 6) );
BOOST_CHECK_EQUAL(out.peek(4,3), 4);
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -22,6 +22,7 @@ set( RETRO68_ROOT "" CACHE PATH "path to root of Retro68 Toolchain" )
set( CMAKE_INSTALL_PREFIX "${RETRO68_ROOT}/m68k-unknown-elf/" CACHE PATH "installation prefix" )
set( MAKE_APPL "${RETRO68_ROOT}/bin/MakeAPPL" )
set( REZ "${RETRO68_ROOT}/bin/Rez" )
set( CMAKE_C_COMPILER "${RETRO68_ROOT}/bin/m68k-unknown-elf-gcc" )
set( CMAKE_CXX_COMPILER "${RETRO68_ROOT}/bin/m68k-unknown-elf-g++" )