basic PS2 instancing

This commit is contained in:
aap 2015-07-12 22:57:05 +02:00
parent e9f638db05
commit d832570142
10 changed files with 1186 additions and 504 deletions

139
dumprwtree.cpp Normal file
View File

@ -0,0 +1,139 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <new>
#include "rw.h"
using namespace std;
using namespace rw;
const char *chunks[] = { "None", "Struct", "String", "Extension", "Unknown",
"Camera", "Texture", "Material", "Material List", "Atomic Section",
"Plane Section", "World", "Spline", "Matrix", "Frame List",
"Geometry", "Clump", "Unknown", "Light", "Unicode String", "Atomic",
"Texture Native", "Texture Dictionary", "Animation Database",
"Image", "Skin Animation", "Geometry List", "Anim Animation",
"Team", "Crowd", "Delta Morph Animation", "Right To Render",
"MultiTexture Effect Native", "MultiTexture Effect Dictionary",
"Team Dictionary", "Platform Independet Texture Dictionary",
"Table of Contents", "Particle Standard Global Data", "AltPipe",
"Platform Independet Peds", "Patch Mesh", "Chunk Group Start",
"Chunk Group End", "UV Animation Dictionary", "Coll Tree"
};
/* From 0x0101 through 0x0135 */
const char *toolkitchunks0[] = { "Metrics PLG", "Spline PLG", "Stereo PLG",
"VRML PLG", "Morph PLG", "PVS PLG", "Memory Leak PLG", "Animation PLG",
"Gloss PLG", "Logo PLG", "Memory Info PLG", "Random PLG",
"PNG Image PLG", "Bone PLG", "VRML Anim PLG", "Sky Mipmap Val",
"MRM PLG", "LOD Atomic PLG", "ME PLG", "Lightmap PLG",
"Refine PLG", "Skin PLG", "Label PLG", "Particles PLG", "GeomTX PLG",
"Synth Core PLG", "STQPP PLG",
"Part PP PLG", "Collision PLG", "HAnim PLG", "User Data PLG",
"Material Effects PLG", "Particle System PLG", "Delta Morph PLG",
"Patch PLG", "Team PLG", "Crowd PP PLG", "Mip Split PLG",
"Anisotrophy PLG", "Not used", "GCN Material PLG", "Geometric PVS PLG",
"XBOX Material PLG", "Multi Texture PLG", "Chain PLG", "Toon PLG",
"PTank PLG", "Particle Standard PLG", "PDS PLG", "PrtAdv PLG",
"Normal Map PLG", "ADC PLG", "UV Animation PLG"
};
/* From 0x0180 through 0x01c1 */
const char *toolkitchunks1[] = {
"Character Set PLG", "NOHS World PLG", "Import Util PLG",
"Slerp PLG", "Optim PLG", "TL World PLG", "Database PLG",
"Raytrace PLG", "Ray PLG", "Library PLG",
"Not used", "Not used", "Not used", "Not used", "Not used", "Not used",
"2D PLG", "Tile Render PLG", "JPEG Image PLG", "TGA Image PLG",
"GIF Image PLG", "Quat PLG", "Spline PVS PLG", "Mipmap PLG",
"MipmapK PLG", "2D Font", "Intersection PLG", "TIFF Image PLG",
"Pick PLG", "BMP Image PLG", "RAS Image PLG", "Skin FX PLG",
"VCAT PLG", "2D Path", "2D Brush", "2D Object", "2D Shape", "2D Scene",
"2D Pick Region", "2D Object String", "2D Animation PLG",
"2D Animation",
"Not used", "Not used", "Not used", "Not used", "Not used", "Not used",
"2D Keyframe", "2D Maestro", "Barycentric",
"Platform Independent Texture Dictionary TK", "TOC TK", "TPL TK",
"AltPipe TK", "Animation TK", "Skin Split Tookit", "Compressed Key TK",
"Geometry Conditioning PLG", "Wing PLG", "Generic Pipeline TK",
"Lightmap Conversion TK", "Filesystem PLG", "Dictionary TK",
"UV Animation Linear", "UV Animation Parameter"
};
const char *RSchunks[] = { "Unused 1", "Unused 2", "Unused 3",
"Pipeline Set", "Unused 5", "Unused 6", "Specular Material",
"Unused 8", "2dfx", "Night Vertex Colors", "Collision Model",
"Unused 12", "Reflection Material", "Mesh Extension", "Frame",
"Unused 16"
};
const char*
getChunkName(uint32 id)
{
switch(id){
case 0x50E:
return "Bin Mesh PLG";
case 0x510:
return "Native Data PLG";
case 0xF21E:
return "ZModeler Lock";
}
if(id <= 45)
return chunks[id];
else if(id <= 0x0253F2FF && id >= 0x0253F2F0)
return RSchunks[id-0x0253F2F0];
else if(id <= 0x0135 && id >= 0x0101)
return toolkitchunks0[id-0x0101];
else if(id <= 0x01C0 && id >= 0x0181)
return toolkitchunks1[id-0x0181];
else
return "Unknown";
}
void
readchunk(StreamFile *s, ChunkHeaderInfo *h, int level)
{
for(int i = 0; i < level; i++)
printf(" ");
const char *name = getChunkName(h->type);
printf("%s (%x bytes @ 0x%x/0x%x) - [0x%x]\n",
name, h->length, s->tell()-12, s->tell(), h->type);
uint32 end = s->tell() + h->length;
while(s->tell() < end){
ChunkHeaderInfo nh;
readChunkHeaderInfo(s, &nh);
if(nh.version == h->version && nh.build == h->build){
readchunk(s, &nh, level+1);
if(h->type == 0x510)
s->seek(end, 0);
}else{
s->seek(h->length-12);
break;
}
}
}
int
main(int argc, char *argv[])
{
if(argc < 2){
fprintf(stderr, "usage: %s rwStreamFile\n", argv[0]);
return 0;
}
StreamFile s;
s.open(argv[1], "rb");
ChunkHeaderInfo header;
readChunkHeaderInfo(&s, &header);
readchunk(&s, &header, 0);
printf("%x %x %x\n", header.version, header.build,
libraryIDPack(header.version, header.build));
s.close();
return 0;
}

100
insttest.cpp Normal file
View File

@ -0,0 +1,100 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <new>
#include "rw.h"
#include "src/gtaplg.h"
using namespace std;
using namespace rw;
int
main(int argc, char *argv[])
{
rw::version = 0x33002;
gta::registerEnvSpecPlugin();
rw::registerMatFXPlugin();
rw::registerMaterialRightsPlugin();
rw::registerAtomicRightsPlugin();
rw::registerHAnimPlugin();
gta::registerNodeNamePlugin();
gta::registerBreakableModelPlugin();
gta::registerExtraVertColorPlugin();
rw::ps2::registerADCPlugin();
rw::registerSkinPlugin();
rw::registerNativeDataPlugin();
rw::registerMeshPlugin();
rw::platform = rw::PLATFORM_PS2;
rw::Pipeline *defpipe = rw::ps2::makeDefaultPipeline();
rw::Pipeline *skinpipe = rw::ps2::makeSkinPipeline();
// rw::ps2::dumpPipeline(defpipe);
// rw::ps2::dumpPipeline(skinpipe);
int uninstance = 0;
int arg = 1;
if(argc < 2){
printf("usage: %s [-u] ps2.dff\n", argv[0]);
return 0;
}
if(strcmp(argv[arg], "-u") == 0){
uninstance++;
arg++;
if(argc < 3){
printf("usage: %s [-u] ps2.dff\n", argv[0]);
return 0;
}
}
Clump *c;
uint32 len;
uint8 *data = getFileContents(argv[arg], &len);
assert(data != NULL);
StreamMemory in;
in.open(data, len);
findChunk(&in, ID_CLUMP, NULL, NULL);
debugFile = argv[arg];
c = Clump::streamRead(&in);
assert(c != NULL);
for(int32 i = 0; i < c->numAtomics; i++){
Atomic *a = c->atomicList[i];
if(a->pipeline){
printf("has pipeline %x %x %x\n",
a->pipeline->pluginID,
a->pipeline->pluginData,
a->pipeline->platform);
if(uninstance)
a->pipeline->uninstance(a);
else
a->pipeline->instance(a);
}else{
printf("default pipeline\n");
if(uninstance)
defpipe->uninstance(a);
else
defpipe->instance(a);
}
}
data = new rw::uint8[256*1024];
rw::StreamMemory out;
out.open(data, 0, 256*1024);
c->streamWrite(&out);
FILE *cf = fopen("out.dff", "wb");
assert(cf != NULL);
fwrite(data, out.getLength(), 1, cf);
fclose(cf);
out.close();
delete[] data;
delete c;
return 0;
}

View File

@ -1,492 +1,493 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <new>
#include "rwbase.h"
#include "rwplugin.h"
#include "rwpipeline.h"
#include "rwobjects.h"
using namespace std;
namespace rw {
//
// TexDictionary
//
TexDictionary *currentTexDictionary;
TexDictionary::TexDictionary(void)
{
this->first = NULL;
}
void
TexDictionary::add(Texture *tex)
{
tex->next = this->first;
this->first = tex;
}
Texture*
TexDictionary::find(const char *name)
{
for(Texture *tex = this->first; tex; tex = tex->next)
if(strncmp(tex->name, name, 32) == 0)
return tex;
return NULL;
}
//
// Texture
//
Texture::Texture(void)
{
memset(this->name, 0, 32);
memset(this->mask, 0, 32);
this->filterAddressing = (WRAP << 12) | (WRAP << 8) | NEAREST;
this->raster = NULL;
this->refCount = 1;
this->next = NULL;
this->constructPlugins();
}
Texture::~Texture(void)
{
this->destructPlugins();
}
void
Texture::decRef(void)
{
this->refCount--;
if(this->refCount)
delete this;
}
// TODO: do this properly, pretty ugly right now
Texture*
Texture::read(const char *name, const char *mask)
{
(void)mask;
Raster *raster = NULL;
Texture *tex;
if(currentTexDictionary && (tex = currentTexDictionary->find(name)))
return tex;
tex = new Texture;
strncpy(tex->name, name, 32);
strncpy(tex->mask, mask, 32);
char *n = (char*)malloc(strlen(name) + 5);
strcpy(n, name);
strcat(n, ".tga");
Image *img = readTGA(n);
free(n);
if(img){
raster = Raster::createFromImage(img);
delete img;
}
tex->raster = raster;
if(currentTexDictionary)
currentTexDictionary->add(tex);
return tex;
}
Texture*
Texture::streamRead(Stream *stream)
{
uint32 length;
char name[32], mask[32];
assert(findChunk(stream, ID_STRUCT, NULL, NULL));
uint32 filterAddressing = stream->readU16();
// TODO: what is this? (mipmap? i think)
stream->seek(2);
assert(findChunk(stream, ID_STRING, &length, NULL));
stream->read(name, length);
assert(findChunk(stream, ID_STRING, &length, NULL));
stream->read(mask, length);
Texture *tex = Texture::read(name, mask);
tex->refCount++;
tex->filterAddressing = filterAddressing;
tex->streamReadPlugins(stream);
return tex;
}
bool
Texture::streamWrite(Stream *stream)
{
int size;
writeChunkHeader(stream, ID_TEXTURE, this->streamGetSize());
writeChunkHeader(stream, ID_STRUCT, 4);
stream->writeU32(this->filterAddressing);
// TODO: length can't be > 32
size = strlen(this->name)+4 & ~3;
writeChunkHeader(stream, ID_STRING, size);
stream->write(this->name, size);
size = strlen(this->mask)+4 & ~3;
writeChunkHeader(stream, ID_STRING, size);
stream->write(this->mask, size);
this->streamWritePlugins(stream);
return true;
}
uint32
Texture::streamGetSize(void)
{
uint32 size = 0;
size += 12 + 4;
size += 12 + 12;
size += strlen(this->name)+4 & ~3;
size += strlen(this->mask)+4 & ~3;
size += 12 + this->streamGetPluginSize();
return size;
}
//
// Image
//
Image::Image(int32 width, int32 height, int32 depth)
{
this->flags = 0;
this->width = width;
this->height = height;
this->depth = depth;
this->stride = 0;
this->pixels = NULL;
this->palette = NULL;
}
Image::~Image(void)
{
this->free();
}
void
Image::allocate(void)
{
if(this->pixels == NULL){
this->stride = this->width*(this->depth==4 ? 1 : this->depth/8);
this->pixels = new uint8[this->stride*this->height];
this->flags |= 1;
}
if(this->palette == NULL){
if(this->depth == 4 || this->depth == 8)
this->palette = new uint8[(this->depth==4? 16 : 256)*4];
this->flags |= 2;
}
}
void
Image::free(void)
{
if(this->flags&1)
delete[] this->pixels;
if(this->flags&2)
delete[] this->palette;
}
void
Image::setPixels(uint8 *pixels)
{
this->pixels = pixels;
this->flags |= 1;
}
void
Image::setPalette(uint8 *palette)
{
this->palette = palette;
this->flags |= 2;
}
static char *searchPaths = NULL;
int numSearchPaths = 0;
void
Image::setSearchPath(const char *path)
{
char *p, *end;
::free(searchPaths);
numSearchPaths = 0;
if(path)
searchPaths = p = strdup(path);
else{
searchPaths = NULL;
return;
}
while(p && *p){
end = strchr(p, ';');
if(end)
*end++ = '\0';
numSearchPaths++;
p = end;
}
}
void
Image::printSearchPath(void)
{
char *p = searchPaths;
for(int i = 0; i < numSearchPaths; i++){
printf("%s\n", p);
p += strlen(p) + 1;
}
}
char*
Image::getFilename(const char *name)
{
FILE *f;
char *s, *p = searchPaths;
int len = strlen(name)+1;
if(numSearchPaths == 0){
f = fopen(name, "r");
if(f){
fclose(f);
printf("found %s\n", name);
return strdup(name);
}
return NULL;
}else
for(int i = 0; i < numSearchPaths; i++){
s = (char*)malloc(strlen(p)+len);
strcpy(s, p);
strcat(s, name);
f = fopen(s, "r");
if(f){
fclose(f);
printf("found %s\n", name);
return s;
}
::free(s);
p += strlen(p) + 1;
}
return NULL;
}
//
// TGA I/O
//
#ifndef RW_PS2
#pragma pack(push)
#pragma pack(1)
#define PACKED_STRUCT
#else
#define PACKED_STRUCT __attribute__((__packed__))
#endif
struct PACKED_STRUCT TGAHeader
{
int8 IDlen;
int8 colorMapType;
int8 imageType;
int16 colorMapOrigin;
int16 colorMapLength;
int8 colorMapDepth;
int16 xOrigin, yOrigin;
int16 width, height;
uint8 depth;
uint8 descriptor;
};
#ifndef RW_PS2
#pragma pack(push)
#endif
Image*
readTGA(const char *afilename)
{
TGAHeader header;
Image *image;
char *filename;
int depth = 0, palDepth = 0;
filename = Image::getFilename(afilename);
if(filename == NULL)
return NULL;
uint32 length;
uint8 *data = getFileContents(filename, &length);
assert(data != NULL);
free(filename);
StreamMemory file;
file.open(data, length);
file.read(&header, sizeof(header));
assert(header.imageType == 1 || header.imageType == 2);
file.seek(header.IDlen);
if(header.colorMapType){
assert(header.colorMapOrigin == 0);
depth = (header.colorMapLength <= 16) ? 4 : 8;
palDepth = header.colorMapDepth;
assert(palDepth == 24 || palDepth == 32);
}else{
depth = header.depth;
assert(depth == 24 || depth == 32);
}
image = new Image(header.width, header.height, depth);
image->allocate();
uint8 *palette = header.colorMapType ? image->palette : NULL;
uint8 (*color)[4] = NULL;
if(palette){
int maxlen = depth == 4 ? 16 : 256;
color = (uint8(*)[4])palette;
int i;
for(i = 0; i < header.colorMapLength; i++){
color[i][2] = file.readU8();
color[i][1] = file.readU8();
color[i][0] = file.readU8();
color[i][3] = 0xFF;
if(palDepth == 32)
color[i][3] = file.readU8();
}
for(; i < maxlen; i++){
color[i][0] = color[i][1] = color[i][2] = 0;
color[i][3] = 0xFF;
}
}
uint8 *pixels = image->pixels;
if(!(header.descriptor & 0x20))
pixels += (image->height-1)*image->stride;
for(int y = 0; y < image->height; y++){
uint8 *line = pixels;
for(int x = 0; x < image->width; x++){
if(palette)
*line++ = file.readU8();
else{
line[2] = file.readU8();
line[1] = file.readU8();
line[0] = file.readU8();
line += 3;
if(depth == 32)
*line++ = file.readU8();
}
}
pixels += (header.descriptor&0x20) ?
image->stride : -image->stride;
}
file.close();
delete[] data;
return image;
}
void
writeTGA(Image *image, const char *filename)
{
TGAHeader header;
StreamFile file;
assert(file.open(filename, "wb"));
header.IDlen = 0;
header.imageType = image->palette != NULL ? 1 : 2;
header.colorMapType = image->palette != NULL;
header.colorMapOrigin = 0;
header.colorMapLength = image->depth == 4 ? 16 :
image->depth == 8 ? 256 : 0;
header.colorMapDepth = image->palette ? 32 : 0;
header.xOrigin = 0;
header.yOrigin = 0;
header.width = image->width;
header.height = image->height;
header.depth = image->depth == 4 ? 8 : image->depth;
header.descriptor = 0x20 | (image->depth == 32 ? 8 : 0);
file.write(&header, sizeof(header));
uint8 *pixels = image->pixels;
uint8 *palette = header.colorMapType ? image->palette : NULL;
uint8 (*color)[4] = (uint8(*)[4])palette;;
if(palette)
for(int i = 0; i < header.colorMapLength; i++){
file.writeU8(color[i][2]);
file.writeU8(color[i][1]);
file.writeU8(color[i][0]);
file.writeU8(color[i][3]);
}
for(int y = 0; y < image->height; y++){
uint8 *line = pixels;
for(int x = 0; x < image->width; x++){
if(palette)
file.writeU8(*line++);
else{
file.writeU8(line[2]);
file.writeU8(line[1]);
file.writeU8(line[0]);
line += 3;
if(image->depth == 32)
file.writeU8(*line++);
}
}
pixels += image->stride;
}
file.close();
}
//
// Raster
//
Raster::Raster(void)
{
this->type = 0;
this->width = this->height = this->depth = this->stride = 0;
this->format = 0;
this->texels = this->palette = NULL;
this->constructPlugins();
}
Raster::~Raster(void)
{
this->destructPlugins();
delete[] this->texels;
delete[] this->palette;
}
Raster*
Raster::createFromImage(Image *image)
{
Raster *raster = new Raster;
raster->type = 4;
raster->width = image->width;
raster->stride = image->stride;
raster->height = image->height;
raster->depth = image->depth;
raster->texels = raster->palette = NULL;
if(raster->depth == 32)
raster->format = Raster::C8888;
else if(raster->depth == 24)
raster->format = Raster::C888;
else if(raster->depth == 16)
raster->format = Raster::C1555;
else if(raster->depth == 8)
raster->format = Raster::PAL8 | Raster::C8888;
else if(raster->depth == 4)
raster->format = Raster::PAL4 | Raster::C8888;
else{
delete raster;
return NULL;
}
raster->texels = new uint8[raster->stride*raster->height];
memcpy(raster->texels, image->pixels, raster->stride*raster->height);
if(image->palette){
int size = raster->depth == 4 ? 16 : 256;
raster->palette = new uint8[size*4];
memcpy(raster->palette, image->palette, size*4);
}
return raster;
}
}
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <new>
#include "rwbase.h"
#include "rwplugin.h"
#include "rwpipeline.h"
#include "rwobjects.h"
using namespace std;
namespace rw {
//
// TexDictionary
//
TexDictionary *currentTexDictionary;
TexDictionary::TexDictionary(void)
{
this->first = NULL;
}
void
TexDictionary::add(Texture *tex)
{
tex->next = this->first;
this->first = tex;
}
Texture*
TexDictionary::find(const char *name)
{
for(Texture *tex = this->first; tex; tex = tex->next)
if(strncmp(tex->name, name, 32) == 0)
return tex;
return NULL;
}
//
// Texture
//
Texture::Texture(void)
{
memset(this->name, 0, 32);
memset(this->mask, 0, 32);
this->filterAddressing = (WRAP << 12) | (WRAP << 8) | NEAREST;
this->raster = NULL;
this->refCount = 1;
this->next = NULL;
this->constructPlugins();
}
Texture::~Texture(void)
{
this->destructPlugins();
}
void
Texture::decRef(void)
{
this->refCount--;
if(this->refCount)
delete this;
}
// TODO: do this properly, pretty ugly right now
Texture*
Texture::read(const char *name, const char *mask)
{
(void)mask;
Raster *raster = NULL;
Texture *tex;
if(currentTexDictionary && (tex = currentTexDictionary->find(name)))
return tex;
tex = new Texture;
strncpy(tex->name, name, 32);
strncpy(tex->mask, mask, 32);
char *n = (char*)malloc(strlen(name) + 5);
strcpy(n, name);
strcat(n, ".tga");
Image *img = readTGA(n);
free(n);
if(img){
raster = Raster::createFromImage(img);
delete img;
}
tex->raster = raster;
if(currentTexDictionary)
currentTexDictionary->add(tex);
return tex;
}
Texture*
Texture::streamRead(Stream *stream)
{
uint32 length;
char name[32], mask[32];
assert(findChunk(stream, ID_STRUCT, NULL, NULL));
uint32 filterAddressing = stream->readU16();
// TODO: what is this? (mipmap? i think)
stream->seek(2);
assert(findChunk(stream, ID_STRING, &length, NULL));
stream->read(name, length);
assert(findChunk(stream, ID_STRING, &length, NULL));
stream->read(mask, length);
Texture *tex = Texture::read(name, mask);
tex->refCount++;
tex->filterAddressing = filterAddressing;
tex->streamReadPlugins(stream);
return tex;
}
bool
Texture::streamWrite(Stream *stream)
{
int size;
writeChunkHeader(stream, ID_TEXTURE, this->streamGetSize());
writeChunkHeader(stream, ID_STRUCT, 4);
stream->writeU32(this->filterAddressing);
// TODO: length can't be > 32
size = strlen(this->name)+4 & ~3;
writeChunkHeader(stream, ID_STRING, size);
stream->write(this->name, size);
size = strlen(this->mask)+4 & ~3;
writeChunkHeader(stream, ID_STRING, size);
stream->write(this->mask, size);
this->streamWritePlugins(stream);
return true;
}
uint32
Texture::streamGetSize(void)
{
uint32 size = 0;
size += 12 + 4;
size += 12 + 12;
size += strlen(this->name)+4 & ~3;
size += strlen(this->mask)+4 & ~3;
size += 12 + this->streamGetPluginSize();
return size;
}
//
// Image
//
Image::Image(int32 width, int32 height, int32 depth)
{
this->flags = 0;
this->width = width;
this->height = height;
this->depth = depth;
this->stride = 0;
this->pixels = NULL;
this->palette = NULL;
}
Image::~Image(void)
{
this->free();
}
void
Image::allocate(void)
{
if(this->pixels == NULL){
this->stride = this->width*(this->depth==4 ? 1 : this->depth/8);
this->pixels = new uint8[this->stride*this->height];
this->flags |= 1;
}
if(this->palette == NULL){
if(this->depth == 4 || this->depth == 8)
this->palette = new uint8[(this->depth==4? 16 : 256)*4];
this->flags |= 2;
}
}
void
Image::free(void)
{
if(this->flags&1)
delete[] this->pixels;
if(this->flags&2)
delete[] this->palette;
}
void
Image::setPixels(uint8 *pixels)
{
this->pixels = pixels;
this->flags |= 1;
}
void
Image::setPalette(uint8 *palette)
{
this->palette = palette;
this->flags |= 2;
}
static char *searchPaths = NULL;
int numSearchPaths = 0;
void
Image::setSearchPath(const char *path)
{
char *p, *end;
::free(searchPaths);
numSearchPaths = 0;
if(path)
searchPaths = p = strdup(path);
else{
searchPaths = NULL;
return;
}
while(p && *p){
end = strchr(p, ';');
if(end)
*end++ = '\0';
numSearchPaths++;
p = end;
}
}
void
Image::printSearchPath(void)
{
char *p = searchPaths;
for(int i = 0; i < numSearchPaths; i++){
printf("%s\n", p);
p += strlen(p) + 1;
}
}
char*
Image::getFilename(const char *name)
{
FILE *f;
char *s, *p = searchPaths;
int len = strlen(name)+1;
if(numSearchPaths == 0){
f = fopen(name, "r");
if(f){
fclose(f);
printf("found %s\n", name);
return strdup(name);
}
return NULL;
}else
for(int i = 0; i < numSearchPaths; i++){
s = (char*)malloc(strlen(p)+len);
strcpy(s, p);
strcat(s, name);
f = fopen(s, "r");
if(f){
fclose(f);
printf("found %s\n", name);
return s;
}
::free(s);
p += strlen(p) + 1;
}
return NULL;
}
//
// TGA I/O
//
// TODO: fuck pakced structs
#ifndef RW_PS2
#pragma pack(push)
#pragma pack(1)
#define PACKED_STRUCT
#else
#define PACKED_STRUCT __attribute__((__packed__))
#endif
struct PACKED_STRUCT TGAHeader
{
int8 IDlen;
int8 colorMapType;
int8 imageType;
int16 colorMapOrigin;
int16 colorMapLength;
int8 colorMapDepth;
int16 xOrigin, yOrigin;
int16 width, height;
uint8 depth;
uint8 descriptor;
};
#ifndef RW_PS2
#pragma pack(push)
#endif
Image*
readTGA(const char *afilename)
{
TGAHeader header;
Image *image;
char *filename;
int depth = 0, palDepth = 0;
filename = Image::getFilename(afilename);
if(filename == NULL)
return NULL;
uint32 length;
uint8 *data = getFileContents(filename, &length);
assert(data != NULL);
free(filename);
StreamMemory file;
file.open(data, length);
file.read(&header, sizeof(header));
assert(header.imageType == 1 || header.imageType == 2);
file.seek(header.IDlen);
if(header.colorMapType){
assert(header.colorMapOrigin == 0);
depth = (header.colorMapLength <= 16) ? 4 : 8;
palDepth = header.colorMapDepth;
assert(palDepth == 24 || palDepth == 32);
}else{
depth = header.depth;
assert(depth == 24 || depth == 32);
}
image = new Image(header.width, header.height, depth);
image->allocate();
uint8 *palette = header.colorMapType ? image->palette : NULL;
uint8 (*color)[4] = NULL;
if(palette){
int maxlen = depth == 4 ? 16 : 256;
color = (uint8(*)[4])palette;
int i;
for(i = 0; i < header.colorMapLength; i++){
color[i][2] = file.readU8();
color[i][1] = file.readU8();
color[i][0] = file.readU8();
color[i][3] = 0xFF;
if(palDepth == 32)
color[i][3] = file.readU8();
}
for(; i < maxlen; i++){
color[i][0] = color[i][1] = color[i][2] = 0;
color[i][3] = 0xFF;
}
}
uint8 *pixels = image->pixels;
if(!(header.descriptor & 0x20))
pixels += (image->height-1)*image->stride;
for(int y = 0; y < image->height; y++){
uint8 *line = pixels;
for(int x = 0; x < image->width; x++){
if(palette)
*line++ = file.readU8();
else{
line[2] = file.readU8();
line[1] = file.readU8();
line[0] = file.readU8();
line += 3;
if(depth == 32)
*line++ = file.readU8();
}
}
pixels += (header.descriptor&0x20) ?
image->stride : -image->stride;
}
file.close();
delete[] data;
return image;
}
void
writeTGA(Image *image, const char *filename)
{
TGAHeader header;
StreamFile file;
assert(file.open(filename, "wb"));
header.IDlen = 0;
header.imageType = image->palette != NULL ? 1 : 2;
header.colorMapType = image->palette != NULL;
header.colorMapOrigin = 0;
header.colorMapLength = image->depth == 4 ? 16 :
image->depth == 8 ? 256 : 0;
header.colorMapDepth = image->palette ? 32 : 0;
header.xOrigin = 0;
header.yOrigin = 0;
header.width = image->width;
header.height = image->height;
header.depth = image->depth == 4 ? 8 : image->depth;
header.descriptor = 0x20 | (image->depth == 32 ? 8 : 0);
file.write(&header, sizeof(header));
uint8 *pixels = image->pixels;
uint8 *palette = header.colorMapType ? image->palette : NULL;
uint8 (*color)[4] = (uint8(*)[4])palette;;
if(palette)
for(int i = 0; i < header.colorMapLength; i++){
file.writeU8(color[i][2]);
file.writeU8(color[i][1]);
file.writeU8(color[i][0]);
file.writeU8(color[i][3]);
}
for(int y = 0; y < image->height; y++){
uint8 *line = pixels;
for(int x = 0; x < image->width; x++){
if(palette)
file.writeU8(*line++);
else{
file.writeU8(line[2]);
file.writeU8(line[1]);
file.writeU8(line[0]);
line += 3;
if(image->depth == 32)
file.writeU8(*line++);
}
}
pixels += image->stride;
}
file.close();
}
//
// Raster
//
Raster::Raster(void)
{
this->type = 0;
this->width = this->height = this->depth = this->stride = 0;
this->format = 0;
this->texels = this->palette = NULL;
this->constructPlugins();
}
Raster::~Raster(void)
{
this->destructPlugins();
delete[] this->texels;
delete[] this->palette;
}
Raster*
Raster::createFromImage(Image *image)
{
Raster *raster = new Raster;
raster->type = 4;
raster->width = image->width;
raster->stride = image->stride;
raster->height = image->height;
raster->depth = image->depth;
raster->texels = raster->palette = NULL;
if(raster->depth == 32)
raster->format = Raster::C8888;
else if(raster->depth == 24)
raster->format = Raster::C888;
else if(raster->depth == 16)
raster->format = Raster::C1555;
else if(raster->depth == 8)
raster->format = Raster::PAL8 | Raster::C8888;
else if(raster->depth == 4)
raster->format = Raster::PAL4 | Raster::C8888;
else{
delete raster;
return NULL;
}
raster->texels = new uint8[raster->stride*raster->height];
memcpy(raster->texels, image->pixels, raster->stride*raster->height);
if(image->palette){
int size = raster->depth == 4 ? 16 : 256;
raster->palette = new uint8[size*4];
memcpy(raster->palette, image->palette, size*4);
}
return raster;
}
}

54
src/pipeline.cpp Normal file
View File

@ -0,0 +1,54 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <new>
#include "rwbase.h"
#include "rwplugin.h"
#include "rwpipeline.h"
#include "rwobjects.h"
#include "rwps2.h"
using namespace std;
namespace rw {
Pipeline::Pipeline(uint32 platform)
{
this->pluginID = 0;
this->pluginData = 0;
this->platform = platform;
for(int i = 0; i < 10; i++)
this->attribs[i] = NULL;
}
Pipeline::Pipeline(Pipeline *)
{
assert(0 && "Can't copy pipeline");
}
Pipeline::~Pipeline(void)
{
}
void
Pipeline::instance(Atomic *atomic)
{
fprintf(stderr, "This pipeline can't instance\n");
}
void
Pipeline::uninstance(Atomic *atomic)
{
fprintf(stderr, "This pipeline can't uninstance\n");
}
void
Pipeline::render(Atomic *atomic)
{
fprintf(stderr, "This pipeline can't render\n");
}
}

View File

@ -554,7 +554,8 @@ registerSkinPlugin(void)
defpipe->pluginData = 1;
for(uint i = 0; i < nelem(matFXGlobals.pipelines); i++)
skinGlobals.pipelines[i] = defpipe;
skinGlobals.pipelines[platformIdx[PLATFORM_PS2]] =
ps2::makeSkinPipeline();
skinGlobals.offset = Geometry::registerPlugin(sizeof(Skin*), ID_SKIN,
createSkin,
@ -883,6 +884,8 @@ registerMatFXPlugin(void)
defpipe->pluginData = 0;
for(uint i = 0; i < nelem(matFXGlobals.pipelines); i++)
matFXGlobals.pipelines[i] = defpipe;
matFXGlobals.pipelines[platformIdx[PLATFORM_PS2]] =
ps2::makeMatFXPipeline();
matFXGlobals.atomicOffset =
Atomic::registerPlugin(sizeof(int32), ID_MATFX,

View File

@ -126,7 +126,7 @@ fixDmaOffsets(InstanceData *inst)
// DMAref
case 0x30000000:
// fix address and jump to next
tag[1] = base + tag[1]*0x10;
tag[1] = base + tag[1]<<4;
tag += 4;
break;
@ -165,7 +165,7 @@ unfixDmaOffsets(InstanceData *inst)
// DMAref
case 0x30000000:
// unfix address and jump to next
tag[1] = (tag[1] - base)/0x10;
tag[1] = (tag[1] - base)>>4;
tag += 4;
break;
@ -203,9 +203,8 @@ enum PS2Attribs {
enum PS2AttibTypes {
AT_XYZ = 0,
AT_UV = 1,
AT_UV2 = 2,
AT_RGBA = 3,
AT_NORMAL = 4
AT_RGBA = 2,
AT_NORMAL = 3
};
PipeAttribute attribXYZ = {
@ -241,6 +240,301 @@ PipeAttribute attribWeights = {
Pipeline::Pipeline(uint32 platform)
: rw::Pipeline(platform) { }
static uint32
attribSize(uint32 unpack)
{
static uint32 size[] = { 32, 16, 8, 16 };
return ((unpack>>26 & 3)+1)*size[unpack>>24 & 3]/8;
}
#define QWC(x) (((x)+0xF)>>4)
static uint32
getBatchSize(Pipeline *pipe, uint32 vertCount)
{
PipeAttribute *a;
uint32 size = 1;
for(uint i = 0; i < nelem(pipe->attribs); i++)
if((a = pipe->attribs[i]) && (a->attrib & AT_RW) == 0){
size++;
size += QWC(vertCount*attribSize(a->attrib));
}
return size;
}
uint32*
instanceXYZ(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n)
{
uint16 j;
uint32 *d = (uint32*)g->morphTargets[0].vertices;
for(uint32 i = idx; i < idx+n; i++){
j = m->indices[i];
*p++ = d[j*3+0];
*p++ = d[j*3+1];
*p++ = d[j*3+2];
}
while((uintptr)p % 0x10)
*p++ = 0;
return p;
}
uint32*
instanceUV(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n)
{
uint16 j;
uint32 *d = (uint32*)g->texCoords[0];
if((g->geoflags & Geometry::TEXTURED) ||
(g->geoflags & Geometry::TEXTURED2))
for(uint32 i = idx; i < idx+n; i++){
j = m->indices[i];
*p++ = d[j*2+0];
*p++ = d[j*2+1];
}
else
for(uint32 i = idx; i < idx+n; i++){
*p++ = 0;
*p++ = 0;
}
while((uintptr)p % 0x10)
*p++ = 0;
return p;
}
uint32*
instanceRGBA(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n)
{
uint16 j;
uint32 *d = (uint32*)g->colors;
if((g->geoflags & Geometry::PRELIT))
for(uint32 i = idx; i < idx+n; i++){
j = m->indices[i];
*p++ = d[j];
}
else
for(uint32 i = idx; i < idx+n; i++)
*p++ = 0xFF000000;
while((uintptr)p % 0x10)
*p++ = 0;
return p;
}
uint32*
instanceNormal(uint32 *wp, Geometry *g, Mesh *m, uint32 idx, uint32 n)
{
uint16 j;
float *d = g->morphTargets[0].normals;
uint8 *p = (uint8*)wp;
if((g->geoflags & Geometry::NORMALS))
for(uint32 i = idx; i < idx+n; i++){
j = m->indices[i];
*p++ = d[j*3+0]*127.0f;
*p++ = d[j*3+1]*127.0f;
*p++ = d[j*3+2]*127.0f;
}
else
for(uint32 i = idx; i < idx+n; i++){
*p++ = 0;
*p++ = 0;
*p++ = 0;
}
while((uintptr)p % 0x10)
*p++ = 0;
return (uint32*)p;
}
uint32 markcnt = 0xf790;
static void
instanceMat(Pipeline *pipe, Geometry *g, InstanceData *inst, Mesh *m)
{
PipeAttribute *a;
uint32 numAttribs = 0;
uint32 numBrokenAttribs = 0;
for(uint i = 0; i < nelem(pipe->attribs); i++)
if(a = pipe->attribs[i])
if(a->attrib & AT_RW)
numBrokenAttribs++;
else
numAttribs++;
uint32 numBatches = 0;
uint32 totalVerts = 0;
uint32 batchVertCount, lastBatchVertCount;
if(g->meshHeader->flags == 1){ // tristrip
for(uint i = 0; i < m->numIndices; i += pipe->triStripCount-2){
numBatches++;
totalVerts += m->numIndices-i < pipe->triStripCount ?
m->numIndices-i : pipe->triStripCount;
}
batchVertCount = pipe->triStripCount;
lastBatchVertCount = totalVerts%pipe->triStripCount;
}else{ // trilist
numBatches = (m->numIndices+pipe->triListCount-1) /
pipe->triListCount;
totalVerts = m->numIndices;
batchVertCount = pipe->triListCount;
lastBatchVertCount = totalVerts%pipe->triListCount;
}
uint32 batchSize = getBatchSize(pipe, batchVertCount);
uint32 lastBatchSize = getBatchSize(pipe, lastBatchVertCount);
uint32 size = 0;
if(numBrokenAttribs == 0)
size = 1 + batchSize*(numBatches-1) + lastBatchSize;
else
size = 2*numBatches +
(1+batchSize)*(numBatches-1) + 1+lastBatchSize;
/* figure out size and addresses of broken out sections */
uint32 attribPos[nelem(pipe->attribs)];
uint32 size2 = 0;
for(uint i = 0; i < nelem(pipe->attribs); i++)
if((a = pipe->attribs[i]) && a->attrib & AT_RW){
attribPos[i] = size2 + size;
size2 += QWC(m->numIndices*attribSize(a->attrib));
}
/*
printf("attribs: %d %d\n", numAttribs, numBrokenAttribs);
printf("numIndices: %d\n", m->numIndices);
printf("%d %d, %x %x\n", numBatches, totalVerts,
batchVertCount, lastBatchVertCount);
printf("%x %x\n", batchSize, lastBatchSize);
printf("size: %x, %x\n", size, size2);
*/
inst->dataSize = (size+size2)<<4;
inst->arePointersFixed = numBrokenAttribs == 0;
// TODO: force alignment
inst->data = new uint8[inst->dataSize];
uint32 idx = 0;
uint32 *p = (uint32*)inst->data;
if(numBrokenAttribs == 0){
*p++ = 0x60000000 | size-1;
*p++ = 0;
*p++ = 0x11000000; // FLUSH
*p++ = 0x06000000; // MSKPATH3; SA: FLUSH
}
for(uint32 j = 0; j < numBatches; j++){
uint32 nverts, bsize;
if(j < numBatches-1){
bsize = batchSize;
nverts = batchVertCount;
}else{
bsize = lastBatchSize;
nverts = lastBatchVertCount;
}
for(uint i = 0; i < nelem(pipe->attribs); i++)
if((a = pipe->attribs[i]) && a->attrib & AT_RW){
uint32 atsz = attribSize(a->attrib);
*p++ = 0x30000000 | QWC(nverts*atsz);
*p++ = attribPos[i];
*p++ = 0x01000100 |
pipe->inputStride; // STCYCL
*p++ = (a->attrib&0xFF004000)
| 0x8000 | nverts << 16 | i; // UNPACK
*p++ = 0x10000000;
*p++ = 0x0;
*p++ = 0x0;
*p++ = 0x0;
attribPos[i] += g->meshHeader->flags == 1 ?
QWC((batchVertCount-2)*atsz) :
QWC(batchVertCount*atsz);
}
if(numBrokenAttribs){
*p++ = (j < numBatches-1 ? 0x10000000 : 0x60000000) |
bsize;
*p++ = 0x0;
*p++ = 0x0;
*p++ = 0x0;
}
for(uint i = 0; i < nelem(pipe->attribs); i++)
if((a = pipe->attribs[i]) && (a->attrib & AT_RW) == 0){
*p++ = 0x07000000 | markcnt++; // MARK (SA: NOP)
*p++ = 0x05000000; // STMOD
*p++ = 0x01000100 |
pipe->inputStride; // STCYCL
*p++ = (a->attrib&0xFF004000)
| 0x8000 | nverts << 16 | i; // UNPACK
// TODO: instance
switch(i){
case 0:
p = instanceXYZ(p, g, m, idx, nverts);
break;
case 1:
p = instanceUV(p, g, m, idx, nverts);
break;
case 2:
p = instanceRGBA(p, g, m, idx, nverts);
break;
case 3:
p = instanceNormal(p,g, m, idx, nverts);
break;
}
}
idx += g->meshHeader->flags == 1
? batchVertCount-2 : batchVertCount;
*p++ = 0x04000000 | nverts; // ITOP
*p++ = j == 0 ? 0x15000000 : 0x17000000;
if(j < numBatches-1){
*p++ = 0x0;
*p++ = 0x0;
}else{
*p++ = 0x11000000; // FLUSH
*p++ = 0x06000000; // MSKPATH3; SA: FLUSH
}
}
/*
FILE *f = fopen("out.bin", "w");
fwrite(inst->data, inst->dataSize, 1, f);
fclose(f);
*/
}
#undef QWC
void
Pipeline::instance(Atomic *atomic)
{
Geometry *geometry = atomic->geometry;
InstanceDataHeader *header = new InstanceDataHeader;
geometry->instData = header;
header->platform = PLATFORM_PS2;
assert(geometry->meshHeader != NULL);
header->numMeshes = geometry->meshHeader->numMeshes;
header->instanceMeshes = new InstanceData[header->numMeshes];
for(uint32 i = 0; i < header->numMeshes; i++){
Mesh *mesh = &geometry->meshHeader->mesh[i];
InstanceData *instance = &header->instanceMeshes[i];
// TODO: should depend on material pipeline
instanceMat(this, geometry, instance, mesh);
//printf("\n");
}
geometry->geoflags |= Geometry::NATIVE;
}
// Only a dummy right now
void
Pipeline::uninstance(Atomic *atomic)
{
Geometry *geometry = atomic->geometry;
assert(geometry->instData->platform == PLATFORM_PS2);
assert(geometry->instData != NULL);
InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
for(uint32 i = 0; i < header->numMeshes; i++){
Mesh *mesh = &geometry->meshHeader->mesh[i];
InstanceData *instance = &header->instanceMeshes[i];
printf("numIndices: %d\n", mesh->numIndices);
printDMA(instance);
}
}
void
Pipeline::setTriBufferSizes(uint32 inputStride,
uint32 stripCount, uint32 listCount)
@ -277,6 +571,8 @@ Pipeline*
makeSkinPipeline(void)
{
Pipeline *pipe = new Pipeline(PLATFORM_PS2);
pipe->pluginID = ID_SKIN;
pipe->pluginData = 1;
pipe->attribs[AT_XYZ] = &attribXYZ;
pipe->attribs[AT_UV] = &attribUV;
pipe->attribs[AT_RGBA] = &attribRGBA;
@ -288,6 +584,24 @@ makeSkinPipeline(void)
return pipe;
}
Pipeline*
makeMatFXPipeline(void)
{
Pipeline *pipe = new Pipeline(PLATFORM_PS2);
pipe->pluginID = ID_MATFX;
pipe->pluginData = 0;
pipe->attribs[AT_XYZ] = &attribXYZ;
pipe->attribs[AT_UV] = &attribUV;
pipe->attribs[AT_RGBA] = &attribRGBA;
pipe->attribs[AT_NORMAL] = &attribNormal;
// TODO: not correct
uint32 vertCount = Pipeline::getVertCount(VU_Lights, 4, 3, 2);
pipe->setTriBufferSizes(4, vertCount, vertCount/3);
pipe->triStripCount = 0x38;
pipe->vifOffset = pipe->inputStride*vertCount;
return pipe;
}
void
dumpPipeline(rw::Pipeline *rwpipe)
{
@ -454,6 +768,32 @@ registerADCPlugin(void)
// misc stuff
void
printDMA(InstanceData *inst)
{
uint32 *tag = (uint32*)inst->data;
for(;;){
switch(tag[0]&0x70000000){
// DMAcnt
case 0x10000000:
printf("%08x %08x\n", tag[0], tag[1]);
tag += (1+(tag[0]&0xFFFF))*4;
break;
// DMAref
case 0x30000000:
printf("%08x %08x\n", tag[0], tag[1]);
tag += 4;
break;
// DMAret
case 0x60000000:
printf("%08x %08x\n", tag[0], tag[1]);
return;
}
}
}
/* Function to specifically walk geometry chains */
void
walkDMA(InstanceData *inst, void (*f)(uint32 *data, int32 size))
@ -471,7 +811,7 @@ walkDMA(InstanceData *inst, void (*f)(uint32 *data, int32 size))
break;
// DMAref
case 0x3000000:
case 0x30000000:
f(base + tag[1]*4, (tag[0]&0xFFFF)*4);
tag += 4;
break;

View File

@ -18,7 +18,7 @@ int version = 0x36003;
int build = 0xFFFF;
#ifdef RW_PS2
int platform = PLATFORM_PS2;
#elseif RW_OPENGL
#elif RW_OPENGL
int platform = PLATFORM_OPENGL;
#else
int platform = PLATFORM_NULL;

27
src/rwpipeline.h Normal file
View File

@ -0,0 +1,27 @@
namespace rw {
struct PipeAttribute
{
const char *name;
uint32 attrib;
};
struct Atomic;
struct Pipeline
{
uint32 pluginID;
uint32 pluginData;
uint32 platform;
PipeAttribute *attribs[10];
Pipeline(uint32 platform);
Pipeline(Pipeline *p);
~Pipeline(void);
virtual void instance(Atomic *atomic);
virtual void uninstance(Atomic *atomic);
virtual void render(Atomic *atomic);
};
}

View File

@ -29,6 +29,7 @@ void writeNativeData(Stream *stream, int32 len, void *object, int32, int32);
int32 getSizeNativeData(void *object, int32, int32);
void registerNativeDataPlugin(void);
void printDMA(InstanceData *inst);
void walkDMA(InstanceData *inst, void (*f)(uint32 *data, int32 size));
void sizedebug(InstanceData *inst);
@ -49,12 +50,16 @@ struct Pipeline : rw::Pipeline
}
Pipeline(uint32 platform);
virtual void instance(Atomic *atomic);
virtual void uninstance(Atomic *atomic);
// virtual void render(Atomic *atomic);
void setTriBufferSizes(uint32 inputStride,
uint32 stripCount, uint32 listCount);
};
Pipeline *makeDefaultPipeline(void);
Pipeline *makeSkinPipeline(void);
Pipeline *makeMatFXPipeline(void);
void dumpPipeline(rw::Pipeline *pipe);
// Skin plugin

View File

@ -109,10 +109,18 @@ dumpRasterPacket(int n)
printf("\n");
}
rw::Pipeline *defpipe;
void
drawAtomic(rw::Atomic *atomic)
{
rw::Geometry *geo = atomic->geometry;
if(!(geo->geoflags & rw::Geometry::NATIVE)){
if(atomic->pipeline)
atomic->pipeline->instance(atomic);
else
defpipe->instance(atomic);
}
assert(geo->instData != NULL);
rw::ps2::InstanceDataHeader *instData =
(rw::ps2::InstanceDataHeader*)geo->instData;
@ -178,10 +186,10 @@ draw(void)
gsClear();
matMakeIdentity(viewMat);
matTranslate(viewMat, 0.0f, 0.0f, -34.0f);
// matTranslate(viewMat, 0.0f, 0.0f, -34.0f);
// matTranslate(viewMat, 0.0f, 0.0f, -10.0f);
// matTranslate(viewMat, 0.0f, 0.0f, -8.0f);
// matTranslate(viewMat, 0.0f, 0.0f, -4.0f);
matTranslate(viewMat, 0.0f, 0.0f, -4.0f);
matRotateX(viewMat, rot);
matRotateY(viewMat, rot);
matRotateZ(viewMat, rot);
@ -199,7 +207,7 @@ draw(void)
matCopy(vuMat, m);
}
rot += 0.01f;
rot += 0.001f;
if(rot > 2*M_PI)
rot -= 2*M_PI;
}
@ -233,9 +241,14 @@ main()
// rw::ps2::registerNativeDataPlugin();
rw::registerMeshPlugin();
defpipe = rw::ps2::makeDefaultPipeline();
printf("platform: %d\n", rw::platform);
rw::uint32 len;
// rw::uint8 *data = rw::getFileContents("host:player-vc-ps2.dff", &len);
rw::uint8 *data = rw::getFileContents("host:od_newscafe_dy-ps2.dff", &len);
rw::uint8 *data = rw::getFileContents("host:player_pc.dff", &len);
// rw::uint8 *data = rw::getFileContents("host:od_newscafe_dy-ps2.dff", &len);
// rw::uint8 *data = rw::getFileContents("host:admiral-ps2.dff", &len);
rw::StreamMemory in;
in.open(data, len);