Add Resource as interface for in-memory resources

This commit is contained in:
2015-06-08 22:44:07 +02:00
parent 584da2e3e8
commit ed32a1290e
15 changed files with 136 additions and 80 deletions

View File

@@ -9,13 +9,13 @@ struct ChunkHeader {
uint32_t length;
};
IffFile::IffFile(uint8_t const* base, size_t length)
: base_(base), length_(length)
IffFile::IffFile(Resource const& res)
: res_(res)
{
size_t pos = 0;
while (pos < length) {
roots_.push_back(parseObject(base+pos, length-pos));
pos += roots_.back()->getSize() + 8;
while (pos < res_.size()) {
roots_.push_back(parseObject(res_.data()+pos, res_.size()-pos));
pos += roots_.back()->size() + 8;
}
}
@@ -27,7 +27,7 @@ static void _printStructure(IffFile::Object const& obj, unsigned level)
{
for (unsigned i = 0;i < level;++i)
putchar('\t');
printf("%s Length %lu (0x%.lx)", obj.getType().c_str(), obj.getSize(), obj.getSize());
printf("%s Length %lu (0x%.lx)", obj.getType().c_str(), obj.size(), obj.size());
if (obj.isForm()) {
auto& form = dynamic_cast<IffFile::Form const&>(obj);
@@ -129,7 +129,7 @@ IffFile::Form::Form(std::string type, uint8_t const* base, size_t length)
size_t pos = 4;
while (pos+8 < length) {
children_.push_back(parseObject(base+pos, length-pos));
pos += 8 + children_.back()->getSize();
pos += 8 + children_.back()->size();
if (pos%2 != 0)
++pos;

View File

@@ -7,9 +7,11 @@
#include <memory>
#include <typeinfo>
#include "Resource.hh"
class IffFile {
public:
IffFile(uint8_t const* base, size_t length);
IffFile(Resource const& res);
~IffFile();
@@ -109,12 +111,12 @@ public:
std::vector<std::unique_ptr<Object> >::const_iterator implIt_;
};
class Object {
class Object : public Resource {
public:
Object(std::string type, uint8_t const* base, size_t length);
Object(Object const& copy) = delete;
virtual ~Object() {
~Object() override {
}
std::string const& getType() const {
@@ -125,10 +127,14 @@ public:
return (typeid(*this) == typeid(Form));
}
size_t getSize() const {
size_t size() const override {
return length_;
}
uint8_t const* data() const override {
return base_;
}
uint8_t const* begin() const {
return base_;
}
@@ -138,6 +144,10 @@ public:
}
operator std::string() const;
operator bool() const override {
return true;
}
protected:
uint8_t const* base_;
@@ -188,8 +198,7 @@ public:
private:
static std::unique_ptr<Object> parseObject(uint8_t const* base, size_t length);
uint8_t const* base_;
const size_t length_;
Resource const& res_;
std::vector<std::unique_ptr<Object> > roots_;
};

View File

@@ -8,8 +8,8 @@
#include "decompress.hh"
#include "MveDecoder.hh"
MveDecoder::MveDecoder(uint8_t const* base, size_t length)
: iff_(base, length), width_(320), height_(165)
MveDecoder::MveDecoder(Resource const& res)
: iff_(res), width_(320), height_(165)
{
//iff_.printStructure();
@@ -26,7 +26,7 @@ MveDecoder::MveDecoder(uint8_t const* base, size_t length)
// Parse _PC_ chunk
auto& PC = *it++;
if ((PC.getType() != "_PC_") ||
(PC.getSize() != 12))
(PC.size() != 12))
throw FormatException{"_PC_ chunk missing or wrong size"};
const uint32_t PC_PALTcount = readU32LE(PC.begin()+8);
@@ -37,7 +37,7 @@ MveDecoder::MveDecoder(uint8_t const* base, size_t length)
// // Parse SOND chunk
// auto& SOND = *it++;
// if ((SOND.getType() != "SOND") ||
// (SOND.getSize() != 4))
// (SOND.size() != 4))
// throw FormatException{"SOND chunk missing or wrong size"};
// const uint32_t SONDc = readU32LE(SOND.begin());
@@ -53,7 +53,7 @@ MveDecoder::MveDecoder(uint8_t const* base, size_t length)
for (;it != rootForm.childrenEnd();++it) {
if (it->getType() == "SIZE") {
if (it->getSize() != 8)
if (it->size() != 8)
throw FormatException{"Unexpected SIZE size"};
width_ = readU32LE(it->begin());
@@ -61,7 +61,7 @@ MveDecoder::MveDecoder(uint8_t const* base, size_t length)
} else if (it->getType() == "PALT") {
if (curPALT > PC_PALTcount)
throw FormatException{"Number of PALT chunks exceeds amount specified in _PC_"};
if (it->getSize() != 768)
if (it->size() != 768)
throw FormatException{"Unexpected PALT size"};
palts_.emplace_back();
@@ -95,11 +95,11 @@ MveDecoder::MveDecoder(uint8_t const* base, size_t length)
throw FormatException{"INDX after SHOT"};
haveIndex = true;
for(unsigned i = 0;i < it->getSize()/4;++i)
for(unsigned i = 0;i < it->size()/4;++i)
branches.push_back(readU32LE(it->begin()+i*4));
branches_.resize(branches.size());
} else if (it->getType() == "BRCH") {
size_t ofs = it->begin()-base-8;
size_t ofs = it->begin()-res.data()-8;
auto idxIt = std::find(branches.begin(), branches.end(), ofs);
if (idxIt == branches.end())
throw FormatException{"Could not resolve branch " + std::to_string(ofs)};
@@ -276,13 +276,13 @@ unsigned MveDecoder::Movie::getHeight() const
void MveDecoder::Movie::parseVGA(IffFile::Object const& vga)
{
// Parse header
if (vga.getSize() < 8)
if (vga.size() < 8)
throw FormatException{"VGA chunk smaller than VGA header"};
std::array<uint16_t, 4> segOfs;
for(unsigned i = 0;i < segOfs.size();++i) {
segOfs[i] = readU16LE(vga.begin()+i*2);
if (segOfs[i] >= vga.getSize())
if (segOfs[i] >= vga.size())
throw FormatException{"Segment offset exceeds chunk"};
}
@@ -291,7 +291,7 @@ void MveDecoder::Movie::parseVGA(IffFile::Object const& vga)
((segOfs[1] && (segOfs[1] - segOfs[0]) < 45)) ||
((segOfs[2] && (segOfs[2] - segOfs[0]) < 45)) ||
((segOfs[3] && (segOfs[3] - segOfs[0]) < 45)) ||
(vga.getSize() - segOfs[0] < 45))
(vga.size() - segOfs[0] < 45))
throw FormatException{"Segment 1 too small"};
commandBuf_.clear();
@@ -301,7 +301,7 @@ void MveDecoder::Movie::parseVGA(IffFile::Object const& vga)
size_t pixelCount = 0;
if (segOfs[3]) {
pixelCount = parsePixels_(vga.begin()+segOfs[3], vga.getSize()-segOfs[3],
pixelCount = parsePixels_(vga.begin()+segOfs[3], vga.size()-segOfs[3],
pixelBuf_.data(), pixelBuf_.size());
maxPixel_ = std::max(maxPixel_, pixelCount);
}
@@ -309,7 +309,7 @@ void MveDecoder::Movie::parseVGA(IffFile::Object const& vga)
#ifdef VIDDEBUG_
printf("%lu commands, %lu pixel data, %u mvecs, %lu size data\n",
commands.size(), pixels.size(), (segOfs[2]?(segOfs[3]-segOfs[2]):0),
segOfs[2]?(segOfs[2]-segOfs[1]):(segOfs[3]?(segOfs[3]-segOfs[1]):(vga.getSize()-segOfs[1])));
segOfs[2]?(segOfs[2]-segOfs[1]):(segOfs[3]?(segOfs[3]-segOfs[1]):(vga.size()-segOfs[1])));
#endif
// Interpret command stream to render frame
@@ -352,7 +352,7 @@ void MveDecoder::Movie::parseVGA(IffFile::Object const& vga)
if (!segOfs[1] ||
(segOfs[2] && (segOfs[1]+seg2Idx >= segOfs[2])) ||
(segOfs[3] && (segOfs[1]+seg2Idx >= segOfs[3])) ||
(segOfs[1]+seg2Idx >= vga.getSize()))
(segOfs[1]+seg2Idx >= vga.size()))
throw FormatException{"Segment 2 overrun"};
size = vga.begin()[segOfs[1]+seg2Idx++]&0xff;
break;
@@ -361,7 +361,7 @@ void MveDecoder::Movie::parseVGA(IffFile::Object const& vga)
if (!segOfs[1] ||
(segOfs[2] && (segOfs[1]+seg2Idx+1 >= segOfs[2])) ||
(segOfs[3] && (segOfs[1]+seg2Idx >= segOfs[3])) ||
(segOfs[1]+seg2Idx+1 >= vga.getSize()))
(segOfs[1]+seg2Idx+1 >= vga.size()))
throw FormatException{"Segment 2 overrun"};
size = readU16BE(vga.begin()+segOfs[1]+seg2Idx);
seg2Idx += 2;
@@ -371,7 +371,7 @@ void MveDecoder::Movie::parseVGA(IffFile::Object const& vga)
if (!segOfs[1] ||
(segOfs[2] && (segOfs[1]+seg2Idx+2 >= segOfs[2])) ||
(segOfs[3] && (segOfs[1]+seg2Idx >= segOfs[3])) ||
(segOfs[1]+seg2Idx+2 >= vga.getSize()))
(segOfs[1]+seg2Idx+2 >= vga.size()))
throw FormatException{"Segment 2 overrun"};
size = readU24BE(vga.begin()+segOfs[1]+seg2Idx);
seg2Idx += 3;
@@ -410,7 +410,7 @@ void MveDecoder::Movie::parseVGA(IffFile::Object const& vga)
#endif
if (!segOfs[2] ||
(segOfs[3] && (segOfs[2]+seg3Idx > segOfs[3])) ||
(segOfs[2]+seg3Idx > vga.getSize()))
(segOfs[2]+seg3Idx > vga.size()))
throw FormatException{"Segment 3 overrun"};
const uint8_t mpos = vga.begin()[segOfs[2]+seg3Idx++];
@@ -467,7 +467,7 @@ void audioCB_(void* userdata, uint8_t *buf, int len)
}
while ((len > 0) && cbdata->cur) {
if (cbdata->pos >= (*cbdata->aCur)->getSize()) {
if (cbdata->pos >= (*cbdata->aCur)->size()) {
cbdata->pos = 0;
++cbdata->aCur;
if (cbdata->aCur == cbdata->cur->AUDIs.end()) {
@@ -484,8 +484,8 @@ void audioCB_(void* userdata, uint8_t *buf, int len)
auto& aCur = *cbdata->aCur;
size_t toCopy = len;
if (cbdata->pos+toCopy > aCur->getSize())
toCopy = aCur->getSize()-cbdata->pos;
if (cbdata->pos+toCopy > aCur->size())
toCopy = aCur->size()-cbdata->pos;
std::copy(aCur->begin()+cbdata->pos, aCur->begin()+cbdata->pos+toCopy,
buf);
len -= toCopy;

View File

@@ -6,9 +6,11 @@
#include "IffFile.hh"
class Resource;
class MveDecoder {
public:
MveDecoder(uint8_t const* base, size_t length);
MveDecoder(Resource const& res);
size_t numBranches() const {
return branches_.size();

View File

@@ -4,8 +4,8 @@
#include "exceptions.hh"
#include "util.hh"
ObjDecoder::ObjDecoder(uint8_t const* base, size_t length)
: iff_(base, length)
ObjDecoder::ObjDecoder(Resource const& res)
: iff_(res)
{
auto& root = iff_.getRoot();
if (!root.isForm())
@@ -17,7 +17,7 @@ ObjDecoder::ObjDecoder(uint8_t const* base, size_t length)
for (auto realIt = rootForm.childrenBegin();realIt != rootForm.childrenEnd();++realIt) {
if (realIt->getType() == "INFO") {
name_ = *realIt;
name_ = static_cast<std::string>(*realIt);
printf("Name: \"%s\"\n", name_.c_str());
} else if (realIt->isForm()) {
auto& itForm = dynamic_cast<IffFile::Form const&>(*realIt);
@@ -79,9 +79,9 @@ void ObjDecoder::parsePOLY_(IffFile::Form const& form)
for (auto it = form.childrenBegin();it != form.childrenEnd();++it) {
if (it->getType() == "INFO") {
} else if (it->getType() == "VERT") {
if (it->getSize()%12 != 0)
if (it->size()%12 != 0)
throw FormatException{"Unexpected VERT size"};
for (size_t i = 0;i < it->getSize();i+=12) {
for (size_t i = 0;i < it->size();i+=12) {
std::array<int32_t, 3> vertex;
for (size_t j = 0;j < 3;++j)
vertex[j] = readU32LE(it->begin()+i+j*4);
@@ -137,10 +137,10 @@ void ObjDecoder::parseTRIS_(IffFile::Form const& form)
assert(face && maps);
if (face->getSize()%8 != 0)
if (face->size()%8 != 0)
throw FormatException{"Unexpected FACE size"};
auto numTriangles = face->getSize()/8;
if (maps->getSize() != numTriangles*16)
auto numTriangles = face->size()/8;
if (maps->size() != numTriangles*16)
printf("WARNING: Unexpected MAPS size\n");
// throw FormatException{"Unexpected MAPS size"};
@@ -185,10 +185,10 @@ void ObjDecoder::parseQUAD_(IffFile::Form const& form)
assert(face && maps);
if (face->getSize()%10 != 0)
if (face->size()%10 != 0)
throw FormatException{"Unexpected FACE size"};
auto numQuads = face->getSize()/10;
if (maps->getSize() != numQuads*20)
auto numQuads = face->size()/10;
if (maps->size() != numQuads*20)
printf("WARNING: Unexpected MAPS size\n");
//throw FormatException{"Unexpected MAPS size"};
@@ -225,12 +225,12 @@ void ObjDecoder::parseDETA_(IffFile::Form const& form)
if (std::stoi(it->getType().substr(3)) != lvl)
throw FormatException{"Unexpected detail level"};
if (it->getSize() < 4)
if (it->size() < 4)
throw FormatException{"DETA child too small"};
// uint32_t unkown = readU32LE(it->begin());
detailLevels_.emplace_back();
for (size_t pos = 4;pos < it->getSize()-1;pos += 2)
for (size_t pos = 4;pos < it->size()-1;pos += 2)
detailLevels_.back().push_back(readU16LE(it->begin()+pos));
printf("LVL %u: %lu entries\n", lvl, detailLevels_.back().size());
@@ -250,7 +250,7 @@ void ObjDecoder::parseTXMS_(IffFile::Form const& form)
auto& info = *it++;
if (info.getType() != "INFO")
throw FormatException{"Missing INFO in TXMS"};
if (info.getSize() != 4)
if (info.size() != 4)
throw FormatException{"Unexpected INFO size in TXMS"};
uint16_t numTXMP, numTXMA;
@@ -263,7 +263,7 @@ void ObjDecoder::parseTXMS_(IffFile::Form const& form)
texAnims_.reserve(numTXMA);
for (;it != form.childrenEnd();++it) {
if (it->getType() == "TXMP") {
if (it->getSize() < 12u)
if (it->size() < 12u)
throw FormatException{"TXMP smaller than header"};
Texture tex;
@@ -277,12 +277,12 @@ void ObjDecoder::parseTXMS_(IffFile::Form const& form)
tex.height = readU16LE(it->begin()+10);
printf("TXMP size %hux%hu\n", tex.width, tex.height);
if (it->getSize() < 12u+tex.width*tex.height)
if (it->size() < 12u+tex.width*tex.height)
throw FormatException{"TXMP too small for pixel data"};
tex.pixels = std::vector<uint8_t>(it->begin()+12, it->begin()+12+tex.width*tex.height);
textures_.emplace_back(std::move(tex));
} else if (it->getType() == "TXMA") {
if (it->getSize() < 14u)
if (it->size() < 14u)
throw FormatException{"TXMA smaller than header"};
TextureAnimation tex;
@@ -297,7 +297,7 @@ void ObjDecoder::parseTXMS_(IffFile::Form const& form)
tex.frames = readU16LE(it->begin()+12);
printf("TXMA size %hux%hu, %hu frames\n", tex.width, tex.height, tex.frames);
if (it->getSize() < 14u+tex.width*tex.height*tex.frames)
if (it->size() < 14u+tex.width*tex.height*tex.frames)
throw FormatException{"TXMA too small for pixel data"};
tex.pixels.reserve(tex.frames);

View File

@@ -8,9 +8,11 @@
#include "IffFile.hh"
class Resource;
class ObjDecoder {
public:
ObjDecoder(uint8_t const* base, size_t length);
ObjDecoder(Resource const& res);
using Vertex = std::array<int32_t, 3>;
using Vertices = std::vector<Vertex>;

View File

@@ -3,8 +3,8 @@
#include "PaletteDecoder.hh"
#include "exceptions.hh"
PaletteDecoder::PaletteDecoder(uint8_t const* base, size_t length)
: iff_(base, length)
PaletteDecoder::PaletteDecoder(Resource const& res)
: iff_(res)
{
auto& root = iff_.getRoot();
if (!root.isForm())
@@ -20,6 +20,21 @@ PaletteDecoder::PaletteDecoder(uint8_t const* base, size_t length)
std::transform(paltIt->begin()+4, paltIt->begin()+772,
palette_.begin(),
[](const char& in) -> uint8_t {return (in << 2) | ((in >> 6)&0x3);});
uint8_t min = 0xff, max = 0;
for (auto it = paltIt->begin()+4;it != paltIt->end();++it) {
if (*it == 0xff)
continue;
min = std::min(min, *it);
max = std::max(max, *it);
}
printf("palt value range %hhu..%hhu\n", min, max);
} else if (paltIt->getType() == "BLWH") {
auto dstIt = palette_.begin();
for (auto it = paltIt->begin()+4;it != paltIt->end();++it) {
*dstIt = *dstIt * ((*it << 2) | ((*it >> 6)&0x3)) / 255u;
++dstIt;
}
}
}
}

View File

@@ -8,7 +8,7 @@
class PaletteDecoder {
public:
PaletteDecoder(uint8_t const* base, size_t length);
PaletteDecoder(Resource const& res);
using Palette = std::array<uint8_t, 768>;

18
Resource.hh Normal file
View File

@@ -0,0 +1,18 @@
#ifndef WC3RE_RESOURCE_HH__
#define WC3RE_RESOURCE_HH__
// Interface for any memory-resident resource
// For example mmap'd data file, TRE object, IFF Object, ...
class Resource {
public:
virtual ~Resource() {}
virtual uint8_t const* data() const = 0;
virtual size_t size() const = 0;
virtual operator bool() const = 0;
};
#endif

View File

@@ -8,6 +8,8 @@
#include <tuple>
#include <experimental/optional>
#include "Resource.hh"
class TreFile {
public:
TreFile(uint8_t const* base, size_t length);
@@ -27,7 +29,7 @@ public:
'path' */
static uint32_t calcCRC(std::string const& path);
class Object {
class Object : public Resource {
public:
Object() : base_(nullptr), length_(0) {
}
@@ -50,19 +52,22 @@ public:
move.length_ = 0;
return *this;
}
~Object() override {
}
uint8_t const* data() const {
uint8_t const* data() const override {
if (data_)
return data_->data();
else
return base_;
}
size_t size() const {
size_t size() const override {
return length_;
}
operator bool() const {
operator bool() const override {
return data_ || base_;
}

View File

@@ -10,13 +10,13 @@
#include "util.hh"
#include "IffFile.hh"
void blobDump(IffFile::Object const& obj, std::string const& filename)
void blobDump(Resource const& obj, std::string const& filename)
{
FILEUPtr file{fopen(filename.c_str(), "wb")};
if (!file)
throw POSIXException{errno, "Could not open file: " + filename};
if (fwrite(obj.begin(), obj.getSize(), 1, file.get()) != 1)
if (fwrite(obj.data(), obj.size(), 1, file.get()) != 1)
throw POSIXException{errno, "Could not write"};
}
@@ -25,7 +25,7 @@ void iffDumper(IffFile::Object const& obj, bool dumpBlobs, std::string dumpPath,
{
for (unsigned i = 0;i < level;++i)
putchar('\t');
printf("%s Length %lu (0x%.lx)", obj.getType().c_str(), obj.getSize(), obj.getSize());
printf("%s Length %lu (0x%.lx)", obj.getType().c_str(), obj.size(), obj.size());
if (obj.isForm()) {
auto& form = dynamic_cast<IffFile::Form const&>(obj);
@@ -88,7 +88,7 @@ int main(int argc, char *argv[])
try {
MmapFile mmap{iffFile};
IffFile iff{mmap.data(), mmap.size()};
IffFile iff{mmap};
if (printStructure) {
unsigned blobCount = 0, subdoc = 0;

View File

@@ -87,9 +87,9 @@ int main(int argc, char *argv[]) {
if (!object) // Wasn't a CRC, try as name
object = tre->openName(objectSpec);
mve = std::make_unique<MveDecoder>(object.data(), object.size());
mve = std::make_unique<MveDecoder>(object);
} else
mve = std::make_unique<MveDecoder>(mmap.data(), mmap.size());
mve = std::make_unique<MveDecoder>(mmap);
if (play) {
Renderer renderer;

View File

@@ -17,21 +17,25 @@
#include "game/GSShowObject.hh"
void usage(char *argv0) {
fprintf(stderr, "Usage: %s [-h] (tre-file name/crc)/iff-file\n", argv0);
fprintf(stderr, "Usage: %s [-h] [-ppalt] (tre-file name/crc)/iff-file\n", argv0);
fprintf(stderr, "\tAttempt to decode the object stored in iff-file, or in the\n\tiff-object \"name\"/\"crc\" contained in tre-file\n");
fprintf(stderr, "\t-p palt\tUse palette \"palt\"");
fprintf(stderr, "\t-h\tPrint this help\n");
}
int main(int argc, char *argv[]) {
std::string inFile, objectSpec;
std::string inFile, objectSpec, paltFile = "tmp/1DC61F50";
bool useTre = false;
{
int opt;
while ((opt = getopt(argc, argv, "h")) != -1) {
while ((opt = getopt(argc, argv, "hp:")) != -1) {
switch (opt) {
case 'h':
usage(argv[0]);
return 0;
case 'p':
paltFile = optarg;
break;
default:
usage(argv[0]);
return 1;
@@ -52,8 +56,8 @@ int main(int argc, char *argv[]) {
}
try {
MmapFile paltMmap{"tmp/1DC61F50"};
PaletteDecoder palt{paltMmap.data(), paltMmap.size()};
MmapFile paltMmap{paltFile};
PaletteDecoder palt{paltMmap};
MmapFile mmap{inFile};
@@ -77,9 +81,9 @@ int main(int argc, char *argv[]) {
if (!object) // Wasn't a CRC, try as name
object = tre->openName(objectSpec);
obj = std::make_unique<ObjDecoder>(object.data(), object.size());
obj = std::make_unique<ObjDecoder>(object);
} else
obj = std::make_unique<ObjDecoder>(mmap.data(), mmap.size());
obj = std::make_unique<ObjDecoder>(mmap);
render::Renderer renderer;
renderer.pushGS(std::make_unique<game::GSShowObject>(renderer, *obj, palt));

View File

@@ -74,7 +74,7 @@ int main(int argc, char *argv[]) {
continue;
auto f = file.openName(name);
try {
IffFile iff{f.data(), f.size()};
IffFile iff{f};
printf("%s:\n", name.c_str());
iff.printStructure(1);
} catch(FormatException &ex) {
@@ -85,7 +85,7 @@ int main(int argc, char *argv[]) {
for(auto crc : file.getCRCs()) {
auto f = file.openCRC(crc);
try {
IffFile iff{f.data(), f.size()};
IffFile iff{f};
printf("%.8x:\n", crc);
iff.printStructure(1);
} catch(FormatException &ex) {

11
util.hh
View File

@@ -5,23 +5,24 @@
#include <string>
#include "common.hh"
#include "Resource.hh"
class MmapFile {
class MmapFile : public Resource {
public:
MmapFile(std::string fileName);
MmapFile(MmapFile const& copy) = delete;
MmapFile(MmapFile && move);
~MmapFile();
~MmapFile() override;
MmapFile& operator=(MmapFile const& copy) = delete;
MmapFile& operator=(MmapFile && move);
std::string const& name() const;
size_t size() const;
uint8_t const* data() const;
size_t size() const override;
uint8_t const* data() const override;
operator bool() const;
operator bool() const override;
private:
std::string name_;
FILEUPtr fd_;