summaryrefslogtreecommitdiff
path: root/src/ps2
diff options
context:
space:
mode:
Diffstat (limited to 'src/ps2')
-rw-r--r--src/ps2/pds.cpp203
-rw-r--r--src/ps2/ps2.cpp1583
-rw-r--r--src/ps2/ps2device.cpp49
-rw-r--r--src/ps2/ps2matfx.cpp68
-rw-r--r--src/ps2/ps2raster.cpp2238
-rw-r--r--src/ps2/ps2skin.cpp334
-rw-r--r--src/ps2/rwps2.h281
-rw-r--r--src/ps2/rwps2impl.h16
-rw-r--r--src/ps2/rwps2plg.h27
9 files changed, 4799 insertions, 0 deletions
diff --git a/src/ps2/pds.cpp b/src/ps2/pds.cpp
new file mode 100644
index 0000000..afdc6e8
--- /dev/null
+++ b/src/ps2/pds.cpp
@@ -0,0 +1,203 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "../rwanim.h"
+#include "../rwplugins.h"
+#include "rwps2.h"
+#include "rwps2plg.h"
+
+namespace rw {
+namespace ps2 {
+
+struct PdsGlobals
+{
+ Pipeline **pipes;
+ int32 maxPipes;
+ int32 numPipes;
+};
+static PdsGlobals pdsGlobals;
+
+Pipeline*
+getPDSPipe(uint32 data)
+{
+ for(int32 i = 0; i < pdsGlobals.numPipes; i++)
+ if(pdsGlobals.pipes[i]->pluginData == data)
+ return pdsGlobals.pipes[i];
+ return nil;
+}
+
+void
+registerPDSPipe(Pipeline *pipe)
+{
+ if(pdsGlobals.pipes == nil)
+ pdsGlobals.pipes = rwNewT(Pipeline*, pdsGlobals.maxPipes, MEMDUR_GLOBAL | ID_PDS);
+ assert(pdsGlobals.numPipes < pdsGlobals.maxPipes);
+ pdsGlobals.pipes[pdsGlobals.numPipes++] = pipe;
+}
+
+static void
+atomicPDSRights(void *object, int32, int32, uint32 data)
+{
+ Atomic *a = (Atomic*)object;
+ a->pipeline = (ObjPipeline*)getPDSPipe(data);
+// printf("atm pds: %x %x %x\n", data, a->pipeline->pluginID, a->pipeline->pluginData);
+}
+
+static void
+materialPDSRights(void *object, int32, int32, uint32 data)
+{
+ Material *m = (Material*)object;
+ m->pipeline = (ObjPipeline*)getPDSPipe(data);
+// printf("mat pds: %x %x %x\n", data, m->pipeline->pluginID, m->pipeline->pluginData);
+}
+
+static void *pdsOpen(void *object, int32 offset, int32 size) { return object; }
+static void*
+pdsClose(void *object, int32 offset, int32 size)
+{
+ // TODO MEMORY: free registered pipelines
+ rwFree(pdsGlobals.pipes);
+ return object;
+}
+
+void
+registerPDSPlugin(int32 n)
+{
+ pdsGlobals.maxPipes = n;
+ pdsGlobals.numPipes = 0;
+ pdsGlobals.pipes = nil;
+ Engine::registerPlugin(0, ID_PDS, pdsOpen, pdsClose);
+ Atomic::registerPlugin(0, ID_PDS, nil, nil, nil);
+ Atomic::setStreamRightsCallback(ID_PDS, atomicPDSRights);
+
+ Material::registerPlugin(0, ID_PDS, nil, nil, nil);
+ Material::setStreamRightsCallback(ID_PDS, materialPDSRights);
+}
+
+void
+registerPluginPDSPipes(void)
+{
+ // TODO: how do we destroy them?
+
+ // rwPDS_G3_Skin_GrpMatPipeID
+ MatPipeline *pipe = MatPipeline::create();
+ pipe->pluginID = ID_PDS;
+ pipe->pluginData = 0x11001;
+ pipe->attribs[AT_XYZ] = &attribXYZ;
+ pipe->attribs[AT_UV] = &attribUV;
+ pipe->attribs[AT_RGBA] = &attribRGBA;
+ pipe->attribs[AT_NORMAL] = &attribNormal;
+ pipe->attribs[AT_NORMAL+1] = &attribWeights;
+ uint32 vertCount = MatPipeline::getVertCount(VU_Lights-0x100, 5, 3, 2);
+ pipe->setTriBufferSizes(5, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->instanceCB = skinInstanceCB;
+ pipe->uninstanceCB = genericUninstanceCB;
+ pipe->preUninstCB = skinPreCB;
+ pipe->postUninstCB = skinPostCB;
+ registerPDSPipe(pipe);
+
+ // rwPDS_G3_Skin_GrpAtmPipeID
+ ObjPipeline *opipe = ObjPipeline::create();
+ opipe->pluginID = ID_PDS;
+ opipe->pluginData = 0x11002;
+ opipe->groupPipeline = pipe;
+ registerPDSPipe(opipe);
+
+ // rwPDS_G3_MatfxUV1_GrpMatPipeID
+ pipe = MatPipeline::create();
+ pipe->pluginID = ID_PDS;
+ pipe->pluginData = 0x1100b;
+ pipe->attribs[AT_XYZ] = &attribXYZ;
+ pipe->attribs[AT_UV] = &attribUV;
+ pipe->attribs[AT_RGBA] = &attribRGBA;
+ pipe->attribs[AT_NORMAL] = &attribNormal;
+ vertCount = MatPipeline::getVertCount(0x3C5, 4, 3, 3);
+ pipe->setTriBufferSizes(4, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->uninstanceCB = genericUninstanceCB;
+ registerPDSPipe(pipe);
+
+ // rwPDS_G3_MatfxUV1_GrpAtmPipeID
+ opipe = ObjPipeline::create();
+ opipe->pluginID = ID_PDS;
+ opipe->pluginData = 0x1100d;
+ opipe->groupPipeline = pipe;
+ registerPDSPipe(opipe);
+
+ // rwPDS_G3_MatfxUV2_GrpMatPipeID
+ pipe = MatPipeline::create();
+ pipe->pluginID = ID_PDS;
+ pipe->pluginData = 0x1100c;
+ pipe->attribs[AT_XYZ] = &attribXYZ;
+ pipe->attribs[AT_UV] = &attribUV2;
+ pipe->attribs[AT_RGBA] = &attribRGBA;
+ pipe->attribs[AT_NORMAL] = &attribNormal;
+ vertCount = MatPipeline::getVertCount(0x3C5, 4, 3, 3);
+ pipe->setTriBufferSizes(4, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->uninstanceCB = genericUninstanceCB;
+ registerPDSPipe(pipe);
+
+ // rwPDS_G3_MatfxUV2_GrpAtmPipeID
+ opipe = ObjPipeline::create();
+ opipe->pluginID = ID_PDS;
+ opipe->pluginData = 0x1100e;
+ opipe->groupPipeline = pipe;
+ registerPDSPipe(opipe);
+
+ // RW World plugin
+
+ // rwPDS_G3x_Generic_AtmPipeID
+ opipe = ObjPipeline::create();
+ opipe->pluginID = ID_PDS;
+ opipe->pluginData = 0x50001;
+ registerPDSPipe(opipe);
+
+ // rwPDS_G3x_Skin_AtmPipeID
+ opipe = ObjPipeline::create();
+ opipe->pluginID = ID_PDS;
+ opipe->pluginData = 0x5000b;
+ registerPDSPipe(opipe);
+
+ // rwPDS_G3xd_A4D_MatPipeID
+ pipe = MatPipeline::create();
+ pipe->pluginID = ID_PDS;
+ pipe->pluginData = 0x5002f;
+ pipe->attribs[0] = &attribXYZW;
+ pipe->attribs[1] = &attribUV;
+ pipe->attribs[2] = &attribNormal;
+ vertCount = 0x50;
+ pipe->setTriBufferSizes(3, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount; // 0xF0
+ pipe->uninstanceCB = genericUninstanceCB;
+ pipe->preUninstCB = genericPreCB;
+ registerPDSPipe(pipe);
+
+ // rwPDS_G3xd_A4DSkin_MatPipeID
+ pipe = MatPipeline::create();
+ pipe->pluginID = ID_PDS;
+ pipe->pluginData = 0x5003e;
+ pipe->attribs[0] = &attribXYZW;
+ pipe->attribs[1] = &attribUV;
+ pipe->attribs[2] = &attribNormal;
+ pipe->attribs[3] = &attribWeights;
+ vertCount = 0x30;
+ pipe->setTriBufferSizes(4, vertCount); // 0xC0
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->instanceCB = skinInstanceCB;
+ pipe->uninstanceCB = genericUninstanceCB;
+ pipe->preUninstCB = genericPreCB;
+ pipe->postUninstCB = skinPostCB;
+ registerPDSPipe(pipe);
+}
+
+}
+}
diff --git a/src/ps2/ps2.cpp b/src/ps2/ps2.cpp
new file mode 100644
index 0000000..0f504d6
--- /dev/null
+++ b/src/ps2/ps2.cpp
@@ -0,0 +1,1583 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "../rwanim.h"
+#include "../rwplugins.h"
+#include "rwps2.h"
+#include "rwps2plg.h"
+
+#include "rwps2impl.h"
+
+#define PLUGIN_ID 2
+
+namespace rw {
+namespace ps2 {
+
+bool adcHack = false;
+
+#define ALIGNPTR(p,a) ((uint8*)(((uintptr)(p)+a-1) & ~(uintptr)(a-1)))
+
+static void*
+driverOpen(void *o, int32, int32)
+{
+ engine->driver[PLATFORM_PS2]->defaultPipeline = makeDefaultPipeline();
+
+ engine->driver[PLATFORM_PS2]->rasterNativeOffset = nativeRasterOffset;
+ engine->driver[PLATFORM_PS2]->rasterCreate = rasterCreate;
+ engine->driver[PLATFORM_PS2]->rasterLock = rasterLock;
+ engine->driver[PLATFORM_PS2]->rasterUnlock = rasterUnlock;
+ engine->driver[PLATFORM_PS2]->rasterLockPalette = rasterLockPalette;
+ engine->driver[PLATFORM_PS2]->rasterUnlockPalette = rasterUnlockPalette;
+ engine->driver[PLATFORM_PS2]->rasterNumLevels = rasterNumLevels;
+ engine->driver[PLATFORM_PS2]->imageFindRasterFormat = imageFindRasterFormat;
+ engine->driver[PLATFORM_PS2]->rasterFromImage = rasterFromImage;
+ engine->driver[PLATFORM_PS2]->rasterToImage = rasterToImage;
+
+ return o;
+}
+
+static void*
+driverClose(void *o, int32, int32)
+{
+ return o;
+}
+
+void
+registerPlatformPlugins(void)
+{
+ Driver::registerPlugin(PLATFORM_PS2, 0, PLATFORM_PS2,
+ driverOpen, driverClose);
+
+ registerNativeRaster();
+}
+
+ObjPipeline *defaultObjPipe;
+MatPipeline *defaultMatPipe;
+
+void*
+destroyNativeData(void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_PS2)
+ return object;
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ for(uint32 i = 0; i < header->numMeshes; i++)
+ rwFree(header->instanceMeshes[i].dataRaw);
+ rwFree(header->instanceMeshes);
+ rwFree(header);
+ geometry->instData = nil;
+ return object;
+}
+
+Stream*
+readNativeData(Stream *stream, int32, void *object, int32, int32)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_PS2){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ geometry->instData = header;
+ header->platform = PLATFORM_PS2;
+ assert(geometry->meshHeader != nil);
+ header->numMeshes = geometry->meshHeader->numMeshes;
+ header->instanceMeshes = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY);
+ Mesh *m = geometry->meshHeader->getMeshes();
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ InstanceData *instance = &header->instanceMeshes[i];
+ uint32 buf[2];
+ stream->read32(buf, 8);
+ instance->dataSize = buf[0];
+ instance->dataRaw = rwNewT(uint8, instance->dataSize+0x7F, MEMDUR_EVENT | ID_GEOMETRY);
+ instance->data = ALIGNPTR(instance->dataRaw, 0x80);
+#ifdef RW_PS2
+ uint32 a = (uint32)instance->data;
+ assert(a % 0x10 == 0);
+#endif
+ stream->read8(instance->data, instance->dataSize);
+#ifdef RW_PS2
+ if(!buf[1])
+ fixDmaOffsets(instance);
+#endif
+ instance->material = m->material;
+// sizedebug(instance);
+ m++;
+ }
+ return stream;
+}
+
+Stream*
+writeNativeData(Stream *stream, int32 len, void *object, int32, int32)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ writeChunkHeader(stream, ID_STRUCT, len-12);
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_PS2)
+ return stream;
+ stream->writeU32(PLATFORM_PS2);
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ InstanceData *instance = &header->instanceMeshes[i];
+ uint32 buf[2];
+ buf[0] = instance->dataSize;
+ buf[1] = unfixDmaOffsets(instance);
+ stream->write32(buf, 8);
+ stream->write8(instance->data, instance->dataSize);
+#ifdef RW_PS2
+ if(!buf[1])
+ fixDmaOffsets(instance);
+#endif
+ }
+ return stream;
+}
+
+int32
+getSizeNativeData(void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ int32 size = 16;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_PS2)
+ return 0;
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ InstanceData *instance = &header->instanceMeshes[i];
+ size += 8;
+ size += instance->dataSize;
+ }
+ return size;
+}
+
+void
+registerNativeDataPlugin(void)
+{
+ Geometry::registerPlugin(0, ID_NATIVEDATA,
+ nil, destroyNativeData, nil);
+ Geometry::registerPluginStream(ID_NATIVEDATA,
+ readNativeData,
+ writeNativeData,
+ getSizeNativeData);
+}
+
+// Patch DMA ref ADDR fields to point to the actual data.
+#ifdef RW_PS2
+void
+fixDmaOffsets(InstanceData *inst)
+{
+ uint32 base = (uint32)inst->data;
+ uint32 *tag = (uint32*)inst->data;
+ for(;;){
+ switch(tag[0]&0x70000000){
+ // DMAcnt
+ case 0x10000000:
+ // no need to fix
+ tag += (1+(tag[0]&0xFFFF))*4;
+ break;
+
+ // DMAref
+ case 0x30000000:
+ // fix address and jump to next
+ tag[1] = base + tag[1]<<4;
+ tag += 4;
+ break;
+
+ // DMAret
+ case 0x60000000:
+ // we're done
+ return;
+
+ default:
+ fprintf(stderr, "error: unknown DMAtag %X\n", tag[0]);
+ return;
+ }
+ }
+}
+#endif
+
+// Patch DMA ref ADDR fields to qword offsets and return whether
+// no ref tags were found.
+// Only under RW_PS2 are the addresses actually patched but we need
+// the return value for streaming out.
+bool32
+unfixDmaOffsets(InstanceData *inst)
+{
+ bool32 norefs = 1;
+#ifdef RW_PS2
+ uint32 base = (uint32)inst->data;
+#endif
+ uint32 *tag = (uint32*)inst->data;
+ for(;;){
+ switch(tag[0]&0x70000000){
+ // DMAcnt
+ case 0x10000000:
+ // no need to unfix
+ tag += (1+(tag[0]&0xFFFF))*4;
+ break;
+
+ // DMAref
+ case 0x30000000:
+ norefs = 0;
+ // unfix address and jump to next
+#ifdef RW_PS2
+ tag[1] = (tag[1] - base)>>4;
+#endif
+ tag += 4;
+ break;
+
+ // DMAret
+ case 0x60000000:
+ return norefs;
+
+ default:
+ fprintf(stderr, "error: unknown DMAtag %X\n", tag[0]);
+ return norefs;
+ }
+ }
+}
+
+// Pipeline
+
+PipeAttribute attribXYZ = {
+ "XYZ",
+ AT_V3_32
+};
+
+PipeAttribute attribXYZW = {
+ "XYZW",
+ AT_V4_32
+};
+
+PipeAttribute attribUV = {
+ "UV",
+ AT_V2_32
+};
+
+PipeAttribute attribUV2 = {
+ "UV2",
+ AT_V4_32
+};
+
+PipeAttribute attribRGBA = {
+ "RGBA",
+ AT_V4_8 | AT_UNSGN
+};
+
+PipeAttribute attribNormal = {
+ "Normal",
+ AT_V3_8 // RW has V4_8 but uses V3_8, wtf?
+};
+
+PipeAttribute attribWeights = {
+ "Weights",
+ AT_V4_32 | AT_RW
+};
+
+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(MatPipeline *pipe, uint32 vertCount)
+{
+ PipeAttribute *a;
+ if(vertCount == 0)
+ return 0;
+ uint32 size = 1; // ITOP &c. at the end
+ for(uint i = 0; i < nelem(pipe->attribs); i++)
+ if((a = pipe->attribs[i]) && (a->attrib & AT_RW) == 0){
+ size++; // UNPACK &c.
+ 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*
+instanceXYZW(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n)
+{
+ uint16 j;
+ uint32 *d = (uint32*)g->morphTargets[0].vertices;
+ int8 *adcbits = getADCbitsForMesh(g, m);
+ 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];
+ *p++ = adcbits && adcbits[i] ? 0x8000 : 0;
+ }
+ // don't need to pad
+ return p;
+}
+
+uint32*
+instanceUV(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n)
+{
+ uint16 j;
+ uint32 *d = (uint32*)g->texCoords[0];
+ if((g->flags & Geometry::TEXTURED) ||
+ (g->flags & 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*
+instanceUV2(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n)
+{
+ uint16 j;
+ uint32 *d0 = (uint32*)g->texCoords[0];
+ uint32 *d1 = (uint32*)g->texCoords[1];
+ for(uint32 i = idx; i < idx+n; i++){
+ j = m->indices[i];
+ if(g->numTexCoordSets > 0){
+ *p++ = d0[j*2+0];
+ *p++ = d0[j*2+1];
+ }else{
+ *p++ = 0;
+ *p++ = 0;
+ }
+ if(g->numTexCoordSets > 1){
+ *p++ = d1[j*2+0];
+ *p++ = d1[j*2+1];
+ }else{
+ *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->flags & 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;
+ V3d *d = g->morphTargets[0].normals;
+ uint8 *p = (uint8*)wp;
+ if((g->flags & Geometry::NORMALS))
+ for(uint32 i = idx; i < idx+n; i++){
+ j = m->indices[i];
+ *p++ = d[j].x*127.0f;
+ *p++ = d[j].y*127.0f;
+ *p++ = d[j].z*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;
+}
+
+void
+MatPipeline::init(void)
+{
+ this->rw::Pipeline::init(PLATFORM_PS2);
+ for(int i = 0; i < 10; i++)
+ this->attribs[i] = nil;
+ this->instanceCB = nil;
+ this->uninstanceCB = nil;
+ this->preUninstCB = nil;
+ this->postUninstCB = nil;
+}
+
+MatPipeline*
+MatPipeline::create(void)
+{
+ MatPipeline *pipe = rwNewT(MatPipeline, 1, MEMDUR_GLOBAL);
+ pipe->init();
+ return pipe;
+}
+
+void
+MatPipeline::destroy(void)
+{
+ rwFree(this);
+}
+
+void
+MatPipeline::dump(void)
+{
+ if(this->platform != PLATFORM_PS2)
+ return;
+ PipeAttribute *a;
+ printf("%x %x\n", this->pluginID, this->pluginData);
+ for(uint i = 0; i < nelem(this->attribs); i++){
+ a = this->attribs[i];
+ if(a)
+ printf("%d %s: %x\n", i, a->name, a->attrib);
+ }
+ printf("stride: %x\n", this->inputStride);
+ printf("vertcount: %x\n", this->vifOffset/this->inputStride);
+ printf("triSCount: %x\n", this->triStripCount);
+ printf("triLCount: %x\n", this->triListCount);
+ printf("vifOffset: %x\n", this->vifOffset);
+ printf("\n");
+}
+
+void
+MatPipeline::setTriBufferSizes(uint32 inputStride, uint32 bufferSize)
+{
+ PipeAttribute *a;
+
+ this->inputStride = inputStride;
+ uint32 numTLtris = bufferSize/3;
+ this->triListCount = (numTLtris & ~3) * 3;
+ this->triStripCount = bufferSize & ~3;
+ for(uint i = 0; i < nelem(this->attribs); i++){
+ a = this->attribs[i];
+ if(a && a->attrib & AT_RW){
+ // broken out attribs have different requirement
+ // because we have to be able to restart a strip
+ // at an aligned offset
+ this->triStripCount = ((bufferSize-2) & ~3)+2;
+ return;
+ }
+ }
+}
+
+// Instance format:
+// no broken out clusters
+// ======================
+// DMAret [FLUSH; MSKPATH3 || FLUSH; FLUSH] {
+// foreach batch {
+// foreach cluster {
+// MARK/0; STMOD; STCYCL; UNPACK
+// unpack-data
+// }
+// ITOP; MSCALF/MSCNT; // if first/not-first
+// 0/FLUSH; 0/MSKPATH3 || 0/FLUSH; 0/FLUSH // if not-last/last
+// }
+// }
+//
+// broken out clusters
+// ===================
+// foreach batch {
+// foreach broken out cluster {
+// DMAref [STCYCL; UNPACK] -> pointer into unpack-data
+// DMAcnt (empty)
+// }
+// DMAcnt/ret {
+// foreach cluster {
+// MARK/0; STMOD; STCYCL; UNPACK
+// unpack-data
+// }
+// ITOP; MSCALF/MSCNT; // if first/not-first
+// 0/FLUSH; 0/MSKPATH3 || 0/FLUSH; 0/FLUSH // if not-last/last
+// }
+// }
+// unpack-data for broken out clusters
+
+uint32 markcnt = 0;
+
+enum {
+ DMAcnt = 0x10000000,
+ DMAref = 0x30000000,
+ DMAret = 0x60000000,
+
+ VIF_NOP = 0,
+ VIF_STCYCL = 0x01000000,
+ VIF_STCYCL1 = 0x01000100, // WL = 1
+ VIF_OFFSET = 0x02000000,
+ VIF_BASE = 0x03000000,
+ VIF_ITOP = 0x04000000,
+ VIF_STMOD = 0x05000000,
+ VIF_MSKPATH3 = 0x06000000,
+ VIF_MARK = 0x07000000,
+ VIF_FLUSHE = 0x10000000,
+ VIF_FLUSH = 0x11000000,
+ VIF_FLUSHA = 0x13000000,
+ VIF_MSCAL = 0x14000000,
+ VIF_MSCALF = 0x15000000,
+ VIF_MSCNT = 0x17000000,
+ VIF_STMASK = 0x20000000,
+ VIF_STROW = 0x30000000,
+ VIF_STCOL = 0x31000000,
+ VIF_MPG = 0x4A000000,
+ VIF_DIRECT = 0x50000000,
+ VIF_DIRECTHL = 0x51000000,
+ VIF_UNPACK = 0x60000000 // no mode encoded
+};
+
+struct InstMeshInfo
+{
+ uint32 numAttribs, numBrokenAttribs;
+ uint32 batchVertCount, lastBatchVertCount;
+ uint32 numBatches;
+ uint32 batchSize, lastBatchSize;
+ uint32 size; // size of DMA chain without broken out data
+ uint32 size2; // size of broken out data
+ uint32 vertexSize;
+ uint32 attribPos[10];
+};
+
+InstMeshInfo
+getInstMeshInfo(MatPipeline *pipe, Geometry *g, Mesh *m)
+{
+ PipeAttribute *a;
+ InstMeshInfo im;
+ im.numAttribs = 0;
+ im.numBrokenAttribs = 0;
+ im.vertexSize = 0;
+ for(uint i = 0; i < nelem(pipe->attribs); i++)
+ if((a = pipe->attribs[i])) {
+ if(a->attrib & AT_RW)
+ im.numBrokenAttribs++;
+ else{
+ im.vertexSize += attribSize(a->attrib);
+ im.numAttribs++;
+ }
+ }
+ if(g->meshHeader->flags == MeshHeader::TRISTRIP){
+ im.numBatches = (m->numIndices-2) / (pipe->triStripCount-2);
+ im.batchVertCount = pipe->triStripCount;
+ im.lastBatchVertCount = (m->numIndices-2) % (pipe->triStripCount-2);
+ if(im.lastBatchVertCount){
+ im.numBatches++;
+ im.lastBatchVertCount += 2;
+ }
+ }else{ // TRILIST; nothing else supported yet
+ im.numBatches = (m->numIndices+pipe->triListCount-1) /
+ pipe->triListCount;
+ im.batchVertCount = pipe->triListCount;
+ im.lastBatchVertCount = m->numIndices % pipe->triListCount;
+ }
+ if(im.lastBatchVertCount == 0)
+ im.lastBatchVertCount = im.batchVertCount;
+
+ im.batchSize = getBatchSize(pipe, im.batchVertCount);
+ im.lastBatchSize = getBatchSize(pipe, im.lastBatchVertCount);
+ if(im.numBrokenAttribs == 0)
+ im.size = 1 + im.batchSize*(im.numBatches-1) + im.lastBatchSize;
+ else
+ im.size = 2*im.numBrokenAttribs*im.numBatches +
+ (1+im.batchSize)*(im.numBatches-1) + 1+im.lastBatchSize;
+
+ /* figure out size and addresses of broken out sections */
+ im.size2 = 0;
+ for(uint i = 0; i < nelem(im.attribPos); i++)
+ if((a = pipe->attribs[i]) && a->attrib & AT_RW){
+ im.attribPos[i] = im.size2 + im.size;
+ im.size2 += QWC(m->numIndices*attribSize(a->attrib));
+ }
+
+ return im;
+}
+
+void
+MatPipeline::instance(Geometry *g, InstanceData *inst, Mesh *m)
+{
+ PipeAttribute *a;
+ InstMeshInfo im = getInstMeshInfo(this, g, m);
+
+ inst->dataSize = (im.size+im.size2)<<4;
+ // TODO: do this properly, just a test right now
+ inst->dataSize += 0x7F;
+ inst->dataRaw = rwNewT(uint8, inst->dataSize, MEMDUR_EVENT | ID_GEOMETRY);
+ inst->data = ALIGNPTR(inst->dataRaw, 0x80);
+
+ /* make array of addresses of broken out sections */
+ uint8 *datap[nelem(this->attribs)];
+ uint8 **dp = datap;
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i]) && a->attrib & AT_RW)
+ dp[i] = inst->data + im.attribPos[i]*0x10;
+
+ // TODO: not sure if this is correct
+ uint32 msk_flush = rw::version >= 0x35000 ? VIF_FLUSH : VIF_MSKPATH3;
+
+ uint32 idx = 0;
+ uint32 *p = (uint32*)inst->data;
+ if(im.numBrokenAttribs == 0){
+ *p++ = DMAret | im.size-1;
+ *p++ = 0;
+ *p++ = VIF_FLUSH;
+ *p++ = msk_flush;
+ }
+ for(uint32 j = 0; j < im.numBatches; j++){
+ uint32 nverts, bsize;
+ if(j < im.numBatches-1){
+ bsize = im.batchSize;
+ nverts = im.batchVertCount;
+ }else{
+ bsize = im.lastBatchSize;
+ nverts = im.lastBatchVertCount;
+ }
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i]) && a->attrib & AT_RW){
+ uint32 atsz = attribSize(a->attrib);
+ *p++ = DMAref | QWC(nverts*atsz);
+ *p++ = im.attribPos[i];
+ *p++ = VIF_STCYCL1 | this->inputStride;
+ // Round up nverts so UNPACK will fit exactly into the DMA packet
+ // (can't pad with zeroes in broken out sections).
+ int num = (QWC(nverts*atsz)<<4)/atsz;
+ *p++ = (a->attrib&0xFF004000)
+ | 0x8000 | num << 16 | i; // UNPACK
+ // This probably shouldn't happen.
+ if(num*this->inputStride > this->vifOffset)
+ fprintf(stderr, "WARNING: PS2 instance data over vifOffset %08X, %X-> %X %X\n",
+ p[-1], num,
+ num*this->inputStride, this->vifOffset);
+
+ *p++ = DMAcnt;
+ *p++ = 0x0;
+ *p++ = VIF_NOP;
+ *p++ = VIF_NOP;
+
+ im.attribPos[i] += g->meshHeader->flags == 1 ?
+ QWC((im.batchVertCount-2)*atsz) :
+ QWC(im.batchVertCount*atsz);
+ }
+ if(im.numBrokenAttribs){
+ *p++ = (j < im.numBatches-1 ? DMAcnt : DMAret) | bsize;
+ *p++ = 0x0;
+ *p++ = VIF_NOP;
+ *p++ = VIF_NOP;
+ }
+
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i]) && (a->attrib & AT_RW) == 0){
+ uint32 mask;
+ if(adcHack && a == &attribXYZ){
+ *p++ = VIF_STMASK;
+ *p++ = 1 << 6; // write w field with VIF1_R3
+ mask = 0x10000000;
+ }else{
+ if(rw::version >= 0x35000)
+ *p++ = VIF_NOP;
+ else
+ *p++ = VIF_MARK | markcnt++;
+ *p++ = VIF_STMOD;
+ mask = 0x00000000;
+ }
+ *p++ = VIF_STCYCL1 | this->inputStride;
+ *p++ = (a->attrib&0xFF004000) | mask
+ | 0x8000 | nverts << 16 | i; // UNPACK
+
+ if(a == &attribXYZ)
+ p = instanceXYZ(p, g, m, idx, nverts);
+ else if(a == &attribXYZW)
+ p = instanceXYZW(p, g, m, idx, nverts);
+ else if(a == &attribUV)
+ p = instanceUV(p, g, m, idx, nverts);
+ else if(a == &attribUV2)
+ p = instanceUV2(p, g, m, idx, nverts);
+ else if(a == &attribRGBA)
+ p = instanceRGBA(p, g, m, idx, nverts);
+ else if(a == &attribNormal)
+ p = instanceNormal(p, g, m, idx, nverts);
+ }
+ idx += g->meshHeader->flags == 1
+ ? im.batchVertCount-2 : im.batchVertCount;
+
+ *p++ = VIF_ITOP | nverts;
+ *p++ = j == 0 ? VIF_MSCALF : VIF_MSCNT;
+ if(j < im.numBatches-1){
+ *p++ = VIF_NOP;
+ *p++ = VIF_NOP;
+ }else{
+ *p++ = VIF_FLUSH;
+ *p++ = msk_flush;
+ }
+ }
+
+ if(this->instanceCB)
+ this->instanceCB(this, g, m, datap);
+#ifdef RW_PS2
+ if(im.numBrokenAttribs)
+ fixDmaOffsets(inst);
+#endif
+}
+
+uint8*
+MatPipeline::collectData(Geometry *g, InstanceData *inst, Mesh *m, uint8 *data[])
+{
+ PipeAttribute *a;
+ InstMeshInfo im = getInstMeshInfo(this, g, m);
+
+ uint8 *raw = rwNewT(uint8, im.vertexSize*m->numIndices, MEMDUR_EVENT | ID_GEOMETRY);
+ uint8 *dp = raw;
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i])) {
+ if(a->attrib & AT_RW){
+ data[i] = inst->data + im.attribPos[i]*0x10;
+ }else{
+ data[i] = dp;
+ dp += m->numIndices*attribSize(a->attrib);
+ }
+ }
+
+ uint8 *datap[nelem(this->attribs)];
+ memcpy(datap, data, sizeof(datap));
+
+ uint32 overlap = g->meshHeader->flags == 1 ? 2 : 0;
+ uint32 *p = (uint32*)inst->data;
+ if(im.numBrokenAttribs == 0)
+ p += 4;
+ for(uint32 j = 0; j < im.numBatches; j++){
+ uint32 nverts = j < im.numBatches-1 ? im.batchVertCount :
+ im.lastBatchVertCount;
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i]) && a->attrib & AT_RW)
+ p += 8;
+ if(im.numBrokenAttribs)
+ p += 4;
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i]) && (a->attrib & AT_RW) == 0){
+ uint32 asz = attribSize(a->attrib);
+ p += 4;
+ if((p[-1] & 0xff004000) != a->attrib){
+ fprintf(stderr, "unexpected unpack: %08x %08x\n", p[-1], a->attrib);
+ assert(0 && "unexpected unpack\n");
+ }
+ memcpy(datap[i], p, asz*nverts);
+ datap[i] += asz*(nverts-overlap);
+ p += QWC(asz*nverts)*4;
+ }
+ p += 4;
+ }
+ return raw;
+}
+
+static void
+objInstance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ // TODO: allow for REINSTANCE
+ if(geo->instData)
+ return;
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ geo->instData = header;
+ header->platform = PLATFORM_PS2;
+ assert(geo->meshHeader != nil);
+ header->numMeshes = geo->meshHeader->numMeshes;
+ header->instanceMeshes = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY);
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Mesh *mesh = &geo->meshHeader->getMeshes()[i];
+ InstanceData *instance = &header->instanceMeshes[i];
+
+ MatPipeline *m;
+ m = pipe->groupPipeline ?
+ pipe->groupPipeline :
+ (MatPipeline*)mesh->material->pipeline;
+ if(m == nil)
+ m = defaultMatPipe;
+ m->instance(geo, instance, mesh);
+ instance->material = mesh->material;
+ }
+}
+
+/*
+static void
+printVertCounts(InstanceData *inst, int flag)
+{
+ uint32 *d = (uint32*)inst->data;
+ uint32 id = 0;
+ if(inst->material->pipeline)
+ id = inst->material->pipeline->pluginData;
+ int stride;
+ if(inst->arePointersFixed){
+ d += 4;
+ while(d[3]&0x60000000){ // skip UNPACKs
+ stride = d[2]&0xFF;
+ d += 4 + 4*QWC(attribSize(d[3])*((d[3]>>16)&0xFF));
+ }
+ if(d[2] == 0)
+ printf("ITOP %x %d (%d) %x\n", *d, stride, flag, id);
+ }else{
+ while((*d&0x70000000) == 0x30000000){
+ stride = d[2]&0xFF;
+ printf("UNPACK %x %d (%d) %x\n", d[3], stride, flag, id);
+ d += 8;
+ }
+ if((*d&0x70000000) == 0x10000000){
+ d += (*d&0xFFFF)*4;
+ printf("ITOP %x %d (%d) %x\n", *d, stride, flag, id);
+ }
+ }
+}
+*/
+
+static void
+objUninstance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ if((geo->flags & Geometry::NATIVE) == 0)
+ return;
+ assert(geo->instData != nil);
+ assert(geo->instData->platform == PLATFORM_PS2);
+ InstanceDataHeader *header = (InstanceDataHeader*)geo->instData;
+ // highest possible number of vertices
+ geo->numVertices = geo->meshHeader->totalIndices;
+ geo->numTriangles = geo->meshHeader->guessNumTriangles();
+ geo->allocateData();
+ geo->allocateMeshes(geo->meshHeader->numMeshes, geo->meshHeader->totalIndices, 0);
+ uint32 *flags = rwNewT(uint32, geo->numVertices,
+ MEMDUR_FUNCTION | ID_GEOMETRY);
+ memset(flags, 0, 4*geo->numVertices);
+ memset(geo->meshHeader->getMeshes()->indices, 0, 2*geo->meshHeader->totalIndices);
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Mesh *mesh = &geo->meshHeader->getMeshes()[i];
+ MatPipeline *m;
+ m = pipe->groupPipeline ?
+ pipe->groupPipeline :
+ (MatPipeline*)mesh->material->pipeline;
+ if(m == nil) m = defaultMatPipe;
+ if(m->preUninstCB) m->preUninstCB(m, geo);
+ }
+ geo->numVertices = 0;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Mesh *mesh = &geo->meshHeader->getMeshes()[i];
+ InstanceData *instance = &header->instanceMeshes[i];
+ MatPipeline *m;
+ m = pipe->groupPipeline ?
+ pipe->groupPipeline :
+ (MatPipeline*)mesh->material->pipeline;
+ if(m == nil) m = defaultMatPipe;
+
+ //printDMAVIF(instance);
+ uint8 *data[nelem(m->attribs)] = { nil };
+ uint8 *raw = m->collectData(geo, instance, mesh, data);
+ assert(m->uninstanceCB);
+ m->uninstanceCB(m, geo, flags, mesh, data);
+ rwFree(raw);
+ }
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Mesh *mesh = &geo->meshHeader->getMeshes()[i];
+ MatPipeline *m;
+ m = pipe->groupPipeline ?
+ pipe->groupPipeline :
+ (MatPipeline*)mesh->material->pipeline;
+ if(m == nil) m = defaultMatPipe;
+ if(m->postUninstCB) m->postUninstCB(m, geo);
+ }
+
+ int8 *bits = getADCbits(geo);
+ geo->generateTriangles(bits);
+ rwFree(flags);
+ geo->flags &= ~Geometry::NATIVE;
+ destroyNativeData(geo, 0, 0);
+/*
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Mesh *mesh = &geo->meshHeader->mesh[i];
+ InstanceData *instance = &header->instanceMeshes[i];
+// printf("numIndices: %d\n", mesh->numIndices);
+// printDMA(instance);
+ printVertCounts(instance, geo->meshHeader->flags);
+ }
+*/
+}
+
+void
+ObjPipeline::init(void)
+{
+ this->rw::ObjPipeline::init(PLATFORM_PS2);
+ this->groupPipeline = nil;
+ this->impl.instance = objInstance;
+ this->impl.uninstance = objUninstance;
+}
+
+ObjPipeline*
+ObjPipeline::create(void)
+{
+ ObjPipeline *pipe = rwNewT(ObjPipeline, 1, MEMDUR_GLOBAL);
+ pipe->init();
+ return pipe;
+}
+
+void
+insertVertex(Geometry *geo, int32 i, uint32 mask, Vertex *v)
+{
+ if(mask & 0x1)
+ geo->morphTargets[0].vertices[i] = v->p;
+ if(mask & 0x10)
+ geo->morphTargets[0].normals[i] = v->n;
+ if(mask & 0x100)
+ geo->colors[i] = v->c;
+ if(mask & 0x1000)
+ geo->texCoords[0][i] = v->t;
+ if(mask & 0x2000)
+ geo->texCoords[1][i] = v->t1;
+}
+
+void
+genericPreCB(MatPipeline *pipe, Geometry *geo)
+{
+ PipeAttribute *a;
+ for(int32 i = 0; i < (int)nelem(pipe->attribs); i++)
+ if((a = pipe->attribs[i]))
+ if(a == &attribXYZW){
+ allocateADC(geo);
+ break;
+ }
+ skinPreCB(pipe, geo);
+}
+
+void
+genericUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[])
+{
+ float32 *xyz = nil, *xyzw = nil;
+ float32 *uv = nil, *uv2 = nil;
+ uint8 *rgba = nil;
+ int8 *normals = nil;
+ uint32 *weights = nil;
+ int8 *adc = nil;
+ Skin *skin = nil;
+ if(skinGlobals.geoOffset)
+ skin = Skin::get(geo);
+
+ PipeAttribute *a;
+ for(int32 i = 0; i < (int)nelem(pipe->attribs); i++)
+ if((a = pipe->attribs[i])){
+ if(a == &attribXYZ) xyz = (float32*)data[i];
+ else if(a == &attribXYZW) xyzw = (float32*)data[i];
+ else if(a == &attribUV) uv = (float32*)data[i];
+ else if(a == &attribUV2) uv2 = (float32*)data[i];
+ else if(a == &attribRGBA) rgba = data[i];
+ else if(a == &attribNormal) normals = (int8*)data[i];
+ else if(a == &attribWeights) weights = (uint32*)data[i];
+ }
+
+ uint32 mask = 0x1; // vertices
+ if(normals && geo->flags & Geometry::NORMALS)
+ mask |= 0x10;
+ if(rgba && geo->flags & Geometry::PRELIT)
+ mask |= 0x100;
+ if((uv || uv2) && geo->numTexCoordSets > 0)
+ mask |= 0x1000;
+ if(uv2 && geo->numTexCoordSets > 1)
+ mask |= 0x2000;
+ if(weights && skin)
+ mask |= 0x10000;
+ if(xyzw)
+ adc = getADCbitsForMesh(geo, mesh);
+
+ Vertex v;
+ for(uint32 i = 0; i < mesh->numIndices; i++){
+ if(mask & 0x1)
+ memcpy(&v.p, xyz ? xyz : xyzw, 12);
+ if(mask & 0x10){
+ // TODO: figure out scaling :/
+ v.n.x = normals[0]/128.0f;
+ v.n.y = normals[1]/128.0f;
+ v.n.z = normals[2]/128.0f;
+ }
+ if(mask & 0x100)
+ memcpy(&v.c, rgba, 4);
+ if(mask & 0x1000)
+ memcpy(&v.t, uv ? uv : uv2, 8);
+ if(mask & 0x2000)
+ memcpy(&v.t1, uv2 + 2, 8);
+ if(mask & 0x10000)
+ for(int j = 0; j < 4; j++){
+ ((uint32*)v.w)[j] = weights[j] & ~0x3FF;
+ v.i[j] = (weights[j] & 0x3FF) >> 2;
+ if(v.i[j]) v.i[j]--;
+ if(v.w[j] == 0.0f) v.i[j] = 0;
+ }
+ int32 idx = findVertexSkin(geo, flags, mask, &v);
+ if(idx < 0)
+ idx = geo->numVertices++;
+ mesh->indices[i] = idx;
+ if(adc)
+ adc[i] = xyzw[3] != 0.0f;
+ flags[idx] = mask;
+ insertVertexSkin(geo, idx, mask, &v);
+ if(xyz) xyz += 3;
+ if(xyzw) xyzw += 4;
+ if(uv) uv += 2;
+ if(uv2) uv2 += 4;
+ rgba += 4;
+ normals += 3;
+ weights += 4;
+ }
+}
+
+/*
+void
+defaultUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[])
+{
+ float32 *verts = (float32*)data[AT_XYZ];
+ float32 *texcoords = (float32*)data[AT_UV];
+ uint8 *colors = (uint8*)data[AT_RGBA];
+ int8 *norms = (int8*)data[AT_NORMAL];
+ uint32 mask = 0x1; // vertices
+ if(geo->flags & Geometry::NORMALS)
+ mask |= 0x10;
+ if(geo->flags & Geometry::PRELIT)
+ mask |= 0x100;
+ for(int32 i = 0; i < geo->numTexCoordSets; i++)
+ mask |= 0x1000 << i;
+ int numUV = pipe->attribs[AT_UV] == &attribUV2 ? 2 : 1;
+
+ Vertex v;
+ for(uint32 i = 0; i < mesh->numIndices; i++){
+ if(mask & 0x1)
+ memcpy(&v.p, verts, 12);
+ if(mask & 0x10){
+ v.n[0] = norms[0]/127.0f;
+ v.n[1] = norms[1]/127.0f;
+ v.n[2] = norms[2]/127.0f;
+ }
+ if(mask & 0x100){
+ memcpy(&v.c, colors, 4);
+ //v.c[3] = 0xFF;
+ }
+ if(mask & 0x1000)
+ memcpy(&v.t, texcoords, 8);
+ if(mask & 0x2000)
+ memcpy(&v.t1, texcoords+2, 8);
+
+ int32 idx = findVertex(geo, flags, mask, &v);
+ if(idx < 0)
+ idx = geo->numVertices++;
+ mesh->indices[i] = idx;
+ flags[idx] = mask;
+ insertVertex(geo, idx, mask, &v);
+ verts += 3;
+ texcoords += 2*numUV;
+ colors += 4;
+ norms += 3;
+ }
+}
+*/
+
+#undef QWC
+
+ObjPipeline*
+makeDefaultPipeline(void)
+{
+ if(defaultMatPipe == nil){
+ MatPipeline *pipe = MatPipeline::create();
+ pipe->attribs[AT_XYZ] = &attribXYZ;
+ pipe->attribs[AT_UV] = &attribUV;
+ pipe->attribs[AT_RGBA] = &attribRGBA;
+ pipe->attribs[AT_NORMAL] = &attribNormal;
+ uint32 vertCount = MatPipeline::getVertCount(VU_Lights,4,3,2);
+ pipe->setTriBufferSizes(4, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->uninstanceCB = genericUninstanceCB;
+ defaultMatPipe = pipe;
+ }
+
+ if(defaultObjPipe == nil){
+ ObjPipeline *opipe = ObjPipeline::create();
+ defaultObjPipe = opipe;
+ }
+ return defaultObjPipe;
+}
+
+// ADC
+
+int32 adcOffset;
+
+int8*
+getADCbits(Geometry *geo)
+{
+ int8 *bits = nil;
+ if(adcOffset){
+ ADCData *adc = PLUGINOFFSET(ADCData, geo, adcOffset);
+ if(adc->adcFormatted)
+ bits = adc->adcBits;
+ }
+ return bits;
+}
+
+int8*
+getADCbitsForMesh(Geometry *geo, Mesh *mesh)
+{
+ int8 *bits = getADCbits(geo);
+ if(bits == nil)
+ return nil;
+ int32 n = mesh - geo->meshHeader->getMeshes();
+ for(int32 i = 0; i < n; i++)
+ bits += geo->meshHeader->getMeshes()[i].numIndices;
+ return bits;
+}
+
+// TODO
+void
+convertADC(Geometry*)
+{
+}
+
+// Not optimal but works
+void
+unconvertADC(Geometry *g)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, g, adcOffset);
+ if(!adc->adcFormatted)
+ return;
+ int8 *b = adc->adcBits;
+
+ MeshHeader *oldmh = g->meshHeader;
+ g->meshHeader = nil;
+ // Don't allocate indices for now
+ MeshHeader *newmh = g->allocateMeshes(oldmh->numMeshes, 0, 1);
+ newmh->flags = oldmh->flags; // should be tristrip
+ Mesh *oldm = oldmh->getMeshes();
+ Mesh *newm = newmh->getMeshes();
+ for(int32 i = 0; i < newmh->numMeshes; i++){
+ newm->material = oldm->material;
+ newm->numIndices = oldm->numIndices;
+ for(uint32 j = 0; j < oldm->numIndices; j++)
+ if(*b++)
+ newm->numIndices += 2;
+ newmh->totalIndices += newm->numIndices;
+ newm++;
+ oldm++;
+ }
+ // Now re-allocate with indices
+ newmh = g->allocateMeshes(newmh->numMeshes, newmh->totalIndices, 0);
+ b = adc->adcBits;
+ oldm = oldmh->getMeshes();
+ newm = newmh->getMeshes();
+ for(int32 i = 0; i < newmh->numMeshes; i++){
+ int32 n = 0;
+ for(uint32 j = 0; j < oldm->numIndices; j++){
+ if(*b++){
+ newm->indices[n++] = oldm->indices[j-1];
+ newm->indices[n++] = oldm->indices[j-1];
+ }
+ newm->indices[n++] = oldm->indices[j];
+ }
+ newm++;
+ oldm++;
+ }
+ rwFree(oldmh);
+ adc->adcFormatted = 0;
+ rwFree(adc->adcBits);
+ adc->adcBits = nil;
+ adc->numBits = 0;
+}
+
+void
+allocateADC(Geometry *geo)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, geo, adcOffset);
+ adc->adcFormatted = 1;
+ adc->numBits = geo->meshHeader->totalIndices;
+ int32 size = adc->numBits+3 & ~3;
+ adc->adcBits = rwNewT(int8, size, MEMDUR_EVENT | ID_ADC);
+ memset(adc->adcBits, 0, size);
+}
+
+static void*
+createADC(void *object, int32 offset, int32)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, object, offset);
+ adc->adcFormatted = 0;
+ return object;
+}
+
+static void*
+copyADC(void *dst, void *src, int32 offset, int32)
+{
+ ADCData *dstadc = PLUGINOFFSET(ADCData, dst, offset);
+ ADCData *srcadc = PLUGINOFFSET(ADCData, src, offset);
+ dstadc->adcFormatted = srcadc->adcFormatted;
+ if(!dstadc->adcFormatted)
+ return dst;
+ dstadc->numBits = srcadc->numBits;
+ int32 size = dstadc->numBits+3 & ~3;
+ dstadc->adcBits = rwNewT(int8, size, MEMDUR_EVENT | ID_ADC);
+ memcpy(dstadc->adcBits, srcadc->adcBits, size);
+ return dst;
+}
+
+static void*
+destroyADC(void *object, int32 offset, int32)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, object, offset);
+ if(adc->adcFormatted)
+ rwFree(adc->adcBits);
+ return object;
+}
+
+static Stream*
+readADC(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, object, offset);
+ if(!findChunk(stream, ID_ADC, nil, nil)){
+ RWERROR((ERR_CHUNK, "ADC"));
+ return nil;
+ }
+ adc->numBits = stream->readI32();
+ adc->adcFormatted = 1;
+ if(adc->numBits == 0){
+ adc->adcBits = nil;
+ adc->numBits = 0;
+ return stream;
+ }
+ int32 size = adc->numBits+3 & ~3;
+ adc->adcBits = rwNewT(int8, size, MEMDUR_EVENT | ID_ADC);
+ stream->read8(adc->adcBits, size);
+ return stream;
+}
+
+static Stream*
+writeADC(Stream *stream, int32 len, void *object, int32 offset, int32)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, object, offset);
+ Geometry *geometry = (Geometry*)object;
+ writeChunkHeader(stream, ID_ADC, len-12);
+ if(geometry->flags & Geometry::NATIVE){
+ stream->writeI32(0);
+ return stream;
+ }
+ stream->writeI32(adc->numBits);
+ int32 size = adc->numBits+3 & ~3;
+ stream->write8(adc->adcBits, size);
+ return stream;
+}
+
+static int32
+getSizeADC(void *object, int32 offset, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ ADCData *adc = PLUGINOFFSET(ADCData, object, offset);
+ if(!adc->adcFormatted)
+ return 0;
+ if(geometry->flags & Geometry::NATIVE)
+ return 16;
+ return 16 + (adc->numBits+3 & ~3);
+}
+
+void
+registerADCPlugin(void)
+{
+ adcOffset = Geometry::registerPlugin(sizeof(ADCData), ID_ADC,
+ createADC, destroyADC, copyADC);
+ Geometry::registerPluginStream(ID_ADC,
+ readADC,
+ writeADC,
+ getSizeADC);
+}
+
+// misc stuff
+
+static uint32
+unpackSize(uint32 unpack)
+{
+ static uint32 size[] = { 32, 16, 8, 4 };
+ return ((unpack>>26 & 3)+1)*size[unpack>>24 & 3]/8;
+}
+
+/* A little dumb VIF interpreter */
+static void
+sendVIF(uint32 w)
+{
+ enum VIFstate {
+ VST_cmd,
+ VST_stmask,
+ VST_strow,
+ VST_stcol,
+ VST_mpg,
+ VST_direct,
+ VST_unpack
+ };
+// static uint32 buf[256 * 16]; // maximum unpack size
+ static VIFstate state = VST_cmd;
+ static uint32 n;
+ static uint32 code;
+ uint32 imm, num;
+
+ imm = w & 0xFFFF;
+ num = (w>>16) & 0xFF;
+ switch(state){
+ case VST_cmd:
+ code = w;
+ if((code & 0x60000000) == VIF_UNPACK){
+ printf("\t%08X VIF_UNPACK\n", code);
+ printf("\t...skipping...\n");
+ state = VST_unpack;
+ n = (unpackSize(code)*num + 3) >> 2;
+ }else switch(code & 0x7F000000){
+ case VIF_NOP:
+ printf("\t%08X VIF_NOP\n", code);
+ break;
+ case VIF_STCYCL:
+ printf("\t%08X VIF_STCYCL\n", code);
+ break;
+ case VIF_OFFSET:
+ printf("\t%08X VIF_OFFSET\n", code);
+ break;
+ case VIF_BASE:
+ printf("\t%08X VIF_BASE\n", code);
+ break;
+ case VIF_ITOP:
+ printf("\t%08X VIF_ITOP\n", code);
+ break;
+ case VIF_STMOD:
+ printf("\t%08X VIF_STMOD\n", code);
+ break;
+ case VIF_MSKPATH3:
+ printf("\t%08X VIF_MSKPATH3\n", code);
+ break;
+ case VIF_MARK:
+ printf("\t%08X VIF_MARK\n", code);
+ break;
+ case VIF_FLUSHE:
+ printf("\t%08X VIF_FLUSHE\n", code);
+ break;
+ case VIF_FLUSH:
+ printf("\t%08X VIF_FLUSH\n", code);
+ break;
+ case VIF_FLUSHA:
+ printf("\t%08X VIF_FLUSHA\n", code);
+ break;
+ case VIF_MSCAL:
+ printf("\t%08X VIF_MSCAL\n", code);
+ break;
+ case VIF_MSCALF:
+ printf("\t%08X VIF_MSCALF\n", code);
+ break;
+ case VIF_MSCNT:
+ printf("\t%08X VIF_MSCNT\n", code);
+ break;
+ case VIF_STMASK:
+ printf("\t%08X VIF_STMASK\n", code);
+ printf("\t...skipping...\n");
+ state = VST_stmask;
+ n = 1;
+ break;
+ case VIF_STROW:
+ printf("\t%08X VIF_STROW\n", code);
+ printf("\t...skipping...\n");
+ state = VST_strow;
+ n = 4;
+ break;
+ case VIF_STCOL:
+ printf("\t%08X VIF_STCOL\n", code);
+ printf("\t...skipping...\n");
+ state = VST_stcol;
+ n = 4;
+ break;
+ case VIF_MPG:
+ printf("\t%08X VIF_MPG\n", code);
+ state = VST_mpg;
+ n = num*2;
+ break;
+ case VIF_DIRECT:
+ printf("\t%08X VIF_DIRECT\n", code);
+ printf("\t...skipping...\n");
+ state = VST_direct;
+ n = imm*4;
+ break;
+ case VIF_DIRECTHL:
+ printf("\t%08X VIF_DIRECTHL\n", code);
+ printf("\t...skipping...\n");
+ state = VST_direct;
+ n = imm*4;
+ break;
+ default:
+ printf("\tUnknown VIFcode %08X\n", code);
+ }
+ break;
+ /* TODO: actually do something here */
+ case VST_stmask:
+ n--;
+ break;
+ case VST_strow:
+ n--;
+ break;
+ case VST_stcol:
+ n--;
+ break;
+ case VST_mpg:
+ n--;
+ break;
+ case VST_direct:
+ n--;
+ break;
+ case VST_unpack:
+ n--;
+ break;
+ }
+ if(n == 0)
+ state = VST_cmd;
+}
+
+static void
+dmaVIF(int32 qwc, uint32 *data)
+{
+ qwc *= 4;
+ while(qwc--)
+ sendVIF(*data++);
+}
+
+void
+printDMAVIF(InstanceData *inst)
+{
+ uint32 *tag = (uint32*)inst->data;
+ uint32 *base = (uint32*)inst->data;
+ uint32 qwc;
+
+ for(;;){
+ qwc = tag[0]&0xFFFF;
+ switch(tag[0]&0x70000000){
+ case DMAcnt:
+ printf("DMAcnt %04x %08x\n", qwc, tag[1]);
+ sendVIF(tag[2]);
+ sendVIF(tag[3]);
+ dmaVIF(qwc, tag+4);
+ tag += (1+qwc)*4;
+ break;
+
+ case DMAref:
+ printf("DMAref %04x %08x\n", qwc, tag[1]);
+ sendVIF(tag[2]);
+ sendVIF(tag[3]);
+ dmaVIF(qwc, base + tag[1]*4);
+ tag += 4;
+ break;
+
+ case DMAret:
+ printf("DMAret %04x %08x\n", qwc, tag[1]);
+ sendVIF(tag[2]);
+ sendVIF(tag[3]);
+ dmaVIF(qwc, tag+4);
+ printf("\n");
+ return;
+ }
+ }
+}
+
+void
+printDMA(InstanceData *inst)
+{
+ uint32 *tag = (uint32*)inst->data;
+ uint32 qwc;
+ for(;;){
+ qwc = tag[0]&0xFFFF;
+ switch(tag[0]&0x70000000){
+ case DMAcnt:
+ printf("CNT %04x %08x\n", qwc, tag[1]);
+ tag += (1+qwc)*4;
+ break;
+
+ case DMAref:
+ printf("REF %04x %08x\n", qwc, tag[1]);
+ tag += 4;
+ break;
+
+ case DMAret:
+ printf("RET %04x %08x\n\n", qwc, tag[1]);
+ return;
+ }
+ }
+}
+
+/*
+void
+sizedebug(InstanceData *inst)
+{
+ if(inst->arePointersFixed == 2)
+ return;
+ uint32 *base = (uint32*)inst->data;
+ uint32 *tag = (uint32*)inst->data;
+ uint32 *last = nil;
+ for(;;){
+ switch(tag[0]&0x70000000){
+ case DMAcnt:
+ tag += (1+(tag[0]&0xFFFF))*4;
+ break;
+
+ case DMAref:
+ last = base + tag[1]*4 + (tag[0]&0xFFFF)*4;
+ tag += 4;
+ break;
+
+ case DMAret:
+ tag += (1+(tag[0]&0xFFFF))*4;
+ uint32 diff;
+ if(!last)
+ diff = (uint8*)tag - (uint8*)base;
+ else
+ diff = (uint8*)last - (uint8*)base;
+ printf("%x %x %x\n", inst->dataSize-diff, diff, inst->dataSize);
+ return;
+
+ default:
+ printf("unkown DMAtag: %X %X\n", tag[0], tag[1]);
+ break;
+ }
+ }
+}
+*/
+
+}
+}
diff --git a/src/ps2/ps2device.cpp b/src/ps2/ps2device.cpp
new file mode 100644
index 0000000..98f9914
--- /dev/null
+++ b/src/ps2/ps2device.cpp
@@ -0,0 +1,49 @@
+#ifdef RW_PS2
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "../rwanim.h"
+#include "../rwplugins.h"
+#include "rwps2.h"
+#include "rwps2plg.h"
+
+#include "rwps2impl.h"
+
+#define PLUGIN_ID 2
+
+namespace rw {
+namespace ps2 {
+
+Device renderdevice = {
+ 16777215.0f, 0.0f,
+ null::beginUpdate,
+ null::endUpdate,
+ null::clearCamera,
+ null::showRaster,
+ null::rasterRenderFast,
+ null::setRenderState,
+ null::getRenderState,
+ null::im2DRenderLine,
+ null::im2DRenderTriangle,
+ null::im2DRenderPrimitive,
+ null::im2DRenderIndexedPrimitive,
+ null::im3DTransform,
+ null::im3DRenderPrimitive,
+ null::im3DRenderIndexedPrimitive,
+ null::im3DEnd,
+ null::deviceSystem
+};
+
+}
+}
+
+#endif
diff --git a/src/ps2/ps2matfx.cpp b/src/ps2/ps2matfx.cpp
new file mode 100644
index 0000000..6972a05
--- /dev/null
+++ b/src/ps2/ps2matfx.cpp
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwplugins.h"
+#include "rwps2.h"
+#include "rwps2plg.h"
+
+#define PLUGIN_ID ID_MATFX
+
+namespace rw {
+namespace ps2 {
+
+static void*
+matfxOpen(void *o, int32, int32)
+{
+ matFXGlobals.pipelines[PLATFORM_PS2] = makeMatFXPipeline();
+ return o;
+}
+
+static void*
+matfxClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)matFXGlobals.pipelines[PLATFORM_PS2])->groupPipeline->destroy();
+ ((ObjPipeline*)matFXGlobals.pipelines[PLATFORM_PS2])->destroy();
+ matFXGlobals.pipelines[PLATFORM_PS2] = nil;
+ return o;
+}
+
+void
+initMatFX(void)
+{
+ Driver::registerPlugin(PLATFORM_PS2, 0, ID_MATFX,
+ matfxOpen, matfxClose);
+}
+
+ObjPipeline*
+makeMatFXPipeline(void)
+{
+ MatPipeline *pipe = MatPipeline::create();
+ 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;
+ uint32 vertCount = MatPipeline::getVertCount(0x3C5, 4, 3, 3);
+ pipe->setTriBufferSizes(4, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->uninstanceCB = genericUninstanceCB;
+
+ ObjPipeline *opipe = ObjPipeline::create();
+ opipe->pluginID = ID_MATFX;
+ opipe->pluginData = 0;
+ opipe->groupPipeline = pipe;
+ return opipe;
+}
+
+}
+}
diff --git a/src/ps2/ps2raster.cpp b/src/ps2/ps2raster.cpp
new file mode 100644
index 0000000..db266de
--- /dev/null
+++ b/src/ps2/ps2raster.cpp
@@ -0,0 +1,2238 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "rwps2.h"
+
+#define PLUGIN_ID ID_DRIVER
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+namespace rw {
+namespace ps2 {
+
+int32 nativeRasterOffset;
+
+#define MAXLEVEL(r) ((r)->tex1low >> 2)
+static bool32 noNewStyleRasters;
+
+enum Psm {
+ PSMCT32 = 0x0,
+ PSMCT24 = 0x1,
+ PSMCT16 = 0x2,
+ PSMCT16S = 0xA,
+ PSMT8 = 0x13,
+ PSMT4 = 0x14,
+ PSMT8H = 0x1B,
+ PSMT4HL = 0x24,
+ PSMT4HH = 0x2C,
+ PSMZ32 = 0x30,
+ PSMZ24 = 0x31,
+ PSMZ16 = 0x32,
+ PSMZ16S = 0x3A
+};
+
+// i don't really understand this, stolen from RW
+static void
+transferMinSize(int32 psm, int32 flags, int32 *minw, int32 *minh)
+{
+ *minh = 1;
+ switch(psm){
+ case PSMCT32:
+ case PSMZ32:
+ *minw = 2; // 32 bit
+ break;
+ case PSMCT16:
+ case PSMCT16S:
+ case PSMZ16:
+ case PSMZ16S:
+ *minw = 4; // 16 bit
+ break;
+ case PSMCT24:
+ case PSMT8:
+ case PSMT4:
+ case PSMT8H:
+ case PSMT4HL:
+ case PSMT4HH:
+ case PSMZ24:
+ *minw = 8; // everything else
+ break;
+ }
+ if(flags & 0x2 && psm == PSMT8){
+ *minw = 16;
+ *minh = 4;
+ }
+ if(flags & 0x4 && psm == PSMT4){
+ *minw = 32;
+ *minh = 4;
+ }
+}
+
+#define ALIGN(x,a) ((x) + (a)-1 & ~((a)-1))
+#define ALIGN16(x) ((x) + 0xF & ~0xF)
+#define ALIGN64(x) ((x) + 0x3F & ~0x3F)
+#define NSIZE(dim,pagedim) (((dim) + (pagedim)-1)/(pagedim))
+
+void*
+mallocalign(size_t size, int32 alignment)
+{
+ void *p;
+ void **pp;
+ p = rwMalloc(size + alignment + sizeof(void*), MEMDUR_EVENT | ID_RASTERPS2);
+ if(p == nil) return nil;
+ pp = (void**)(((uintptr)p + sizeof(void*) + alignment)&~(alignment-1));
+ pp[-1] = p;
+ return (void*)pp;
+}
+
+void
+freealign(void *p)
+{
+ void *pp;
+ if(p == nil) return;
+ pp = ((void**)p)[-1];
+ rwFree(pp);
+}
+
+// TODO: these depend on video mode, set in deviceSystem!
+int32 cameraFormat = Raster::C8888;
+int32 cameraDepth = 32;
+int32 cameraZDepth = 16;
+
+int32 defaultMipMapKL = 0xFC0;
+int32 maxMipLevels = 7;
+
+int32
+getRasterFormat(Raster *raster)
+{
+ int32 palformat, pixelformat, mipmapflags;
+ pixelformat = raster->format & 0xF00;
+ palformat = raster->format & 0x6000;
+ mipmapflags = raster->format & 0x9000;
+ switch(raster->type){
+ case Raster::ZBUFFER:
+ if(palformat || mipmapflags){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ if(raster->depth && raster->depth != cameraZDepth){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = cameraZDepth;
+ if(pixelformat){
+ if((raster->depth == 16 && pixelformat != Raster::D16) ||
+ (raster->depth == 32 && pixelformat != Raster::D32)){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ }
+ pixelformat = raster->depth == 16 ? Raster::D16 : Raster::D32;
+ raster->format = pixelformat;
+ break;
+ case Raster::CAMERA:
+ if(palformat || mipmapflags){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ if(raster->depth && raster->depth != cameraDepth){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = cameraDepth;
+ if(pixelformat && pixelformat != cameraFormat){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ pixelformat = cameraFormat;
+ raster->format = pixelformat;
+ break;
+ case Raster::NORMAL:
+ case Raster::CAMERATEXTURE:
+ if(palformat || mipmapflags){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ /* fallthrough */
+ case Raster::TEXTURE:
+ // Find raster format by depth if none was given
+ if(pixelformat == 0)
+ switch(raster->depth){
+ case 4:
+ pixelformat = Raster::C1555;
+ palformat = Raster::PAL4;
+ break;
+ case 8:
+ pixelformat = Raster::C1555;
+ palformat = Raster::PAL8;
+ break;
+ case 24:
+ // unsafe
+ // pixelformat = Raster::C888;
+ // palformat = 0;
+ // break;
+ case 32:
+ pixelformat = Raster::C8888;
+ palformat = 0;
+ break;
+ default:
+ pixelformat = Raster::C1555;
+ palformat = 0;
+ break;
+ }
+ raster->format = pixelformat | palformat | mipmapflags;
+ // Sanity check raster format and depth; set depth if none given
+ if(palformat){
+ if(palformat == Raster::PAL8){
+ if(raster->depth && raster->depth != 8){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = 8;
+ if(pixelformat != Raster::C1555 && pixelformat != Raster::C8888){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ }else if(palformat == Raster::PAL4){
+ if(raster->depth && raster->depth != 4){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = 4;
+ if(pixelformat != Raster::C1555 && pixelformat != Raster::C8888){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ }else{
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ }else if(pixelformat == Raster::C1555){
+ if(raster->depth && raster->depth != 16){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = 16;
+ }else if(pixelformat == Raster::C8888){
+ if(raster->depth && raster->depth != 32){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = 32;
+ }else if(pixelformat == Raster::C888){
+ assert(0 && "24 bit rasters not supported");
+ if(raster->depth && raster->depth != 24){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = 24;
+ }else{
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ break;
+ default:
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Memory units:
+ * Column: 64 bytes (single cycle access)
+ * Block: 256 bytes, 64 words, 4 columns. texture base pointers
+ * Page: 8 kbytes, 2 kwords, 128 columns, 32 blocks. frame buffer base pointers
+ * entire memory: 4 mbytes, 64k columns, 16k blocks, 512 pages
+ *
+ * PSMT4: 128x128 pixels, 4x8 blocks per page, 32x16 pixels per block
+ * PSMT8: 128x64 pixels, 8x4 blocks per page, 16x16 pixels per block
+ * PSMCT16(S): 64x64 pixels, 4x8 blocks per page, 16x8 pixels per block
+ * PSMCT24: 64x32 pixels, 8x4 blocks per page, 8x8 pixels per block
+ * PSMCT32: 64x32 pixels, 8x4 blocks per page, 8x8 pixels per block
+ *
+ * Layout of blocks in page:
+ *
+ * PSMCT24, PSMCT32, PSMT8
+ * +----+----+----+----+----+----+----+----+
+ * | 0 | 1 | 4 | 5 | 16 | 17 | 20 | 21 |
+ * +----+----+----+----+----+----+----+----+
+ * | 2 | 3 | 6 | 7 | 18 | 19 | 22 | 23 |
+ * +----+----+----+----+----+----+----+----+
+ * | 8 | 9 | 12 | 13 | 24 | 25 | 28 | 29 |
+ * +----+----+----+----+----+----+----+----+
+ * | 10 | 11 | 14 | 15 | 26 | 27 | 30 | 31 |
+ * +----+----+----+----+----+----+----+----+
+ *
+ * PSMCT16, PSMT4
+ * +----+----+----+----+
+ * | 0 | 2 | 8 | 10 |
+ * +----+----+----+----+
+ * | 1 | 3 | 9 | 11 |
+ * +----+----+----+----+
+ * | 4 | 6 | 12 | 14 |
+ * +----+----+----+----+
+ * | 5 | 7 | 13 | 15 |
+ * +----+----+----+----+
+ * | 16 | 18 | 24 | 26 |
+ * +----+----+----+----+
+ * | 17 | 19 | 25 | 27 |
+ * +----+----+----+----+
+ * | 20 | 22 | 28 | 30 |
+ * +----+----+----+----+
+ * | 21 | 23 | 29 | 31 |
+ * +----+----+----+----+
+ *
+ * PSMCT16S
+ * +----+----+----+----+
+ * | 0 | 2 | 16 | 18 |
+ * +----+----+----+----+
+ * | 1 | 3 | 17 | 19 |
+ * +----+----+----+----+
+ * | 8 | 10 | 24 | 26 |
+ * +----+----+----+----+
+ * | 9 | 11 | 25 | 27 |
+ * +----+----+----+----+
+ * | 4 | 6 | 20 | 22 |
+ * +----+----+----+----+
+ * | 5 | 7 | 21 | 23 |
+ * +----+----+----+----+
+ * | 12 | 14 | 28 | 30 |
+ * +----+----+----+----+
+ * | 13 | 15 | 29 | 31 |
+ * +----+----+----+----+
+ *
+ */
+
+static uint8 blockmap_PSMCT32[32] = {
+ 0, 1, 4, 5, 16, 17, 20, 21,
+ 2, 3, 6, 7, 18, 19, 22, 23,
+ 8, 9, 12, 13, 24, 25, 28, 29,
+ 10, 11, 14, 15, 26, 27, 30, 31,
+};
+static uint8 blockmap_PSMCT16[32] = {
+ 0, 2, 8, 10,
+ 1, 3, 9, 11,
+ 4, 6, 12, 14,
+ 5, 7, 13, 15,
+ 16, 18, 24, 26,
+ 17, 19, 25, 27,
+ 20, 22, 28, 30,
+ 21, 23, 29, 31,
+};
+static uint8 blockmap_PSMCT16S[32] = {
+ 0, 2, 16, 18,
+ 1, 3, 17, 19,
+ 8, 10, 24, 26,
+ 9, 11, 25, 27,
+ 4, 6, 20, 22,
+ 5, 7, 21, 23,
+ 12, 14, 28, 30,
+ 13, 15, 29, 31,
+};
+static uint8 blockmap_PSMZ32[32] = {
+ 24, 25, 28, 29, 8, 9, 12, 13,
+ 26, 27, 30, 31, 10, 11, 14, 15,
+ 16, 17, 20, 21, 0, 1, 4, 5,
+ 18, 19, 22, 23, 2, 3, 6, 7,
+};
+static uint8 blockmap_PSMZ16[32] = {
+ 24, 26, 16, 18,
+ 25, 27, 17, 19,
+ 28, 30, 20, 22,
+ 29, 31, 21, 23,
+ 8, 10, 0, 2,
+ 9, 11, 1, 3,
+ 12, 14, 4, 6,
+ 13, 15, 5, 7,
+};
+static uint8 blockmap_PSMZ16S[32] = {
+ 24, 26, 8, 10,
+ 25, 27, 9, 11,
+ 16, 18, 0, 2,
+ 17, 19, 1, 3,
+ 28, 30, 12, 14,
+ 29, 31, 13, 15,
+ 20, 22, 4, 6,
+ 21, 23, 5, 7,
+};
+
+static uint8 blockmaprev_PSMCT32[32] = {
+ 0, 1, 8, 9, 2, 3, 10, 11,
+ 16, 17, 24, 25, 18, 19, 26, 27,
+ 4, 5, 12, 13, 6, 7, 14, 15,
+ 20, 21, 28, 29, 22, 23, 30, 31,
+};
+static uint8 blockmaprev_PSMCT16[32] = {
+ 0, 4, 1, 5,
+ 8, 12, 9, 13,
+ 2, 6, 3, 7,
+ 10, 14, 11, 15,
+ 16, 20, 17, 21,
+ 24, 28, 25, 29,
+ 18, 22, 19, 23,
+ 26, 30, 27, 31,
+};
+
+/* Suffixes used:
+ * _Px - pixels
+ * _W - width units (pixels/64)
+ * _B - blocks
+ * _P - pages
+ */
+
+/* Layout mipmaps and palette in GS memory */
+static void
+calcOffsets(int32 width_Px, int32 height_Px, int32 psm, uint64 *bufferBase_B, uint64 *bufferWidth_W, uint32 *trxpos, uint32 *totalSize, uint32 *paletteBase)
+{
+ uint32 pageWidth_Px, pageHeight_Px;
+ uint32 blockWidth_Px, blockHeight_Px;
+ uint32 mindim_Px;
+ int32 nlevels;
+ int32 n;
+ uint32 mipw_Px, miph_Px;
+ uint32 lastmipw_Px, lastmiph_Px;
+ uint32 bufferHeight_P[8];
+ uint32 bufferPage_B[8]; // address of page in which the level is allocated
+ uint32 xoff_Px, yoff_Px; // x/y offset the last level starts at
+ // Whenever we allocate horizontally inside a page,
+ // keep track of the region below it on this stack.
+ uint32 sp;
+ uint32 xoffstack_Px[8]; // actually unused...
+ uint32 widthstack_Px[8];
+ uint32 heightstack_Px[8];
+ uint32 basestack_B[8];
+ uint32 flag;
+
+ switch(psm){
+ case PSMCT32:
+ case PSMCT24:
+ case PSMT8H:
+ case PSMT4HL:
+ case PSMT4HH:
+ case PSMZ32:
+ case PSMZ24:
+ pageWidth_Px = 64;
+ pageHeight_Px = 32;
+ blockWidth_Px = 8;
+ blockHeight_Px = 8;
+ break;
+ case PSMT8:
+ pageWidth_Px = 128;
+ pageHeight_Px = 64;
+ blockWidth_Px = 16;
+ blockHeight_Px = 16;
+ break;
+ case PSMT4:
+ pageWidth_Px = 128;
+ pageHeight_Px = 128;
+ blockWidth_Px = 32;
+ blockHeight_Px = 16;
+ break;
+ case PSMCT16:
+ case PSMCT16S:
+ case PSMZ16:
+ case PSMZ16S:
+ default:
+ pageWidth_Px = 64;
+ pageHeight_Px = 64;
+ blockWidth_Px = 16;
+ blockHeight_Px = 8;
+ break;
+ }
+
+ mindim_Px = min(width_Px, height_Px);
+ for(nlevels = 1; mindim_Px > 8; nlevels++){
+ if(nlevels >= maxMipLevels)
+ break;
+ mindim_Px /= 2;
+ }
+
+#define PAGEWIDTH_B (pageWidth_Px/blockWidth_Px) // number of horizontal blocks per page
+#define NBLKX(dim) (NSIZE((dim), blockWidth_Px))
+#define NBLKY(dim) (NSIZE((dim), blockHeight_Px))
+#define NPGX(dim) (NSIZE((dim), pageWidth_Px))
+#define NPGY(dim) (NSIZE((dim), pageHeight_Px))
+#define REALWIDTH(w) (max((w), blockWidth_Px))
+#define REALHEIGHT(w) (max((w), blockHeight_Px))
+
+ bufferBase_B[0] = 0;
+ bufferWidth_W[0] = NPGX(width_Px)*pageWidth_Px/64;
+ bufferHeight_P[0] = NPGY(height_Px);
+ bufferPage_B[0] = 0;
+ lastmipw_Px = width_Px;
+ lastmiph_Px = height_Px;
+ sp = 0;
+ xoff_Px = 0;
+ yoff_Px = 0;
+ flag = 0;
+ // Calculate info for all mipmap levels.
+ // mipwidth/height are actually the dimensions of level n-1!
+ // This code was reversed from RW and is rather complicated...
+ // partially because it's not clear what the assumptions are,
+ // can width/height be non-powers of 2?
+ for(n = 1; n < nlevels; n++){
+ mipw_Px = lastmipw_Px/2;
+ miph_Px = lastmiph_Px/2;
+ if(lastmipw_Px >= pageWidth_Px){
+ if(lastmiph_Px >= pageHeight_Px){
+ // CASE 0
+ // We allocate full pages
+ // This is the only place bufferWidth can change. Similarly bufferBase_2, which is related
+ bufferBase_B[n] = bufferBase_B[n-1] + (lastmipw_Px/blockWidth_Px)*(lastmiph_Px/blockHeight_Px);
+ bufferPage_B[n] = bufferBase_B[n];
+ bufferWidth_W[n] = NPGX(mipw_Px)*pageWidth_Px/64;
+ bufferHeight_P[n] = NPGY(miph_Px);
+ xoff_Px = 0;
+ yoff_Px = 0;
+ }else{
+ // CASE 1
+ // Allocate vertically in the current page
+ bufferPage_B[n] = bufferPage_B[n-1];
+ bufferHeight_P[n] = bufferHeight_P[n-1];
+ bufferWidth_W[n] = bufferWidth_W[n-1];
+ // How do we know pageHeight - yoff - REALHEIGHT(lastmiph) >= miph?
+ // And how is this condition ever false?
+ // Assuming lastmipw >= pageWidth for any number of levels, lastmiph must be pageHeight/2
+ // or lower to reach this code. No dimension is lower than 8. Then consequent mipmaps
+ // will have heights halved but even with PSMT4 we will only (vertically) fill the
+ // page with the last mipmap and not go beyond...
+ if(REALHEIGHT(lastmiph_Px) + yoff_Px < pageHeight_Px){
+ // CASE 2
+ yoff_Px += REALHEIGHT(lastmiph_Px);
+ bufferBase_B[n] = bufferBase_B[n-1] +
+ PAGEWIDTH_B * NBLKY(lastmiph_Px) *
+ bufferWidth_W[n]*64/pageWidth_Px; // number of horizontal pages for level
+ }else{
+ // CASE 3
+ // Can this happen?
+ xoff_Px += REALWIDTH(lastmipw_Px);
+ bufferBase_B[n] = bufferBase_B[n-1] + NBLKX(lastmipw_Px);
+ }
+ }
+ }else if(lastmiph_Px >= pageHeight_Px){
+ // CASE 4
+ // Allocate horizontally
+ bufferPage_B[n] = bufferPage_B[n-1];
+ bufferHeight_P[n] = bufferHeight_P[n-1];
+ bufferWidth_W[n] = bufferWidth_W[n-1];
+ if(REALWIDTH(lastmipw_Px) + xoff_Px < pageWidth_Px){
+ // CASE 5
+ xoffstack_Px[sp] = xoff_Px; // unused...
+ heightstack_Px[sp] = REALHEIGHT(lastmiph_Px);
+ widthstack_Px[sp] = REALWIDTH(lastmipw_Px);
+ basestack_B[sp] = bufferBase_B[n-1] + NBLKY(lastmiph_Px) * PAGEWIDTH_B;
+ sp++;
+ xoff_Px += REALWIDTH(lastmipw_Px);
+ bufferBase_B[n] = bufferBase_B[n-1] + NBLKX(lastmipw_Px);
+ }else if(sp){
+ // CASE 7
+ bufferBase_B[n] = basestack_B[sp-1];
+ if(REALWIDTH(mipw_Px) < widthstack_Px[sp-1]){
+ // CASE 9
+ basestack_B[sp-1] += NBLKX(mipw_Px);
+ widthstack_Px[sp-1] -= REALWIDTH(mipw_Px);
+ }else if(REALHEIGHT(miph_Px) < heightstack_Px[sp-1]){
+ // CASE 8
+ basestack_B[sp-1] += NBLKY(miph_Px) * PAGEWIDTH_B;
+ heightstack_Px[sp-1] -= REALHEIGHT(miph_Px);
+ }else{
+ // CASE 10
+ sp--;
+ }
+ flag = 1;
+ }else{
+ // CASE 6
+ yoff_Px += REALHEIGHT(lastmiph_Px);
+ bufferBase_B[n] = bufferBase_B[n-1] + PAGEWIDTH_B*NBLKY(lastmiph_Px);
+ }
+ }else{
+ // CASE 11
+ bufferHeight_P[n] = bufferHeight_P[n-1];
+ bufferPage_B[n] = bufferPage_B[n-1];
+ bufferWidth_W[n] = bufferWidth_W[n-1];
+ if(REALWIDTH(lastmipw_Px) + xoff_Px < bufferWidth_W[n-1]*64){
+ // CASE 12
+ xoffstack_Px[sp] = xoff_Px; // unused...
+ widthstack_Px[sp] = REALWIDTH(lastmipw_Px);
+ heightstack_Px[sp] = REALHEIGHT(lastmiph_Px);
+ basestack_B[sp] = bufferBase_B[n-1] + PAGEWIDTH_B * NBLKY(lastmiph_Px);
+ sp++;
+ xoff_Px += REALWIDTH(lastmipw_Px);
+ bufferBase_B[n] = bufferBase_B[n-1] + NBLKX(lastmipw_Px);
+ }else if(REALHEIGHT(lastmiph_Px) + yoff_Px < pageHeight_Px*bufferHeight_P[n] && flag == 0){
+ // CASE 13
+ bufferBase_B[n] = bufferBase_B[n-1] + PAGEWIDTH_B * NBLKY(lastmiph_Px);
+ yoff_Px += blockHeight_Px ? lastmiph_Px : 0; // how exactly can blockHeight be 0?? This looks wrong...
+ flag = n;
+ }else{
+ // CASE 14
+ bufferBase_B[n] = basestack_B[sp-1];
+ if(REALWIDTH(mipw_Px) < widthstack_Px[sp-1]){
+ // CASE 15
+ basestack_B[sp-1] += NBLKX(mipw_Px);
+ widthstack_Px[sp-1] -= REALWIDTH(mipw_Px);
+ }else if(REALHEIGHT(miph_Px) < heightstack_Px[sp-1]){
+ // CASE 16
+ basestack_B[sp-1] += PAGEWIDTH_B * NBLKY(miph_Px);
+ heightstack_Px[sp-1] -= REALHEIGHT(miph_Px);
+ }else{
+ // CASE 17
+ sp--;
+ }
+ }
+ }
+ lastmipw_Px = mipw_Px;
+ lastmiph_Px = miph_Px;
+ }
+
+ // Calculate position of palette.
+ uint32 paletteBase_B = 0;
+ uint64 bufwidth_Px = bufferWidth_W[nlevels-1]*64;
+ uint64 bufheight_Px = bufferHeight_P[nlevels-1]*pageHeight_Px;
+ // != means > really
+ if(bufwidth_Px != lastmipw_Px || bufheight_Px != lastmiph_Px){
+ if(psm == PSMT8){
+ // 2x2 blocks at the end of the page (even for PSMCT16S)
+ paletteBase_B = bufferPage_B[nlevels-1] +
+ ((bufwidth_Px/pageWidth_Px)*bufferHeight_P[nlevels-1] << 5) // total number of blocks
+ - (bufheight_Px/pageWidth_Px) * PAGEWIDTH_B // one block up
+ - 2; // two blocks left
+ }else if(psm == PSMT4){
+ // One block at the end of the page
+ paletteBase_B = bufferPage_B[nlevels-1] +
+ ((bufwidth_Px/pageWidth_Px) * bufferHeight_P[nlevels-1] << 5)
+ - 1;
+ }
+ }else{
+ if(psm == PSMT8 || psm == PSMT4){
+ paletteBase_B = bufferPage_B[nlevels-1] +
+ (bufwidth_Px/blockWidth_Px) * (bufheight_Px/blockHeight_Px);
+ }
+ }
+
+ uint32 bufwidth_W = bufferWidth_W[0];
+ uint32 bufpage_B = bufferPage_B[0];
+ uint32 pixeloff;
+ for(n = 0; n < nlevels; n++){
+ // Calculate TRXPOS register (DSAX and DSAY, shifted up later)
+ // Start of buffer on current page (x in pixels, y in blocks)
+ pixeloff = (bufferBase_B[n] - bufpage_B) * blockWidth_Px;
+ // y coordinate of first pixel
+ yoff_Px = (pixeloff / (bufwidth_W*64)) * blockHeight_Px;
+ // x coordinate of first pixel
+ xoff_Px = pixeloff % (bufwidth_W*64);
+ if(bufferWidth_W[n] == bufwidth_W &&
+ // Not quite sure what's the meaning of this.
+ // DSAY is 11 bits, but so is DSAX and it is not checked?
+ yoff_Px < 0x800){
+ trxpos[n] = yoff_Px<<16 | xoff_Px;
+ }else{
+ bufwidth_W = bufferWidth_W[n];
+ bufpage_B = bufferPage_B[n];
+ trxpos[n] = 0;
+ }
+
+ // If using more than one page we have to swizzle rows inside page rows
+ if(bufwidth_W*64 / pageWidth_Px > 1){
+ uint32 bufpagestride_B = bufwidth_W*64 * 32 / pageWidth_Px; // one row of pages
+ uint32 bufwidth_B = bufwidth_W*64 / blockWidth_Px; // one row of blocks
+ // To illustrate assume:
+ // - 8x4 block pages
+ // - texture is 4 pages wide
+ // Then the lower bits of an input block address look like: RRRPPCC
+ // where the C bits are the block's column inside a page
+ // the P bits are the block's page horizontally
+ // the R bits are the block's row in a row of pages
+ // We want to swap P and R: PPRRRCC
+ bufferBase_B[n] =
+ (bufferBase_B[n] & ~((uint64)bufpagestride_B - PAGEWIDTH_B)) // mask out R and P
+ | ((bufferBase_B[n] & (bufwidth_B - PAGEWIDTH_B)) * (bufpagestride_B/bufwidth_B)) // extract P and shift left
+ | ((bufferBase_B[n] & (bufpagestride_B - bufwidth_B)) / (bufwidth_B/PAGEWIDTH_B)); // extract R and shift right
+ }
+
+ // Always have to swizzle blocks inside pages. We use a lookup, RW does bit operations
+ switch(psm){
+ case PSMCT32:
+ case PSMCT24:
+ case PSMT8:
+ case PSMT8H:
+ case PSMT4HL:
+ case PSMT4HH:
+ // ABCDE -> CADBE
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMCT32[bufferBase_B[n]&0x1F];
+ break;
+ case PSMT4:
+ case PSMCT16:
+ // ABCDE -> ADBEC
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMCT16[bufferBase_B[n]&0x1F];
+ break;
+ case PSMCT16S:
+ // ABCDE -> DBAEC
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMCT16S[bufferBase_B[n]&0x1F];
+ break;
+ case PSMZ32:
+ case PSMZ24:
+ // ABCDE -> ~C~ADBE
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMZ32[bufferBase_B[n]&0x1F];
+ break;
+ case PSMZ16:
+ // ABCDE -> ~A~DBEC
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMZ16[bufferBase_B[n]&0x1F];
+ break;
+ case PSMZ16S:
+ // ABCDE -> ~D~BAEC
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMZ16S[bufferBase_B[n]&0x1F];
+ break;
+ default: break;
+ }
+ }
+
+ // Same dance as above, with the palette
+ if(bufwidth_W*64 / pageWidth_Px > 1){
+ uint32 bufpagestride_B = bufwidth_W*64 * 32 / pageWidth_Px; // one row of pages
+ uint32 bufwidth_B = bufwidth_W*64 / blockWidth_Px; // one row of blocks
+ paletteBase_B =
+ (paletteBase_B & ~(bufpagestride_B - PAGEWIDTH_B)) // mask out R and P
+ | ((paletteBase_B & (bufwidth_B - PAGEWIDTH_B)) * (bufpagestride_B/bufwidth_B)) // extract P and shift left
+ | ((paletteBase_B & (bufpagestride_B - bufwidth_B)) / (bufwidth_B/PAGEWIDTH_B)); // extract R and shift right
+ }
+ switch(psm){
+ case PSMCT32:
+ case PSMCT24:
+ case PSMT8:
+ case PSMT8H:
+ case PSMT4HL:
+ case PSMT4HH:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMCT32[paletteBase_B&0x1F];
+ break;
+ case PSMT4:
+ case PSMCT16:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMCT16[paletteBase_B&0x1F];
+ break;
+ case PSMCT16S:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMCT16S[paletteBase_B&0x1F];
+ break;
+ case PSMZ32:
+ case PSMZ24:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMZ32[paletteBase_B&0x1F];
+ break;
+ case PSMZ16:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMZ16[paletteBase_B&0x1F];
+ break;
+ case PSMZ16S:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMZ16S[paletteBase_B&0x1F];
+ break;
+ default: break;
+ }
+ *paletteBase = paletteBase_B;
+ *totalSize = bufferPage_B[nlevels-1] + // start of last buffer`
+ bufferWidth_W[nlevels-1]*64/blockWidth_Px * // number of horizontal blocks in last level
+ pageHeight_Px*bufferHeight_P[nlevels-1]/blockHeight_Px; // number of vertical blocks in last level
+ *totalSize *= 64; // to words
+
+#undef BLKSTRIDE
+#undef NBLKX
+#undef NBLKY
+#undef NPGX
+#undef NPGY
+#undef REALWIDTH
+#undef REALHEIGHT
+}
+
+static Raster*
+rasterCreateTexture(Raster *raster)
+{
+ // We use a map for fast lookup, even for impossible depths
+ static int32 pageWidths[32] = {
+ 128, 128, 128, 128,
+ 128, 128, 128, 128,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ };
+ static int32 pageHeights[32] = {
+ 128, 128, 128, 128,
+ 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ };
+ enum {
+ TCC_RGBA = 1 << 2,
+ CLD_1 = 1 << 29,
+
+ WD2BLK = 64, // words per block
+ WD2PG = 2048, // words per page
+ };
+ int32 pixelformat, palformat;
+ // TEX0 fields (not all)
+ int64 tbw = 0; // texture buffer width, texels/64
+ int64 psm = 0; // pixel storage mode
+ int64 tw = 0; // texture width exponent, width = 2^tw
+ int64 th = 0; // texture height exponent, height = 2^th
+ int64 tcc = 0; // texture color component, 0 = rgb, 1 = rgba
+ int64 cpsm = 0; // CLUT pixel storage mode
+ int64 cld = 0; // CLUT buffer load control
+
+ uint64 bufferWidth[7]; // in number of pixels / 64
+ uint64 bufferBase[7]; // block address
+ uint32 trxpos_hi[8];
+ int32 width, height, depth;
+ int32 pageWidth, pageHeight;
+ int32 paletteWidth, paletteHeight, paletteDepth;
+ int32 palettePagewidth, palettePageheight;
+
+
+ Ps2Raster *ras = GETPS2RASTEREXT(raster);
+ pixelformat = raster->format & 0xF00;
+ palformat = raster->format & 0x6000;
+ width = raster->width;
+ height = raster->height;
+ depth = raster->depth;
+ // RW's code does not seem to quite work with 24 bit rasters
+ // so make sure we're not generating them for safety
+ assert(depth != 24);
+
+ ras->flags = 0;
+ ras->data = nil;
+ ras->dataSize = 0;
+
+ // RW doesn't seem to check this, hm...
+ if(raster->flags & Raster::DONTALLOCATE)
+ return raster;
+
+ //printf("%x %x %x %x\n", raster->format, raster->flags, raster->type, noNewStyleRasters);
+ pageWidth = pageWidths[depth-1];
+ pageHeight = pageHeights[depth-1];
+
+ int32 s;
+ tw = 0;
+ for(s = 1; s < width; s *= 2)
+ tw++;
+ th = 0;
+ for(s = 1; s < height; s *= 2)
+ th++;
+ ras->kl = defaultMipMapKL;
+ // unk2[0] = 1
+ //printf("%d %d %d %d\n", raster->width, logw, raster->height, logh);
+
+ // round up to page width, set TBW, TW, TH
+ tbw = max(width,pageWidth)/64;
+
+ // set PSM, TCC, CLD, CPSM and figure out palette format
+ if(palformat){
+ if(palformat == Raster::PAL8){
+ psm = PSMT8;
+ paletteWidth = 16;
+ paletteHeight = 16;
+ }else if(palformat == Raster::PAL4){
+ psm = PSMT4;
+ paletteWidth = 8;
+ paletteHeight = 2;
+ }else{
+ // can't happen, sanity check in getRasterFormat
+ return nil;;
+ }
+ tcc = 1; // RGBA
+ cld = 1;
+ if(pixelformat == Raster::C1555){
+ paletteDepth = 2;
+ cpsm = PSMCT16S;
+ palettePagewidth = 64;
+ palettePageheight = 64;
+ }else if(pixelformat == Raster::C8888){
+ paletteDepth = 4;
+ cpsm = PSMCT32;
+ palettePagewidth = 64;
+ palettePageheight = 32;
+ }else
+ // can't happen, sanity check in getRasterFormat
+ return nil;;
+ }else{
+ paletteWidth = 0;
+ paletteHeight = 0;
+ paletteDepth = 0;
+ palettePagewidth = 0;
+ palettePageheight = 0;
+ if(pixelformat == Raster::C8888){
+ psm = PSMCT32;
+ tcc = 1; // RGBA
+ }else if(pixelformat == Raster::C888){
+ psm = PSMCT24;
+ tcc = 0; // RGB
+ }else if(pixelformat == Raster::C1555){
+ psm = PSMCT16S;
+ tcc = 1; // RGBA
+ }else
+ // can't happen, sanity check in getRasterFormat
+ return nil;;
+ }
+
+ for(int i = 0; i < 7; i++){
+ bufferWidth[i] = 1;
+ bufferBase[i] = 0;
+ }
+
+ int32 mipw, miph;
+ int32 w, h;
+ int32 n;
+ int32 nPagW, nPagH;
+ raster->stride = width*depth/8;
+
+ if(raster->format & Raster::MIPMAP){
+ // NOTE: much of this code seems to be totally useless.
+ // calcOffsets overwrites what we calculate here. I wonder
+ // why this code even is in RW. Maybe it's older code that used
+ // the GS' automatic base pointer calculation?
+
+ // see the left columns in the maps above
+ static uint32 blockOffset32_24_8[8] = { 0, 2, 2, 8, 8, 10, 10, 32 };
+ static uint32 blockOffset16_4[8] = { 0, 1, 4, 5, 16, 17, 20, 21 };
+ static uint32 blockOffset16S[8] = { 0, 1, 8, 9, 4, 5, 12, 13 };
+ uint64 lastBufferWidth;
+ mipw = width;
+ miph = height;
+ lastBufferWidth = max(pageWidth, width)/64;
+ ras->pixelSize = 0;
+ int32 lastaddress = 0; // word address
+ int32 nextaddress = 0; // word address
+ int32 stride; // in bytes
+ for(n = 0; mipw != 0 && miph != 0 && n < maxMipLevels; n++){
+ if(width >= 8 && height >= 8 && (mipw < 8 || miph < 8))
+ break;
+ ras->pixelSize += ALIGN64(mipw*miph*depth/8);
+ bufferWidth[n] = max(pageWidth, mipw)/64;
+ stride = bufferWidth[n]*64*depth/8;
+
+ // If buffer width changes, align next address to page
+ if(bufferWidth[n] != lastBufferWidth){
+ nPagW = ((width >> (n-1)) + pageWidth-1)/pageWidth;
+ nPagH = ((height >> (n-1)) + pageHeight-1)/pageHeight;
+ nextaddress = (lastaddress + nPagW*nPagH*WD2PG) & ~(WD2PG-1);
+ }
+ lastBufferWidth = bufferWidth[n];
+ nextaddress = ALIGN64(nextaddress); // this should already be the case...
+ uint32 b = nextaddress>>(11-3) & 7; // upper three bits of block-in-page address
+ switch(psm){
+ case PSMCT32:
+ case PSMCT24:
+ case PSMT8:
+ b = blockOffset32_24_8[b];
+ break;
+ case PSMCT16:
+ case PSMT4:
+ b = blockOffset16_4[b];
+ break;
+ case PSMCT16S:
+ b = blockOffset16S[b];
+ break;
+ default:
+ // can't happen
+ break;
+ }
+ // shift to page address, then to block address and add offset inside page
+ bufferBase[n] = b + (nextaddress>>11 << 5);
+
+ lastaddress = nextaddress;
+ nextaddress = ALIGN64(miph*stride/4 + lastaddress);
+
+ mipw /= 2;
+ miph /= 2;
+ }
+
+ // Do the real work here
+ uint32 paletteBase;
+ uint32 totalSize;
+ calcOffsets(width, height, psm, bufferBase, bufferWidth, trxpos_hi, &totalSize, &paletteBase);
+
+ ras->paletteSize = paletteWidth*paletteHeight*paletteDepth;
+ ras->miptbp1 =
+ bufferWidth[1]<<14 | (bufferBase[1] & 0x3FFF)<<0
+ | bufferWidth[2]<<34 | (bufferBase[2] & 0x3FFF)<<20
+ | bufferWidth[3]<<54 | (bufferBase[3] & 0x3FFF)<<40;
+ ras->miptbp2 =
+ bufferWidth[4]<<14 | (bufferBase[4] & 0x3FFF)<<0
+ | bufferWidth[5]<<34 | (bufferBase[5] & 0x3FFF)<<20
+ | bufferWidth[6]<<54 | (bufferBase[6] & 0x3FFF)<<40;
+ ras->tex1low = (n-1)<<2;
+ ras->totalSize = totalSize;
+ if(ras->paletteSize){
+ ras->paletteBase = paletteBase;
+ if(ras->paletteBase*64 == ras->totalSize)
+ ras->totalSize += WD2PG;
+ }else
+ ras->paletteBase = 0;
+ }else{
+ // No mipmaps
+
+ ras->pixelSize = ALIGN16(raster->stride*raster->height);
+ ras->paletteSize = paletteWidth*paletteHeight*paletteDepth;
+ ras->miptbp1 = 1ULL<<54 | 1ULL<<34 | 1ULL<<14;
+ ras->miptbp2 = 1ULL<<54 | 1ULL<<34 | 1ULL<<14;
+ ras->tex1low = 0; // one mipmap level
+
+ // find out number of pages needed
+ nPagW = (width + pageWidth-1)/pageWidth;
+ nPagH = (height + pageHeight-1)/pageHeight;
+
+ // calculate buffer width in units of pixels/64
+ bufferBase[0] = 0;
+ trxpos_hi[0] = 0;
+ bufferWidth[0] = nPagW*pageWidth / 64;
+
+ // calculate whole buffer size in words
+ ras->totalSize = nPagW*nPagH*WD2PG;
+
+ // calculate palette offset on GS in units of words/64
+ if(ras->paletteSize){
+ // Maximum palette size is 256 words.
+ // If there is still room, use it!
+ // If dimensions don't fill a page, we have at least
+ // half a page left, enough for any palette
+//TODO: this was not always done it seems but even gta3's 3.1 seems to??
+ if(pageWidth*nPagW > width ||
+ pageHeight*nPagH > height){
+ ras->paletteBase = (ras->totalSize - 256) / WD2BLK;
+ }else{
+ // Otherwise allocate more space...
+ ras->paletteBase = ras->totalSize / WD2BLK;
+ // ...using the same calculation as above.
+ // WHY? we never need more than one page!
+ nPagW = (paletteWidth + palettePagewidth-1)/palettePagewidth;
+ nPagH = (paletteHeight + palettePageheight-1)/palettePageheight;
+ ras->totalSize += nPagW*nPagH*WD2PG;
+ }
+ }else
+ ras->paletteBase = 0;
+ }
+ ras->tex0 = tbw << 14 |
+ psm << 20 |
+ tw << 26 |
+ th << 30 |
+ tcc << 34 |
+ cpsm << 51 |
+ 0ULL << 55 | // csm0
+ 0ULL << 56 | // entry offset
+ cld << 61;
+
+
+
+ // allocate data and fill with GIF packets
+ ras->pixelSize = ALIGN16(ras->pixelSize);
+ int32 numLevels = MAXLEVEL(ras)+1;
+ // No GIF packet because we either don't want it (pre 0x310 rasters)
+ // or the data wouldn't fit into a DMA packet
+ if(noNewStyleRasters ||
+ (raster->width*raster->height*raster->depth/8/0x10) >= 0x7FFF){
+ ras->dataSize = ras->paletteSize+ras->pixelSize;
+ uint8 *data = (uint8*)mallocalign(ras->dataSize, 0x40);
+ assert(data);
+ ras->data = data;
+ raster->pixels = data;
+ if(ras->paletteSize)
+ raster->palette = data + ras->pixelSize;
+ if(raster->depth == 8)
+ ras->flags |= Ps2Raster::SWIZZLED8;
+ }else{
+ ras->flags |= Ps2Raster::NEWSTYLE;
+ uint64 paltrxpos = 0;
+ uint32 dsax = trxpos_hi[numLevels-1] & 0x7FF;
+ uint32 dsay = trxpos_hi[numLevels-1]>>16 & 0x7FF;
+ // Set swizzle flags and calculate TRXPOS for palette
+ if(psm == PSMT8){
+ ras->flags |= Ps2Raster::SWIZZLED8;
+ if(cpsm == PSMCT32 && bufferWidth[numLevels-1] == 2){ // one page
+ // unswizzle the starting block of the last buffer and palette
+ uint32 bufbase_B = (bufferBase[numLevels-1]&~0x1F) | (uint64)blockmaprev_PSMCT32[bufferBase[numLevels-1]&0x1F];
+ uint32 palbase_B = (ras->paletteBase&~0x1F) | (uint64)blockmaprev_PSMCT32[ras->paletteBase&0x1F];
+ // find start of page of last level (16,16 are PSMT8 block dimensions)
+ uint32 page_B = bufbase_B - 8*(dsay/16) - dsax/16;
+ // find palette DSAX/Y (in PSMCT32!)
+ dsay = (palbase_B - page_B)/8 * 8; // block/blocksPerPageX * blockHeight
+ dsax = (palbase_B - page_B)*8 % 64; // block*blockWidth % pageWidth
+ if(dsay < 0x800)
+ paltrxpos = dsay<<16 | dsax;
+ }
+ }
+ if(psm == PSMT4){
+ // swizzle flag depends on version :/
+ // but which version? ....
+ if(rw::version > 0x31000){
+ ras->flags |= Ps2Raster::SWIZZLED4;
+ // Where can this come from? if anything we're using PSMCT16S
+ // Looks like they wanted to swizzle palettes too...
+ if(cpsm == PSMCT16){
+ // unswizzle the starting block of the last buffer and palette
+ uint32 bufbase_B = (bufferBase[numLevels-1]&~0x1F) | (uint64)blockmaprev_PSMCT16[bufferBase[numLevels-1]&0x1F];
+ uint32 palbase_B = (ras->paletteBase&~0x1F) | (uint64)blockmaprev_PSMCT16[ras->paletteBase&0x1F];
+ // find start of page of last level (32,16 are PSMT4 block dimensions)
+ uint32 page_B = bufbase_B - 4*(dsay/32) - dsax/16;
+ // find palette DSAX/Y (in PSMCT16!)
+ dsay = (palbase_B - page_B)/4 * 8; // block/blocksPerPageX * blockHeight
+ dsax = (palbase_B - page_B)*16 % 128; // block*blockWidth % pageWidth
+ if(dsay < 0x800)
+ paltrxpos = dsay<<16 | dsax;
+ }
+ }
+ }
+ ras->pixelSize = 0x50*numLevels; // GIF packets
+ int32 minW, minH;
+ transferMinSize(psm, ras->flags, &minW, &minH);
+ w = raster->width;
+ h = raster->height;
+ n = numLevels;
+ while(n--){
+ mipw = max(w, minW);
+ miph = max(h, minH);
+ ras->pixelSize += ALIGN16(mipw*miph*raster->depth/8);
+ w /= 2;
+ h /= 2;
+ }
+ if(ras->paletteSize){
+ if(rw::version > 0x31000 && paletteHeight == 2)
+ paletteHeight = 3;
+ ras->paletteSize = 0x50 +
+ paletteDepth*paletteWidth*paletteHeight;
+ }
+ // One transfer per buffer width, 4 qwords:
+ // DMAcnt(2) [NOP, DIRECT]
+ // GIF tag A+D
+ // BITBLTBUF
+ // DMAref(pixel data) [NOP, DIRECT]
+ uint32 extrasize = 0x10; // PixelPtr
+ int32 numTransfers = 0;
+ for(n = 0; n < numLevels; n++)
+ if(trxpos_hi[n] == 0){
+ extrasize += 0x40;
+ numTransfers++;
+ }
+ if(ras->paletteSize){
+ extrasize += 0x40;
+ numTransfers++;
+ }
+ // What happens here?
+ if(ras->paletteSize && paltrxpos == 0)
+ ras->dataSize = ALIGN(ras->pixelSize,128) + ALIGN(ras->paletteSize,64) + extrasize + 0x70;
+ else
+ ras->dataSize = ALIGN(ras->paletteSize+ras->pixelSize,64) + extrasize + 0x70;
+ uint8 *data = (uint8*)mallocalign(ras->dataSize, 0x40);
+ uint32 *xferchain = (uint32*)(data + 0x10);
+ assert(data);
+ ras->data = data;
+ Ps2Raster::PixelPtr *pp = (Ps2Raster::PixelPtr*)data;
+ pp->numTransfers = numTransfers;
+ pp->numTotalTransfers = numTransfers;
+ pp->pixels = (uint8*)ALIGN((uintptr)data + extrasize, 128);
+ raster->pixels = (uint8*)pp;
+ if(ras->paletteSize)
+ raster->palette = pp->pixels + ALIGN(ras->pixelSize, 128) + 0x50;
+ uint32 *p = (uint32*)pp->pixels;
+ w = raster->width;
+ h = raster->height;
+ for(n = 0; n < numLevels; n++){
+ mipw = max(w, minW);
+ miph = max(h, minH);
+
+ // GIF tag
+ *p++ = 3; // NLOOP = 3
+ *p++ = 0x10000000; // NREG = 1
+ *p++ = 0xE; // A+D
+ *p++ = 0;
+
+ // TRXPOS
+ if((ras->flags & Ps2Raster::SWIZZLED8 && psm == PSMT8) ||
+ (ras->flags & Ps2Raster::SWIZZLED4 && psm == PSMT4)){
+ *p++ = 0; // SSAX/Y is always 0
+ *p++ = (trxpos_hi[n] & ~0x10001)/2; // divide both DSAX/Y by 2
+ }else{
+ *p++ = 0;
+ *p++ = trxpos_hi[n];
+ }
+ *p++ = 0x51;
+ *p++ = 0;
+
+ // TRXREG
+ if((ras->flags & Ps2Raster::SWIZZLED8 && psm == PSMT8) ||
+ (ras->flags & Ps2Raster::SWIZZLED4 && psm == PSMT4)){
+ *p++ = mipw/2;
+ *p++ = miph/2;
+ }else{
+ *p++ = mipw;
+ *p++ = miph;
+ }
+ *p++ = 0x52;
+ *p++ = 0;
+
+ // TRXDIR
+ *p++ = 0; // host -> local
+ *p++ = 0;
+ *p++ = 0x53;
+ *p++ = 0;
+
+ // GIF tag
+ uint32 sz = ALIGN16(mipw*miph*raster->depth/8)/16;
+ *p++ = sz & 0x7FFF;
+ *p++ = 0x08000000; // IMAGE
+ *p++ = 0;
+ *p++ = 0;
+
+ if(trxpos_hi[n] == 0){
+ // Add a transfer, see above for layout
+
+ *xferchain++ = 0x10000002; // DMAcnt, 2 qwords
+ *xferchain++ = 0;
+ *xferchain++ = 0; // VIF nop
+ *xferchain++ = 0x50000002; // VIF DIRECT 2 qwords
+
+ // GIF tag
+ *xferchain++ = 1; // NLOOP = 1
+ *xferchain++ = 0x10000000; // NREG = 1
+ *xferchain++ = 0xE; // A+D
+ *xferchain++ = 0;
+
+ // BITBLTBUF
+ if(ras->flags & Ps2Raster::SWIZZLED8 && psm == PSMT8){
+ // PSMT8 is swizzled to PSMCT32 and dimensions are halved
+ *xferchain++ = PSMCT32<<24 | bufferWidth[n]/2<<16; // src buffer
+ *xferchain++ = PSMCT32<<24 | bufferWidth[n]/2<<16 | bufferBase[n]; // dst buffer
+ }else if(ras->flags & Ps2Raster::SWIZZLED4 && psm == PSMT4){
+ // PSMT4 is swizzled to PSMCT16 and dimensions are halved
+ *xferchain++ = PSMCT16<<24 | bufferWidth[n]/2<<16; // src buffer
+ *xferchain++ = PSMCT16<<24 | bufferWidth[n]/2<<16 | bufferBase[n]; // dst buffer
+ }else{
+ *xferchain++ = psm<<24 | bufferWidth[n]<<16; // src buffer
+ *xferchain++ = psm<<24 | bufferWidth[n]<<16 | bufferBase[n]; // dst buffer
+ }
+ *xferchain++ = 0x50;
+ *xferchain++ = 0;
+
+ *xferchain++ = 0x30000000 | sz+5; // DMAref
+ // this obviously only works with 32 bit pointers, but it's only needed on the PS2 anyway
+ *xferchain++ = (uint32)(uintptr)p - 0x50;
+ *xferchain++ = 0; // VIF nop
+ *xferchain++ = 0x50000000 | sz+5; // VIF DIRECT
+ }else{
+ // Add to existing transfer
+ xferchain[-4] = 0x30000000 | (xferchain[-4]&0xFFFF) + sz+5; // last DMAref
+ xferchain[-1] = 0x50000000 | (xferchain[-1]&0xFFFF) + sz+5; // last DIRECT
+ }
+
+ p += sz*4;
+ w /= 2;
+ h /= 2;
+ }
+
+ if(ras->paletteSize){
+ // huh?
+ if(paltrxpos)
+ raster->palette = (uint8*)p + 0x50;
+ p = (uint32*)(raster->palette - 0x50);
+ // GIF tag
+ *p++ = 3; // NLOOP = 3
+ *p++ = 0x10000000; // NREG = 1
+ *p++ = 0xE; // A+D
+ *p++ = 0;
+
+ // TRXPOS
+ *(uint64*)p = (uint64)paltrxpos<<32;
+ p += 2;
+ *p++ = 0x51;
+ *p++ = 0;
+
+ // TRXREG
+ *p++ = paletteWidth;
+ *p++ = paletteHeight;
+ *p++ = 0x52;
+ *p++ = 0;
+
+ // TRXDIR
+ *p++ = 0; // host -> local
+ *p++ = 0;
+ *p++ = 0x53;
+ *p++ = 0;
+
+ // GIF tag
+ uint32 sz = ALIGN16(ras->paletteSize - 0x50)/16;
+ *p++ = sz & 0x7FFF;
+ *p++ = 0x08000000; // IMAGE
+ *p++ = 0;
+ *p++ = 0;
+
+ // Transfer
+ *xferchain++ = 0x10000002; // DMAcnt, 2 qwords
+ *xferchain++ = 0;
+ *xferchain++ = 0; // VIF nop
+ *xferchain++ = 0x50000002; // VIF DIRECT 2 qwords
+
+ // GIF tag
+ *xferchain++ = 1; // NLOOP = 1
+ *xferchain++ = 0x10000000; // NREG = 1
+ *xferchain++ = 0xE; // A+D
+ *xferchain++ = 0;
+
+ // BITBLTBUF
+ if(paltrxpos == 0){
+ *xferchain++ = cpsm<<24 | 1<<16; // src buffer
+ *xferchain++ = cpsm<<24 | 1<<16 | ras->paletteBase; // dst buffer
+ *xferchain++ = 0x50;
+ *xferchain++ = 0;
+ }else{
+ // copy last pixel bitbltbuf...if uploading palette separately it's still the same buffer
+ xferchain[0] = xferchain[-16];
+ xferchain[1] = xferchain[-15];
+ xferchain[2] = xferchain[-14];
+ xferchain[3] = xferchain[-13];
+ xferchain += 4;
+ // Add to last transfer
+ xferchain[-16] = 0x30000000 | (xferchain[-16]&0xFFFF) + sz+5; // last DMAref
+ xferchain[-13] = 0x50000000 | (xferchain[-13]&0xFFFF) + sz+5; // last DIRECT
+ pp->numTransfers--;
+ }
+
+ *xferchain++ = 0x30000000 | sz+5; // DMAref
+ // this obviously only works with 32 bit pointers, but it's only needed on the PS2 anyway
+ *xferchain++ = (uint32)(uintptr)p - 0x50;
+ *xferchain++ = 0; // VIF nop
+ *xferchain++ = 0x50000000 | sz+5; // VIF DIRECT
+ }
+ }
+ raster->originalPixels = raster->pixels;
+ raster->originalStride = raster->stride;
+ if(ras->flags & Ps2Raster::NEWSTYLE)
+ raster->pixels = ((Ps2Raster::PixelPtr*)raster->pixels)->pixels + 0x50;
+ return raster;
+}
+
+Raster*
+rasterCreate(Raster *raster)
+{
+ if(!getRasterFormat(raster))
+ return nil;
+
+ // init raster
+ raster->pixels = nil;
+ raster->palette = nil;
+ raster->originalWidth = raster->width;
+ raster->originalHeight = raster->height;
+ raster->originalPixels = raster->pixels;
+ if(raster->width == 0 || raster->height == 0){
+ raster->flags = Raster::DONTALLOCATE;
+ raster->stride = 0;
+ raster->originalStride = 0;
+ return raster;
+ }
+
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ return rasterCreateTexture(raster);
+ case Raster::ZBUFFER:
+ // TODO. only RW_PS2
+ // get info from video mode
+ raster->flags = Raster::DONTALLOCATE;
+ return raster;
+ case Raster::CAMERA:
+ // TODO. only RW_PS2
+ // get info from video mode
+ raster->flags = Raster::DONTALLOCATE;
+ return raster;
+ case Raster::CAMERATEXTURE:
+ // TODO. only RW_PS2
+ // check width/height and fall through to texture
+ return nil;
+ }
+ return nil;
+}
+
+static uint32
+swizzle(uint32 x, uint32 y, uint32 logw)
+{
+#define X(n) ((x>>(n))&1)
+#define Y(n) ((y>>(n))&1)
+
+ uint32 nx, ny, n;
+ x ^= (Y(1)^Y(2))<<2;
+ nx = (x&7) | ((x>>1)&~7);
+ ny = (y&1) | ((y>>1)&~1);
+ n = Y(1) | X(3)<<1;
+ return n | nx<<2 | ny<<(logw-1+2);
+}
+
+void
+unswizzleRaster(Raster *raster)
+{
+ uint8 tmpbuf[1024*4]; // 1024x4px, maximum possible width
+ uint32 mask;
+ int32 x, y, w, h;
+ int32 i;
+ int32 logw;
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+ uint8 *px;
+
+ if((raster->format & (Raster::PAL4|Raster::PAL8)) == 0)
+ return;
+
+ int minw, minh;
+ transferMinSize(raster->format & Raster::PAL4 ? PSMT4 : PSMT8, natras->flags, &minw, &minh);
+ w = max(raster->width, minw);
+ h = max(raster->height, minh);
+ px = raster->pixels;
+ logw = 0;
+ for(i = 1; i < w; i *= 2) logw++;
+ mask = (1<<(logw+2))-1;
+
+ if(raster->format & Raster::PAL4 && natras->flags & Ps2Raster::SWIZZLED4){
+ for(y = 0; y < h; y += 4){
+ memcpy(tmpbuf, &px[y<<(logw-1)], 2*w);
+ for(i = 0; i < 4; i++)
+ for(x = 0; x < w; x++){
+ uint32 a = ((y+i)<<logw)+x;
+ uint32 s = swizzle(x, y+i, logw)&mask;
+ uint8 c = s & 1 ? tmpbuf[s>>1] >> 4 : tmpbuf[s>>1] & 0xF;
+ px[a>>1] = a & 1 ? (px[a>>1]&0xF) | c<<4 : (px[a>>1]&0xF0) | c;
+ }
+ }
+ }else if(raster->format & Raster::PAL8 && natras->flags & Ps2Raster::SWIZZLED8){
+ for(y = 0; y < h; y += 4){
+ memcpy(tmpbuf, &px[y<<logw], 4*w);
+ for(i = 0; i < 4; i++)
+ for(x = 0; x < w; x++){
+ uint32 a = ((y+i)<<logw)+x;
+ uint32 s = swizzle(x, y+i, logw)&mask;
+ px[a] = tmpbuf[s];
+ }
+ }
+ }
+}
+
+void
+swizzleRaster(Raster *raster)
+{
+ uint8 tmpbuf[1024*4]; // 1024x4px, maximum possible width
+ uint32 mask;
+ int32 x, y, w, h;
+ int32 i;
+ int32 logw;
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+ uint8 *px;
+
+ if((raster->format & (Raster::PAL4|Raster::PAL8)) == 0)
+ return;
+
+ int minw, minh;
+ transferMinSize(raster->format & Raster::PAL4 ? PSMT4 : PSMT8, natras->flags, &minw, &minh);
+ w = max(raster->width, minw);
+ h = max(raster->height, minh);
+ px = raster->pixels;
+ logw = 0;
+ for(i = 1; i < raster->width; i *= 2) logw++;
+ mask = (1<<(logw+2))-1;
+
+ if(raster->format & Raster::PAL4 && natras->flags & Ps2Raster::SWIZZLED4){
+ for(y = 0; y < h; y += 4){
+ for(i = 0; i < 4; i++)
+ for(x = 0; x < w; x++){
+ uint32 a = ((y+i)<<logw)+x;
+ uint32 s = swizzle(x, y+i, logw)&mask;
+ uint8 c = a & 1 ? px[a>>1] >> 4 : px[a>>1] & 0xF;
+ tmpbuf[s>>1] = s & 1 ? (tmpbuf[s>>1]&0xF) | c<<4 : (tmpbuf[s>>1]&0xF0) | c;
+ }
+ memcpy(&px[y<<(logw-1)], tmpbuf, 2*w);
+ }
+ }else if(raster->format & Raster::PAL8 && natras->flags & Ps2Raster::SWIZZLED8){
+ for(y = 0; y < h; y += 4){
+ for(i = 0; i < 4; i++)
+ for(x = 0; x < w; x++){
+ uint32 a = ((y+i)<<logw)+x;
+ uint32 s = swizzle(x, y+i, logw)&mask;
+ tmpbuf[s] = px[a];
+ }
+ memcpy(&px[y<<logw], tmpbuf, 4*w);
+ }
+ }
+}
+
+uint8*
+rasterLock(Raster *raster, int32 level, int32 lockMode)
+{
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+ assert(raster->depth != 24);
+
+ if(level > 0){
+ int32 minw, minh;
+ int32 mipw, miph;
+ transferMinSize(raster->format & Raster::PAL4 ? PSMT4 : PSMT8, natras->flags, &minw, &minh);
+ while(level--){
+ mipw = max(raster->width, minw);
+ miph = max(raster->height, minh);
+ raster->pixels += ALIGN16(mipw*miph*raster->depth/8) + 0x50;
+ raster->width /= 2;
+ raster->height /= 2;
+ }
+ }
+
+ if((lockMode & Raster::LOCKNOFETCH) == 0)
+ unswizzleRaster(raster);
+ if(lockMode & Raster::LOCKREAD) raster->privateFlags |= Raster::PRIVATELOCK_READ;
+ if(lockMode & Raster::LOCKWRITE) raster->privateFlags |= Raster::PRIVATELOCK_WRITE;
+ return raster->pixels;
+}
+
+void
+rasterUnlock(Raster *raster, int32 level)
+{
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+ if(raster->format & (Raster::PAL4 | Raster::PAL8))
+ swizzleRaster(raster);
+
+ raster->width = raster->originalWidth;
+ raster->height = raster->originalHeight;
+ raster->pixels = raster->originalPixels;
+ raster->stride = raster->originalStride;
+ if(natras->flags & Ps2Raster::NEWSTYLE)
+ raster->pixels = ((Ps2Raster::PixelPtr*)raster->pixels)->pixels + 0x50;
+
+ raster->privateFlags &= ~(Raster::PRIVATELOCK_READ|Raster::PRIVATELOCK_WRITE);
+ // TODO: generate mipmaps
+}
+
+static void
+convertCSM1_16(uint32 *pal)
+{
+ int i, j;
+ uint32 tmp;
+ for(i = 0; i < 256; i++)
+ // palette index bits 0x08 and 0x10 are flipped
+ if((i & 0x18) == 0x10){
+ j = i ^ 0x18;
+ tmp = pal[i];
+ pal[i] = pal[j];
+ pal[j] = tmp;
+ }
+}
+
+static void
+convertCSM1_32(uint32 *pal)
+{
+ int i, j;
+ uint32 tmp;
+ for(i = 0; i < 256; i++)
+ // palette index bits 0x08 and 0x10 are flipped
+ if((i & 0x18) == 0x10){
+ j = i ^ 0x18;
+ tmp = pal[i];
+ pal[i] = pal[j];
+ pal[j] = tmp;
+ }
+}
+
+static void
+convertPalette(Raster *raster)
+{
+ if(raster->format & Raster::PAL8){
+ if((raster->format & 0xF00) == Raster::C8888)
+ convertCSM1_32((uint32*)raster->palette);
+ else if((raster->format & 0xF00) == Raster::C8888)
+ convertCSM1_16((uint32*)raster->palette);
+ }
+}
+
+// NB: RW doesn't convert the palette when locking/unlocking
+uint8*
+rasterLockPalette(Raster *raster, int32 lockMode)
+{
+ if((raster->format & (Raster::PAL4 | Raster::PAL8)) == 0)
+ return nil;
+ if((lockMode & Raster::LOCKNOFETCH) == 0)
+ convertPalette(raster);
+ if(lockMode & Raster::LOCKREAD) raster->privateFlags |= Raster::PRIVATELOCK_READ_PALETTE;
+ if(lockMode & Raster::LOCKWRITE) raster->privateFlags |= Raster::PRIVATELOCK_WRITE_PALETTE;
+ return raster->palette;
+}
+
+void
+rasterUnlockPalette(Raster *raster)
+{
+ if(raster->format & (Raster::PAL4 | Raster::PAL8))
+ convertPalette(raster);
+ raster->privateFlags &= ~(Raster::PRIVATELOCK_READ_PALETTE|Raster::PRIVATELOCK_WRITE_PALETTE);
+}
+
+// Almost the same as d3d9 and gl3 function
+bool32
+imageFindRasterFormat(Image *img, int32 type,
+ int32 *pWidth, int32 *pHeight, int32 *pDepth, int32 *pFormat)
+{
+ int32 width, height, depth, format;
+
+ assert((type&0xF) == Raster::TEXTURE);
+
+ for(width = 1; width < img->width; width <<= 1);
+ for(height = 1; height < img->height; height <<= 1);
+
+ depth = img->depth;
+
+ switch(depth){
+ case 32:
+ case 24:
+ // C888 24 bit is unsafe
+ format = Raster::C8888;
+ depth = 32;
+ break;
+ case 16:
+ format = Raster::C1555;
+ break;
+ case 8:
+ format = Raster::PAL8 | Raster::C8888;
+ break;
+ case 4:
+ format = Raster::PAL4 | Raster::C8888;
+ break;
+ default:
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ format |= type;
+
+ *pWidth = width;
+ *pHeight = height;
+ *pDepth = depth;
+ *pFormat = format;
+
+ return 1;
+}
+
+bool32
+rasterFromImage(Raster *raster, Image *image)
+{
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+
+ int32 pallength = 0;
+ switch(image->depth){
+ case 24:
+ case 32:
+ if(raster->format != Raster::C8888 &&
+ raster->format != Raster::C888) // unsafe already
+ goto err;
+ break;
+ case 16:
+ if(raster->format != Raster::C1555) goto err;
+ break;
+ case 8:
+ if(raster->format != (Raster::PAL8 | Raster::C8888)) goto err;
+ pallength = 256;
+ break;
+ case 4:
+ if(raster->format != (Raster::PAL4 | Raster::C8888)) goto err;
+ pallength = 16;
+ break;
+ default:
+ err:
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ // unsafe
+ if((raster->format&0xF00) == Raster::C888){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ uint8 *in, *out;
+ if(image->depth <= 8){
+ in = image->palette;
+ out = raster->lockPalette(Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ memcpy(out, in, 4*pallength);
+ for(int32 i = 0; i < pallength; i++){
+ out[3] = out[3]*128/255;
+ out += 4;
+ }
+ raster->unlockPalette();
+ }
+
+ int minw, minh;
+ int tw;
+ transferMinSize(image->depth == 4 ? PSMT4 : PSMT8, natras->flags, &minw, &minh);
+ tw = max(image->width, minw);
+ uint8 *src = image->pixels;
+ out = raster->lock(0, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ if(image->depth == 4){
+ compressPal4(out, tw/2, src, image->stride, image->width, image->height);
+ }else if(image->depth == 8){
+ copyPal8(out, tw, src, image->stride, image->width, image->height);
+ }else{
+ for(int32 y = 0; y < image->height; y++){
+ in = src;
+ for(int32 x = 0; x < image->width; x++){
+ switch(image->depth){
+ case 16:
+ conv_ARGB1555_from_ABGR1555(out, in);
+ out += 2;
+ break;
+ case 24:
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = 0x80;
+ out += 4;
+ break;
+ case 32:
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3]*128/255;
+ out += 4;
+ break;
+ }
+ in += image->bpp;
+ }
+ src += image->stride;
+ }
+ }
+ raster->unlock(0);
+ return 1;
+}
+
+Image*
+rasterToImage(Raster *raster)
+{
+ Image *image;
+ int depth;
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+
+ int32 rasterFormat = raster->format & 0xF00;
+ switch(rasterFormat){
+ case Raster::C1555:
+ depth = 16;
+ break;
+ case Raster::C8888:
+ depth = 32;
+ break;
+ case Raster::C888:
+ depth = 24;
+ break;
+ case Raster::C555:
+ depth = 16;
+ break;
+
+ default:
+ case Raster::C565:
+ case Raster::C4444:
+ case Raster::LUM8:
+ assert(0 && "unsupported ps2 raster format");
+ }
+ int32 pallength = 0;
+ if((raster->format & Raster::PAL4) == Raster::PAL4){
+ depth = 4;
+ pallength = 16;
+ }else if((raster->format & Raster::PAL8) == Raster::PAL8){
+ depth = 8;
+ pallength = 256;
+ }
+
+ uint8 *in, *out;
+ image = Image::create(raster->width, raster->height, depth);
+ image->allocate();
+
+ if(pallength){
+ out = image->palette;
+ in = raster->lockPalette(Raster::LOCKREAD);
+ if(rasterFormat == Raster::C8888){
+ memcpy(out, in, pallength*4);
+ for(int32 i = 0; i < pallength; i++){
+ out[3] = out[3]*255/128;
+ out += 4;
+ }
+ }else
+ memcpy(out, in, pallength*2);
+ raster->unlockPalette();
+ }
+
+ int minw, minh;
+ int tw;
+ transferMinSize(depth == 4 ? PSMT4 : PSMT8, natras->flags, &minw, &minh);
+ tw = max(raster->width, minw);
+ uint8 *dst = image->pixels;
+ in = raster->lock(0, Raster::LOCKREAD);
+ if(depth == 4){
+ expandPal4(dst, image->stride, in, tw/2, raster->width, raster->height);
+ }else if(depth == 8){
+ copyPal8(dst, image->stride, in, tw, raster->width, raster->height);
+ }else{
+ for(int32 y = 0; y < image->height; y++){
+ out = dst;
+ for(int32 x = 0; x < image->width; x++){
+ switch(raster->format & 0xF00){
+ case Raster::C8888:
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3]*255/128;
+ in += 4;
+ break;
+ case Raster::C888:
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ in += 4;
+ break;
+ case Raster::C1555:
+ conv_ARGB1555_from_ABGR1555(out, in);
+ in += 2;
+ break;
+ case Raster::C555:
+ conv_ARGB1555_from_ABGR1555(out, in);
+ out[1] |= 0x80;
+ in += 2;
+ break;
+ default:
+ assert(0 && "unknown ps2 raster format");
+ break;
+ }
+ out += image->bpp;
+ }
+ dst += image->stride;
+ }
+ }
+ raster->unlock(0);
+
+ return image;
+}
+
+int32
+rasterNumLevels(Raster *raster)
+{
+ Ps2Raster *ras = GETPS2RASTEREXT(raster);
+ if(raster->pixels == nil) return 0;
+ if(raster->format & Raster::MIPMAP)
+ return MAXLEVEL(ras)+1;
+ return 1;
+}
+
+static void*
+createNativeRaster(void *object, int32 offset, int32)
+{
+ Ps2Raster *raster = GETPS2RASTEREXT(object);
+ raster->tex0 = 0;
+ raster->paletteBase = 0;
+ raster->kl = defaultMipMapKL;
+ raster->tex1low = 0;
+ raster->unk2 = 0;
+ raster->miptbp1 = 0;
+ raster->miptbp2 = 0;
+ raster->pixelSize = 0;
+ raster->paletteSize = 0;
+ raster->totalSize = 0;
+ raster->flags = 0;
+
+ raster->dataSize = 0;
+ raster->data = nil;
+ return object;
+}
+
+static void*
+destroyNativeRaster(void *object, int32 offset, int32)
+{
+ Ps2Raster *raster = GETPS2RASTEREXT(object);
+ freealign(raster->data);
+ return object;
+}
+
+static void*
+copyNativeRaster(void *dst, void *src, int32 offset, int32)
+{
+ Ps2Raster *dstraster = GETPS2RASTEREXT(dst);
+ Ps2Raster *srcraster = GETPS2RASTEREXT(src);
+ *dstraster = *srcraster;
+ return dst;
+}
+
+static Stream*
+readMipmap(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ uint16 val = stream->readI32();
+ Texture *tex = (Texture*)object;
+ if(tex->raster == nil)
+ return stream;
+ Ps2Raster *raster = GETPS2RASTEREXT(tex->raster);
+ raster->kl = val;
+ return stream;
+}
+
+static Stream*
+writeMipmap(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ Texture *tex = (Texture*)object;
+ if(tex->raster){
+ stream->writeI32(defaultMipMapKL);
+ return stream;
+ }
+ Ps2Raster *raster = GETPS2RASTEREXT(tex->raster);
+ stream->writeI32(raster->kl);
+ return stream;
+}
+
+static int32
+getSizeMipmap(void*, int32, int32)
+{
+ return rw::platform == PLATFORM_PS2 ? 4 : 0;
+}
+
+void
+registerNativeRaster(void)
+{
+ nativeRasterOffset = Raster::registerPlugin(sizeof(Ps2Raster),
+ ID_RASTERPS2,
+ createNativeRaster,
+ destroyNativeRaster,
+ copyNativeRaster);
+
+ Texture::registerPlugin(0, ID_SKYMIPMAP, nil, nil, nil);
+ Texture::registerPluginStream(ID_SKYMIPMAP, readMipmap, writeMipmap, getSizeMipmap);
+}
+
+void
+printTEX0(uint64 tex0)
+{
+ printf("%016lX ", tex0);
+ uint32 tbp0 = tex0 & 0x3FFF; tex0 >>= 14;
+ uint32 tbw = tex0 & 0x3F; tex0 >>= 6;
+ uint32 psm = tex0 & 0x3F; tex0 >>= 6;
+ uint32 tw = tex0 & 0xF; tex0 >>= 4;
+ uint32 th = tex0 & 0xF; tex0 >>= 4;
+ uint32 tcc = tex0 & 0x1; tex0 >>= 1;
+ uint32 tfx = tex0 & 0x3; tex0 >>= 2;
+ uint32 cbp = tex0 & 0x3FFF; tex0 >>= 14;
+ uint32 cpsm = tex0 & 0xF; tex0 >>= 4;
+ uint32 csm = tex0 & 0x1; tex0 >>= 1;
+ uint32 csa = tex0 & 0x1F; tex0 >>= 5;
+ uint32 cld = tex0 & 0x7;
+ printf("TBP0:%4X TBW:%2X PSM:%2X TW:%X TH:%X TCC:%X TFX:%X CBP:%4X CPSM:%X CSM:%X CSA:%2X CLD:%X\n",
+ tbp0, tbw, psm, tw, th, tcc, tfx, cbp, cpsm, csm, csa, cld);
+}
+
+void
+printTEX1(uint64 tex1)
+{
+ printf("%016lX ", tex1);
+ uint32 lcm = tex1 & 0x1; tex1 >>= 2;
+ uint32 mxl = tex1 & 0x7; tex1 >>= 3;
+ uint32 mmag = tex1 & 0x1; tex1 >>= 1;
+ uint32 mmin = tex1 & 0x7; tex1 >>= 3;
+ uint32 mtba = tex1 & 0x1; tex1 >>= 10;
+ uint32 l = tex1 & 0x3; tex1 >>= 13;
+ uint32 k = tex1 & 0xFFF;
+ printf("LCM:%X MXL:%X MMAG:%X MMIN:%X MTBA:%X L:%X K:%X\n",
+ lcm, mxl, mmag, mmin, mtba, l, k);
+}
+
+void
+calcTEX1(Raster *raster, uint64 *tex1, int32 filter)
+{
+ enum {
+ NEAREST = 0,
+ LINEAR,
+ NEAREST_MIPMAP_NEAREST,
+ NEAREST_MIPMAP_LINEAR,
+ LINEAR_MIPMAP_NEAREST,
+ LINEAR_MIPMAP_LINEAR,
+ };
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+ uint64 t1 = natras->tex1low;
+ uint64 k = natras->kl & 0xFFF;
+ uint64 l = (natras->kl >> 12) & 0x3;
+ t1 |= k << 32;
+ t1 |= l << 19;
+ switch(filter){
+ case Texture::NEAREST:
+ t1 |= (NEAREST << 5) |
+ (NEAREST << 6);
+ break;
+ case Texture::LINEAR:
+ t1 |= (LINEAR << 5) |
+ (LINEAR << 6);
+ break;
+ case Texture::MIPNEAREST:
+ t1 |= (NEAREST << 5) |
+ (NEAREST_MIPMAP_NEAREST << 6);
+ break;
+ case Texture::MIPLINEAR:
+ t1 |= (LINEAR << 5) |
+ (LINEAR_MIPMAP_NEAREST << 6);
+ break;
+ case Texture::LINEARMIPNEAREST:
+ t1 |= (NEAREST << 5) |
+ (NEAREST_MIPMAP_LINEAR << 6);
+ break;
+ case Texture::LINEARMIPLINEAR:
+ t1 |= (LINEAR << 5) |
+ (LINEAR_MIPMAP_LINEAR << 6);
+ break;
+ }
+ *tex1 = t1;
+}
+
+struct StreamRasterExt
+{
+ int32 width;
+ int32 height;
+ int32 depth;
+ uint16 rasterFormat;
+ int16 version;
+ uint64 tex0;
+ uint32 paletteOffset;
+ uint32 tex1low;
+ uint64 miptbp1;
+ uint64 miptbp2;
+ uint32 pixelSize;
+ uint32 paletteSize;
+ uint32 totalSize;
+ uint32 mipmapVal;
+};
+
+Texture*
+readNativeTexture(Stream *stream)
+{
+ uint32 length, oldversion, version;
+ uint32 fourcc;
+ Raster *raster;
+ Ps2Raster *natras;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ fourcc = stream->readU32();
+ if(fourcc != FOURCC_PS2){
+ RWERROR((ERR_PLATFORM, fourcc));
+ return nil;
+ }
+ Texture *tex = Texture::create(nil);
+ if(tex == nil)
+ return nil;
+
+ // Texture
+ tex->filterAddressing = stream->readU32();
+ if(!findChunk(stream, ID_STRING, &length, nil)){
+ RWERROR((ERR_CHUNK, "STRING"));
+ goto fail;
+ }
+ stream->read8(tex->name, length);
+ if(!findChunk(stream, ID_STRING, &length, nil)){
+ RWERROR((ERR_CHUNK, "STRING"));
+ goto fail;
+ }
+ stream->read8(tex->mask, length);
+
+ // Raster
+ StreamRasterExt streamExt;
+ oldversion = rw::version;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ goto fail;
+ }
+ if(!findChunk(stream, ID_STRUCT, nil, &version)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ goto fail;
+ }
+ ASSERTLITTLE;
+ stream->read8(&streamExt, 0x40);
+/*
+printf("%X %X %X %X %X %016llX %X %X %016llX %016llX %X %X %X %X\n",
+streamExt.width,
+streamExt.height,
+streamExt.depth,
+streamExt.rasterFormat,
+streamExt.version,
+streamExt.tex0,
+streamExt.paletteOffset,
+streamExt.tex1low,
+streamExt.miptbp1,
+streamExt.miptbp2,
+streamExt.pixelSize,
+streamExt.paletteSize,
+streamExt.totalSize,
+streamExt.mipmapVal);
+*/
+ noNewStyleRasters = streamExt.version < 2;
+ rw::version = version;
+ raster = Raster::create(streamExt.width, streamExt.height,
+ streamExt.depth, streamExt.rasterFormat,
+ PLATFORM_PS2);
+ noNewStyleRasters = 0;
+ rw::version = oldversion;
+ tex->raster = raster;
+ natras = GETPS2RASTEREXT(raster);
+//printf("%X %X\n", natras->paletteBase, natras->tex1low);
+// printf("%08X%08X %08X%08X %08X%08X\n",
+// (uint32)natras->tex0, (uint32)(natras->tex0>>32),
+// (uint32)natras->miptbp1, (uint32)(natras->miptbp1>>32),
+// (uint32)natras->miptbp2, (uint32)(natras->miptbp2>>32));
+// printTEX0(natras->tex0);
+ uint64 tex1;
+ calcTEX1(raster, &tex1, tex->filterAddressing & 0xF);
+// printTEX1(tex1);
+
+ // TODO: GTA SA LD_OTB.txd loses here
+ assert(natras->pixelSize >= streamExt.pixelSize);
+ assert(natras->paletteSize >= streamExt.paletteSize);
+
+//if(natras->tex0 != streamExt.tex0){
+//printf("TEX0: %016llX\n %016llX\n", natras->tex0, streamExt.tex0);
+//printTEX0(natras->tex0);
+//printTEX0(streamExt.tex0);
+//fflush(stdout);
+//}
+//if(natras->tex1low != streamExt.tex1low)
+//printf("TEX1: %08X\n %08X\n", natras->tex1low, streamExt.tex1low);
+//if(natras->miptbp1 != streamExt.miptbp1)
+//printf("MIP1: %016llX\n %016llX\n", natras->miptbp1, streamExt.miptbp1);
+//if(natras->miptbp2 != streamExt.miptbp2)
+//printf("MIP2: %016llX\n %016llX\n", natras->miptbp2, streamExt.miptbp2);
+//if(natras->paletteBase != streamExt.paletteOffset)
+//printf("PAL: %08X\n %08X\n", natras->paletteBase, streamExt.paletteOffset);
+//if(natras->pixelSize != streamExt.pixelSize)
+//printf("PXS: %08X\n %08X\n", natras->pixelSize, streamExt.pixelSize);
+//if(natras->paletteSize != streamExt.paletteSize)
+//printf("PLS: %08X\n %08X\n", natras->paletteSize, streamExt.paletteSize);
+//if(natras->totalSize != streamExt.totalSize)
+//printf("TSZ: %08X\n %08X\n", natras->totalSize, streamExt.totalSize);
+
+ // junk addresses, no need to store them
+ streamExt.tex0 &= ~0x3FFFULL;
+ streamExt.tex0 &= ~(0x3FFFULL << 37);
+
+ assert(natras->tex0 == streamExt.tex0);
+ natras->tex0 = streamExt.tex0;
+ assert(natras->paletteBase == streamExt.paletteOffset);
+ natras->paletteBase = streamExt.paletteOffset;
+ assert(natras->tex1low == streamExt.tex1low);
+ natras->tex1low = streamExt.tex1low;
+ assert(natras->miptbp1 == streamExt.miptbp1);
+ natras->miptbp1 = streamExt.miptbp1;
+ assert(natras->miptbp2 == streamExt.miptbp2);
+ natras->miptbp2 = streamExt.miptbp2;
+ assert(natras->pixelSize == streamExt.pixelSize);
+ natras->pixelSize = streamExt.pixelSize;
+ assert(natras->paletteSize == streamExt.paletteSize);
+ natras->paletteSize = streamExt.paletteSize;
+ assert(natras->totalSize == streamExt.totalSize);
+ natras->totalSize = streamExt.totalSize;
+ natras->kl = streamExt.mipmapVal;
+//printf("%X %X\n", natras->paletteBase, natras->tex1low);
+// printf("%08X%08X %08X%08X %08X%08X\n",
+// (uint32)natras->tex0, (uint32)(natras->tex0>>32),
+// (uint32)natras->miptbp1, (uint32)(natras->miptbp1>>32),
+// (uint32)natras->miptbp2, (uint32)(natras->miptbp2>>32));
+// printTEX0(natras->tex0);
+ calcTEX1(raster, &tex1, tex->filterAddressing & 0xF);
+// printTEX1(tex1);
+
+ // this is weird stuff
+ if(streamExt.version < 2){
+ if(streamExt.version == 1){
+ // Version 1 has swizzled 8 bit textures
+ if(!(natras->flags & Ps2Raster::NEWSTYLE))
+ natras->flags |= Ps2Raster::SWIZZLED8;
+ else
+ assert(0 && "can't happen");
+ }else{
+ // Version 0 has no swizzling at all
+ if(!(natras->flags & Ps2Raster::NEWSTYLE))
+ natras->flags &= ~Ps2Raster::SWIZZLED8;
+ else
+ assert(0 && "can't happen");
+ }
+ }
+
+ if(!findChunk(stream, ID_STRUCT, &length, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ goto fail;
+ }
+ if(streamExt.version < 2){
+ stream->read8(raster->pixels, length);
+ }else{
+ stream->read8(((Ps2Raster::PixelPtr*)raster->originalPixels)->pixels, natras->pixelSize);
+ stream->read8(raster->palette-0x50, natras->paletteSize);
+ }
+//printf("\n");
+ return tex;
+
+fail:
+ tex->destroy();
+ return nil;
+}
+
+void
+writeNativeTexture(Texture *tex, Stream *stream)
+{
+ Raster *raster = tex->raster;
+ Ps2Raster *ras = GETPS2RASTEREXT(raster);
+ writeChunkHeader(stream, ID_STRUCT, 8);
+ stream->writeU32(FOURCC_PS2);
+ stream->writeU32(tex->filterAddressing);
+ int32 len = strlen(tex->name)+4 & ~3;
+ writeChunkHeader(stream, ID_STRING, len);
+ stream->write8(tex->name, len);
+ len = strlen(tex->mask)+4 & ~3;
+ writeChunkHeader(stream, ID_STRING, len);
+ stream->write8(tex->mask, len);
+
+ int32 sz = ras->pixelSize + ras->paletteSize;
+ writeChunkHeader(stream, ID_STRUCT, 12 + 64 + 12 + sz);
+ writeChunkHeader(stream, ID_STRUCT, 64);
+ StreamRasterExt streamExt;
+ streamExt.width = raster->width;
+ streamExt.height = raster->height;
+ streamExt.depth = raster->depth;
+ streamExt.rasterFormat = raster->format | raster->type;
+ streamExt.version = 0;
+ if(ras->flags == Ps2Raster::SWIZZLED8 && raster->depth == 8)
+ streamExt.version = 1;
+ if(ras->flags & Ps2Raster::NEWSTYLE)
+ streamExt.version = 2;
+ streamExt.tex0 = ras->tex0;
+ streamExt.paletteOffset = ras->paletteBase;
+ streamExt.tex1low = ras->tex1low;
+ streamExt.miptbp1 = ras->miptbp1;
+ streamExt.miptbp2 = ras->miptbp2;
+ streamExt.pixelSize = ras->pixelSize;
+ streamExt.paletteSize = ras->paletteSize;
+ streamExt.totalSize = ras->totalSize;
+ streamExt.mipmapVal = ras->kl;
+ ASSERTLITTLE;
+ stream->write8(&streamExt, 64);
+
+ writeChunkHeader(stream, ID_STRUCT, sz);
+ if(streamExt.version < 2){
+ stream->write8(raster->pixels, sz);
+ }else{
+ stream->write8(((Ps2Raster::PixelPtr*)raster->originalPixels)->pixels, ras->pixelSize);
+ stream->write8(raster->palette-0x50, ras->paletteSize);
+ }
+}
+
+uint32
+getSizeNativeTexture(Texture *tex)
+{
+ uint32 size = 12 + 8;
+ size += 12 + strlen(tex->name)+4 & ~3;
+ size += 12 + strlen(tex->mask)+4 & ~3;
+ size += 12;
+ size += 12 + 64;
+ Ps2Raster *ras = GETPS2RASTEREXT(tex->raster);
+ size += 12 + ras->pixelSize + ras->paletteSize;
+ return size;
+}
+
+}
+}
diff --git a/src/ps2/ps2skin.cpp b/src/ps2/ps2skin.cpp
new file mode 100644
index 0000000..d4c9d79
--- /dev/null
+++ b/src/ps2/ps2skin.cpp
@@ -0,0 +1,334 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwplugins.h"
+#include "rwps2.h"
+#include "rwps2plg.h"
+
+#include "rwps2impl.h"
+
+#define PLUGIN_ID ID_SKIN
+
+namespace rw {
+namespace ps2 {
+
+static void*
+skinOpen(void *o, int32, int32)
+{
+ skinGlobals.pipelines[PLATFORM_PS2] = makeSkinPipeline();
+ return o;
+}
+
+static void*
+skinClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_PS2])->groupPipeline->destroy();
+ ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_PS2])->groupPipeline = nil;
+ ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_PS2])->destroy();
+ skinGlobals.pipelines[PLATFORM_PS2] = nil;
+ return o;
+}
+
+void
+initSkin(void)
+{
+ Driver::registerPlugin(PLATFORM_PS2, 0, ID_SKIN,
+ skinOpen, skinClose);
+}
+
+ObjPipeline*
+makeSkinPipeline(void)
+{
+ MatPipeline *pipe = MatPipeline::create();
+ pipe->pluginID = ID_SKIN;
+ pipe->pluginData = 1;
+ pipe->attribs[AT_XYZ] = &attribXYZ;
+ pipe->attribs[AT_UV] = &attribUV;
+ pipe->attribs[AT_RGBA] = &attribRGBA;
+ pipe->attribs[AT_NORMAL] = &attribNormal;
+ pipe->attribs[AT_NORMAL+1] = &attribWeights;
+ uint32 vertCount = MatPipeline::getVertCount(VU_Lights-0x100, 5, 3, 2);
+ pipe->setTriBufferSizes(5, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->instanceCB = skinInstanceCB;
+ pipe->uninstanceCB = genericUninstanceCB;
+ pipe->preUninstCB = skinPreCB;
+ pipe->postUninstCB = skinPostCB;
+
+ ObjPipeline *opipe = ObjPipeline::create();
+ opipe->pluginID = ID_SKIN;
+ opipe->pluginData = 1;
+ opipe->groupPipeline = pipe;
+ return opipe;
+}
+
+Stream*
+readNativeSkin(Stream *stream, int32, void *object, int32 offset)
+{
+ uint8 header[4];
+ Geometry *geometry = (Geometry*)object;
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_PS2){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ stream->read8(header, 4);
+ Skin *skin = rwNewT(Skin, 1, MEMDUR_EVENT | ID_SKIN);
+ *PLUGINOFFSET(Skin*, geometry, offset) = skin;
+
+ // numUsedBones and numWeights appear in/after 34003
+ // but not in/before 33002 (probably rw::version >= 0x34000)
+ bool oldFormat = header[1] == 0;
+
+ // Use numBones for numUsedBones to allocate data
+ if(oldFormat)
+ skin->init(header[0], header[0], 0);
+ else
+ skin->init(header[0], header[1], 0);
+ skin->numWeights = header[2];
+
+ if(!oldFormat)
+ stream->read8(skin->usedBones, skin->numUsedBones);
+ if(skin->numBones)
+ stream->read32(skin->inverseMatrices, skin->numBones*64);
+
+ // dummy data in case we need to write data in the new format
+ if(oldFormat){
+ skin->numWeights = 4;
+ for(int32 i = 0; i < skin->numUsedBones; i++)
+ skin->usedBones[i] = i;
+ }
+
+ if(!oldFormat){
+ // TODO: what is this?
+ stream->seek(4*4);
+
+ readSkinSplitData(stream, skin);
+ }
+ return stream;
+}
+
+Stream*
+writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset)
+{
+ uint8 header[4];
+
+ writeChunkHeader(stream, ID_STRUCT, len-12);
+ stream->writeU32(PLATFORM_PS2);
+ Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
+ // not sure which version introduced the new format
+ bool oldFormat = version < 0x34000;
+ header[0] = skin->numBones;
+ if(oldFormat){
+ header[1] = 0;
+ header[2] = 0;
+ }else{
+ header[1] = skin->numUsedBones;
+ header[2] = skin->numWeights;
+ }
+ header[3] = 0;
+ stream->write8(header, 4);
+
+ if(!oldFormat)
+ stream->write8(skin->usedBones, skin->numUsedBones);
+ stream->write32(skin->inverseMatrices, skin->numBones*64);
+ if(!oldFormat){
+ uint32 buffer[4] = { 0, 0, 0, 0, };
+ stream->write32(buffer, 4*4);
+
+ writeSkinSplitData(stream, skin);
+ }
+ return stream;
+}
+
+int32
+getSizeNativeSkin(void *object, int32 offset)
+{
+ Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
+ if(skin == nil)
+ return -1;
+ int32 size = 12 + 4 + 4 + skin->numBones*64;
+ // not sure which version introduced the new format
+ if(version >= 0x34000)
+ size += skin->numUsedBones + 16 + skinSplitDataSize(skin);
+ return size;
+}
+
+void
+instanceSkinData(Geometry*, Mesh *m, Skin *skin, uint32 *data)
+{
+ uint16 j;
+ float32 *weights = (float32*)data;
+ uint32 *indices = data;
+ for(uint32 i = 0; i < m->numIndices; i++){
+ j = m->indices[i];
+ for(int32 k = 0; k < 4; k++){
+ *weights++ = skin->weights[j*4+k];
+ *indices &= ~0x3FF;
+ *indices++ |= skin->indices[j*4+k] && skin->weights[j*4+k] ?
+ (skin->indices[j*4+k]+1) << 2 : 0;
+ }
+ }
+}
+
+void
+skinInstanceCB(MatPipeline *, Geometry *g, Mesh *m, uint8 **data)
+{
+ Skin *skin = Skin::get(g);
+ if(skin == nil)
+ return;
+ instanceSkinData(g, m, skin, (uint32*)data[4]);
+}
+
+// TODO: call base function perhaps?
+int32
+findVertexSkin(Geometry *g, uint32 flags[], uint32 mask, Vertex *v)
+{
+ Skin *skin = Skin::get(g);
+ float32 *wghts = nil;
+ uint8 *inds = nil;
+ if(skin){
+ wghts = skin->weights;
+ inds = skin->indices;
+ }
+
+ V3d *verts = g->morphTargets[0].vertices;
+ TexCoords *tex = g->texCoords[0];
+ TexCoords *tex1 = g->texCoords[1];
+ V3d *norms = g->morphTargets[0].normals;
+ RGBA *cols = g->colors;
+
+ for(int32 i = 0; i < g->numVertices; i++){
+ uint32 flag = flags ? flags[i] : ~0;
+ if(mask & flag & 0x1 && !equal(*verts, v->p))
+ goto cont;
+ if(mask & flag & 0x10 && !equal(*norms, v->n))
+ goto cont;
+ if(mask & flag & 0x100 && !equal(*cols, v->c))
+ goto cont;
+ if(mask & flag & 0x1000 && !equal(*tex, v->t))
+ goto cont;
+ if(mask & flag & 0x2000 && !equal(*tex1, v->t1))
+ goto cont;
+ if(mask & flag & 0x10000 &&
+ !(wghts[0] == v->w[0] && wghts[1] == v->w[1] &&
+ wghts[2] == v->w[2] && wghts[3] == v->w[3] &&
+ inds[0] == v->i[0] && inds[1] == v->i[1] &&
+ inds[2] == v->i[2] && inds[3] == v->i[3]))
+ goto cont;
+ return i;
+ cont:
+ verts++;
+ tex++;
+ tex1++;
+ norms++;
+ cols++;
+ wghts += 4;
+ inds += 4;
+ }
+ return -1;
+}
+
+void
+insertVertexSkin(Geometry *geo, int32 i, uint32 mask, Vertex *v)
+{
+ Skin *skin = Skin::get(geo);
+ insertVertex(geo, i, mask, v);
+ if(mask & 0x10000){
+ memcpy(&skin->weights[i*4], v->w, 16);
+ memcpy(&skin->indices[i*4], v->i, 4);
+ }
+}
+
+/*
+void
+skinUninstanceCB(MatPipeline*, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[])
+{
+ float32 *verts = (float32*)data[AT_XYZ];
+ float32 *texcoords = (float32*)data[AT_UV];
+ uint8 *colors = (uint8*)data[AT_RGBA];
+ int8 *norms = (int8*)data[AT_NORMAL];
+ uint32 *wghts = (uint32*)data[AT_NORMAL+1];
+ uint32 mask = 0x1; // vertices
+ if(geo->flags & Geometry::NORMALS)
+ mask |= 0x10;
+ if(geo->flags & Geometry::PRELIT)
+ mask |= 0x100;
+ if(geo->numTexCoordSets > 0)
+ mask |= 0x1000;
+ mask |= 0x10000;
+
+ Vertex v;
+ for(uint32 i = 0; i < mesh->numIndices; i++){
+ if(mask & 0x1)
+ memcpy(&v.p, verts, 12);
+ if(mask & 0x10){
+ v.n[0] = norms[0]/127.0f;
+ v.n[1] = norms[1]/127.0f;
+ v.n[2] = norms[2]/127.0f;
+ }
+ if(mask & 0x100)
+ memcpy(&v.c, colors, 4);
+ if(mask & 0x1000)
+ memcpy(&v.t, texcoords, 8);
+ for(int j = 0; j < 4; j++){
+ ((uint32*)v.w)[j] = wghts[j] & ~0x3FF;
+ v.i[j] = (wghts[j] & 0x3FF) >> 2;
+ if(v.i[j]) v.i[j]--;
+ if(v.w[j] == 0.0f) v.i[j] = 0;
+ }
+ int32 idx = findVertexSkin(geo, flags, mask, &v);
+ if(idx < 0)
+ idx = geo->numVertices++;
+ mesh->indices[i] = idx;
+ flags[idx] = mask;
+ insertVertexSkin(geo, idx, mask, &v);
+ verts += 3;
+ texcoords += 2;
+ colors += 4;
+ norms += 3;
+ wghts += 4;
+ }
+}
+*/
+
+void
+skinPreCB(MatPipeline*, Geometry *geo)
+{
+ Skin *skin = Skin::get(geo);
+ if(skin == nil)
+ return;
+ uint8 *data = skin->data;
+ float *invMats = skin->inverseMatrices;
+ // meshHeader->totalIndices is highest possible number of vertices again
+ skin->init(skin->numBones, skin->numBones, geo->meshHeader->totalIndices);
+ memcpy(skin->inverseMatrices, invMats, skin->numBones*64);
+ rwFree(data);
+}
+
+void
+skinPostCB(MatPipeline*, Geometry *geo)
+{
+ Skin *skin = Skin::get(geo);
+ if(skin){
+ skin->findNumWeights(geo->numVertices);
+ skin->findUsedBones(geo->numVertices);
+ }
+}
+
+}
+}
diff --git a/src/ps2/rwps2.h b/src/ps2/rwps2.h
new file mode 100644
index 0000000..d2945e9
--- /dev/null
+++ b/src/ps2/rwps2.h
@@ -0,0 +1,281 @@
+namespace rw {
+
+#ifdef RW_PS2
+struct EngineOpenParams
+{
+};
+#endif
+
+namespace ps2 {
+
+void registerPlatformPlugins(void);
+
+extern Device renderdevice;
+
+struct Im2DVertex
+{
+ float32 x, y, z, w;
+ float32 u, v, q, PAD;
+ uint32 r, g, b, a;
+
+ void setScreenX(float32 x) { this->x = x; }
+ void setScreenY(float32 y) { this->y = y; }
+ void setScreenZ(float32 z) { this->z = z; }
+ void setCameraZ(float32 z) { this->w = z; }
+ void setRecipCameraZ(float32 recipz) { this->q = recipz; }
+ void setColor(uint8 r, uint8 g, uint8 b, uint8 a) {
+ this->r = r; this->g = g; this->b = b; this->a = a; }
+ void setU(float32 u, float recipz) { this->u = u; }
+ void setV(float32 v, float recipz) { this->v = v; }
+
+ float getScreenX(void) { return this->x; }
+ float getScreenY(void) { return this->y; }
+ float getScreenZ(void) { return this->z; }
+ float getCameraZ(void) { return this->w; }
+ float getRecipCameraZ(void) { return this->q; }
+ RGBA getColor(void) { return makeRGBA(this->r, this->g, this->b, this->a); }
+ float getU(void) { return this->u; }
+ float getV(void) { return this->v; }
+};
+
+struct Im3DVertex
+{
+ V3d position;
+ uint8 r, g, b, a;
+ float32 u, v;
+
+ void setX(float32 x) { this->position.x = x; }
+ void setY(float32 y) { this->position.y = y; }
+ void setZ(float32 z) { this->position.z = z; }
+ void setColor(uint8 r, uint8 g, uint8 b, uint8 a) {
+ this->r = r; this->g = g; this->b = b; this->a = a; }
+ void setU(float32 u) { this->u = u; }
+ void setV(float32 v) { this->v = v; }
+
+ float getX(void) { return this->position.x; }
+ float getY(void) { return this->position.y; }
+ float getZ(void) { return this->position.z; }
+ RGBA getColor(void) { return makeRGBA(this->r, this->g, this->b, this->a); }
+ float getU(void) { return this->u; }
+ float getV(void) { return this->v; }
+};
+
+
+struct InstanceData
+{
+ uint32 dataSize;
+ uint8 *dataRaw;
+ uint8 *data;
+ Material *material;
+};
+
+struct InstanceDataHeader : rw::InstanceDataHeader
+{
+ uint32 numMeshes;
+ InstanceData *instanceMeshes;
+};
+
+enum {
+ VU_Lights = 0x3d0
+};
+
+enum PS2Attribs {
+ AT_V2_32 = 0x64000000,
+ AT_V2_16 = 0x65000000,
+ AT_V2_8 = 0x66000000,
+ AT_V3_32 = 0x68000000,
+ AT_V3_16 = 0x69000000,
+ AT_V3_8 = 0x6A000000,
+ AT_V4_32 = 0x6C000000,
+ AT_V4_16 = 0x6D000000,
+ AT_V4_8 = 0x6E000000,
+ AT_UNSGN = 0x00004000,
+
+ AT_RW = 0x6
+};
+
+// Not really types as in RW but offsets
+enum PS2AttibTypes {
+ AT_XYZ = 0,
+ AT_UV = 1,
+ AT_RGBA = 2,
+ AT_NORMAL = 3
+};
+
+void *destroyNativeData(void *object, int32, int32);
+Stream *readNativeData(Stream *stream, int32 len, void *object, int32, int32);
+Stream *writeNativeData(Stream *stream, int32 len, void *object, int32, int32);
+int32 getSizeNativeData(void *object, int32, int32);
+void registerNativeDataPlugin(void);
+
+void printDMA(InstanceData *inst);
+void printDMAVIF(InstanceData *inst);
+void sizedebug(InstanceData *inst);
+
+void fixDmaOffsets(InstanceData *inst); // only RW_PS2
+int32 unfixDmaOffsets(InstanceData *inst);
+
+struct PipeAttribute
+{
+ const char *name;
+ uint32 attrib;
+};
+
+extern bool adcHack; // use MASK write to initialize ADC field in vertex cluster
+extern PipeAttribute attribXYZ;
+extern PipeAttribute attribXYZW;
+extern PipeAttribute attribUV;
+extern PipeAttribute attribUV2;
+extern PipeAttribute attribRGBA;
+extern PipeAttribute attribNormal;
+extern PipeAttribute attribWeights;
+
+class MatPipeline : public rw::Pipeline
+{
+public:
+ uint32 vifOffset;
+ uint32 inputStride;
+ // number of vertices for tri strips and lists
+ uint32 triStripCount, triListCount;
+ PipeAttribute *attribs[10];
+ void (*instanceCB)(MatPipeline*, Geometry*, Mesh*, uint8**);
+ void (*uninstanceCB)(MatPipeline*, Geometry*, uint32*, Mesh*, uint8**);
+ void (*preUninstCB)(MatPipeline*, Geometry*);
+ void (*postUninstCB)(MatPipeline*, Geometry*);
+ // RW has more:
+ // instanceTestCB()
+ // resEntryAllocCB()
+ // bridgeCB()
+ // postMeshCB()
+ // vu1code
+ // primtype
+
+ static uint32 getVertCount(uint32 top, uint32 inAttribs,
+ uint32 outAttribs, uint32 outBufs) {
+ return (top-outBufs)/(inAttribs*2+outAttribs*outBufs);
+ }
+
+ void init(void);
+ static MatPipeline *create(void);
+ void destroy(void);
+ void dump(void);
+ void setTriBufferSizes(uint32 inputStride, uint32 bufferSize);
+ void instance(Geometry *g, InstanceData *inst, Mesh *m);
+ uint8 *collectData(Geometry *g, InstanceData *inst, Mesh *m, uint8 *data[]);
+};
+
+class ObjPipeline : public rw::ObjPipeline
+{
+public:
+ void init(void);
+ static ObjPipeline *create(void);
+
+ MatPipeline *groupPipeline;
+ // RW has more:
+ // setupCB()
+ // finalizeCB()
+ // lightOffset
+ // lightSize
+};
+
+struct Vertex {
+ V3d p;
+ TexCoords t;
+ TexCoords t1;
+ RGBA c;
+ V3d n;
+ // skin
+ float32 w[4];
+ uint8 i[4];
+};
+
+void insertVertex(Geometry *geo, int32 i, uint32 mask, Vertex *v);
+
+extern ObjPipeline *defaultObjPipe;
+extern MatPipeline *defaultMatPipe;
+
+void genericUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]);
+void genericPreCB(MatPipeline *pipe, Geometry *geo); // skin and ADC
+//void defaultUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]);
+void skinInstanceCB(MatPipeline *, Geometry *g, Mesh *m, uint8 **data);
+//void skinUninstanceCB(MatPipeline*, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]);
+
+ObjPipeline *makeDefaultPipeline(void);
+void dumpPipeline(rw::Pipeline *pipe);
+
+// ADC plugin
+
+// Each element in adcBits corresponds to an index in Mesh->indices,
+// this assumes the Mesh indices are ADC formatted.
+// ADCData->numBits != Mesh->numIndices. ADCData->numBits is probably
+// equal to Mesh->numIndices before the Mesh gets ADC formatted.
+//
+// Can't convert between ADC-formatted and non-ADC-formatted yet :(
+
+struct ADCData
+{
+ bool32 adcFormatted;
+ int8 *adcBits;
+ int32 numBits;
+};
+extern int32 adcOffset;
+void registerADCPlugin(void);
+
+int8 *getADCbits(Geometry *geo);
+int8 *getADCbitsForMesh(Geometry *geo, Mesh *mesh);
+void convertADC(Geometry *g);
+void unconvertADC(Geometry *geo);
+void allocateADC(Geometry *geo);
+
+// PDS plugin
+
+Pipeline *getPDSPipe(uint32 data);
+void registerPDSPipe(Pipeline *pipe);
+void registerPDSPlugin(int32 n);
+void registerPluginPDSPipes(void);
+
+// Native Texture and Raster
+
+struct Ps2Raster
+{
+ enum Flags {
+ NEWSTYLE = 0x1, // has GIF tags and transfer DMA chain
+ SWIZZLED8 = 0x2,
+ SWIZZLED4 = 0x4
+ };
+ struct PixelPtr {
+ // RW has pixels as second element but we don't want this struct
+ // to be longer than 16 bytes
+ uint8 *pixels;
+ // palette can be allocated in last level, in that case numTransfers is
+ // one less than numTotalTransfers.
+ int32 numTransfers;
+ int32 numTotalTransfers;
+ };
+
+ uint64 tex0;
+ uint32 paletteBase; // block address from beginning of GS data (words/64)
+ uint16 kl;
+ uint8 tex1low; // MXL and LCM of TEX1
+ uint8 unk2;
+ uint64 miptbp1;
+ uint64 miptbp2;
+ uint32 pixelSize; // in bytes
+ uint32 paletteSize; // in bytes
+ uint32 totalSize; // total size of texture on GS in words
+ int8 flags;
+
+ uint8 *data; //tmp
+ uint32 dataSize;
+};
+
+extern int32 nativeRasterOffset;
+void registerNativeRaster(void);
+#define GETPS2RASTEREXT(raster) PLUGINOFFSET(rw::ps2::Ps2Raster, raster, rw::ps2::nativeRasterOffset)
+
+Texture *readNativeTexture(Stream *stream);
+void writeNativeTexture(Texture *tex, Stream *stream);
+uint32 getSizeNativeTexture(Texture *tex);
+
+}
+}
diff --git a/src/ps2/rwps2impl.h b/src/ps2/rwps2impl.h
new file mode 100644
index 0000000..f455bd2
--- /dev/null
+++ b/src/ps2/rwps2impl.h
@@ -0,0 +1,16 @@
+namespace rw {
+namespace ps2 {
+
+Raster *rasterCreate(Raster *raster);
+uint8 *rasterLock(Raster*, int32 level, int32 lockMode);
+void rasterUnlock(Raster*, int32 level);
+uint8 *rasterLockPalette(Raster*, int32 lockMode);
+void rasterUnlockPalette(Raster*);
+int32 rasterNumLevels(Raster*);
+bool32 imageFindRasterFormat(Image *img, int32 type,
+ int32 *width, int32 *height, int32 *depth, int32 *format);
+bool32 rasterFromImage(Raster *raster, Image *image);
+Image *rasterToImage(Raster *raster);
+
+}
+}
diff --git a/src/ps2/rwps2plg.h b/src/ps2/rwps2plg.h
new file mode 100644
index 0000000..8776a51
--- /dev/null
+++ b/src/ps2/rwps2plg.h
@@ -0,0 +1,27 @@
+namespace rw {
+namespace ps2 {
+
+// MatFX plugin
+
+void initMatFX(void);
+ObjPipeline *makeMatFXPipeline(void);
+
+// Skin plugin
+
+void initSkin(void);
+ObjPipeline *makeSkinPipeline(void);
+
+void insertVertexSkin(Geometry *geo, int32 i, uint32 mask, Vertex *v);
+int32 findVertexSkin(Geometry *g, uint32 flags[], uint32 mask, Vertex *v);
+
+Stream *readNativeSkin(Stream *stream, int32, void *object, int32 offset);
+Stream *writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset);
+int32 getSizeNativeSkin(void *object, int32 offset);
+
+void instanceSkinData(Geometry *g, Mesh *m, Skin *skin, uint32 *data);
+
+void skinPreCB(MatPipeline*, Geometry*);
+void skinPostCB(MatPipeline*, Geometry*);
+
+}
+}