mirror of
https://github.com/autc04/Retro68.git
synced 2024-12-23 10:31:22 +00:00
Merge branch 'Rez'
Conflicts: MakeAPPL/main.cc
This commit is contained in:
commit
4351647dba
@ -69,4 +69,5 @@ add_subdirectory(Launcher)
|
||||
else()
|
||||
add_subdirectory(MakeAPPL)
|
||||
add_subdirectory(ASFilter)
|
||||
add_subdirectory(Rez)
|
||||
endif()
|
||||
|
50
MakeAPPL/BinaryIO.cc
Normal file
50
MakeAPPL/BinaryIO.cc
Normal 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
19
MakeAPPL/BinaryIO.h
Normal 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
|
@ -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
58
MakeAPPL/ResType.cc
Normal 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
35
MakeAPPL/ResType.h
Normal 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
133
MakeAPPL/ResourceFiles.cc
Normal 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
44
MakeAPPL/ResourceFiles.h
Normal 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
|
210
MakeAPPL/main.cc
210
MakeAPPL/main.cc
@ -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
71
Rez/CMakeLists.txt
Normal 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
211
Rez/Expression.cc
Normal 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
162
Rez/Expression.h
Normal 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
12
Rez/README.md
Normal 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
24
Rez/ResSpec.h
Normal 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
191
Rez/ResourceCompiler.cc
Normal 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
94
Rez/ResourceCompiler.h
Normal 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
327
Rez/ResourceDefinitions.cc
Normal 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
166
Rez/ResourceDefinitions.h
Normal 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
113
Rez/Rez.cc
Normal 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
144
Rez/RezLexer.cc
Normal 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
32
Rez/RezLexer.h
Normal 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
290
Rez/RezLexerNextToken.cc
Normal 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
15
Rez/RezLexerWaveToken.h
Normal 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
462
Rez/RezParser.yy
Normal 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
45
Rez/RezWorld.cc
Normal 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
37
Rez/RezWorld.h
Normal 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
33
Rez/Test.r
Normal 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
4
Rez/Test/CMakeLists.txt
Normal 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
134
Rez/Test/UnitTests.cc
Normal 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()
|
@ -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++" )
|
||||
|
Loading…
Reference in New Issue
Block a user