diff options
| author | claude-bot <[email protected]> | 2026-07-13 12:40:03 +0000 |
|---|---|---|
| committer | claude-bot <[email protected]> | 2026-07-13 12:40:03 +0000 |
| commit | 847962910f0bff071f3bf07c9abb87764fb6cac3 (patch) | |
| tree | ddcd429e134c7fd5f72ddc97ced175de8d66fcd0 /src/ps2 | |
| download | librw-847962910f0bff071f3bf07c9abb87764fb6cac3.tar.gz librw-847962910f0bff071f3bf07c9abb87764fb6cac3.zip | |
Snapshot for re3/reVC vendoring, per @lzcnt.
Source: https://github.com/aap/librw (master).
Diffstat (limited to 'src/ps2')
| -rw-r--r-- | src/ps2/pds.cpp | 203 | ||||
| -rw-r--r-- | src/ps2/ps2.cpp | 1583 | ||||
| -rw-r--r-- | src/ps2/ps2device.cpp | 49 | ||||
| -rw-r--r-- | src/ps2/ps2matfx.cpp | 68 | ||||
| -rw-r--r-- | src/ps2/ps2raster.cpp | 2238 | ||||
| -rw-r--r-- | src/ps2/ps2skin.cpp | 334 | ||||
| -rw-r--r-- | src/ps2/rwps2.h | 281 | ||||
| -rw-r--r-- | src/ps2/rwps2impl.h | 16 | ||||
| -rw-r--r-- | src/ps2/rwps2plg.h | 27 |
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*); + +} +} |
