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/d3d/xbox.cpp | |
| download | librw-master.tar.gz librw-master.zip | |
Snapshot for re3/reVC vendoring, per @lzcnt.
Source: https://github.com/aap/librw (master).
Diffstat (limited to 'src/d3d/xbox.cpp')
| -rw-r--r-- | src/d3d/xbox.cpp | 1005 |
1 files changed, 1005 insertions, 0 deletions
diff --git a/src/d3d/xbox.cpp b/src/d3d/xbox.cpp new file mode 100644 index 0000000..62fab0c --- /dev/null +++ b/src/d3d/xbox.cpp @@ -0,0 +1,1005 @@ +#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 "rwxbox.h" + +#include "rwxboximpl.h" + +#define PLUGIN_ID 2 + +namespace rw { +namespace xbox { + +static void* +driverOpen(void *o, int32, int32) +{ + engine->driver[PLATFORM_XBOX]->defaultPipeline = makeDefaultPipeline(); + + engine->driver[PLATFORM_XBOX]->rasterNativeOffset = nativeRasterOffset; + engine->driver[PLATFORM_XBOX]->rasterCreate = rasterCreate; + engine->driver[PLATFORM_XBOX]->rasterLock = rasterLock; + engine->driver[PLATFORM_XBOX]->rasterUnlock = rasterUnlock; + engine->driver[PLATFORM_XBOX]->rasterNumLevels = rasterNumLevels; + engine->driver[PLATFORM_XBOX]->rasterToImage = rasterToImage; + + return o; +} + +static void* +driverClose(void *o, int32, int32) +{ + return o; +} + +void +registerPlatformPlugins(void) +{ + Driver::registerPlugin(PLATFORM_XBOX, 0, PLATFORM_XBOX, + driverOpen, driverClose); + registerNativeRaster(); +} + +void* +destroyNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_XBOX) + return object; + InstanceDataHeader *header = + (InstanceDataHeader*)geometry->instData; + geometry->instData = nil; + rwFree(header->vertexBuffer); + rwFree(header->begin); + rwFree(header->data); + rwFree(header); + return object; +} + +Stream* +readNativeData(Stream *stream, int32, void *object, int32, int32) +{ + ASSERTLITTLE; + Geometry *geometry = (Geometry*)object; + uint32 vers; + uint32 platform; + if(!findChunk(stream, ID_STRUCT, nil, &vers)){ + RWERROR((ERR_CHUNK, "STRUCT")); + return nil; + } + platform = stream->readU32(); + if(platform != PLATFORM_XBOX){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + if(vers < 0x35000){ + RWERROR((ERR_VERSION, vers)); + return nil; + } + InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY); + geometry->instData = header; + header->platform = PLATFORM_XBOX; + + int32 size = stream->readI32(); + // The 0x18 byte are the resentryheader. + // We don't have it but it's used for alignment. + header->data = rwNewT(uint8, size + 0x18, MEMDUR_EVENT | ID_GEOMETRY); + uint8 *p = header->data+0x18+4; + stream->read8(p, size-4); + + header->size = size; + header->serialNumber = *(uint16*)p; p += 2; + header->numMeshes = *(uint16*)p; p += 2; + header->primType = *(uint32*)p; p += 4; + header->numVertices = *(uint32*)p; p += 4; + header->stride = *(uint32*)p; p += 4; + // RxXboxVertexFormat in 3.3 here + p += 4; // skip vertexBuffer pointer + header->vertexAlpha = *(bool32*)p; p += 4; + p += 8; // skip begin, end pointers + + InstanceData *inst = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY); + header->begin = inst; + for(int i = 0; i < header->numMeshes; i++){ + inst->minVert = *(uint32*)p; p += 4; + inst->numVertices = *(int32*)p; p += 4; + inst->numIndices = *(int32*)p; p += 4; + inst->indexBuffer = header->data + *(uint32*)p; p += 4; + p += 8; // skip material and vertexShader + inst->vertexShader = 0; + // pixelShader in 3.3 here + inst++; + } + header->end = inst; + + header->vertexBuffer = rwNewT(uint8, header->stride*header->numVertices, MEMDUR_EVENT | ID_GEOMETRY); + stream->read8(header->vertexBuffer, header->stride*header->numVertices); + 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_XBOX) + return stream; + stream->writeU32(PLATFORM_XBOX); + assert(rw::version >= 0x35000 && "can't write native Xbox data < 0x35000"); + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + + // we just fill header->data and write that + uint8 *p = header->data+0x18; + *(int32*)p = header->size; p += 4; + *(uint16*)p = header->serialNumber; p += 2; + *(uint16*)p = header->numMeshes; p += 2; + *(uint32*)p = header->primType; p += 4; + *(uint32*)p = header->numVertices; p += 4; + *(uint32*)p = header->stride; p += 4; + // RxXboxVertexFormat in 3.3 here + p += 4; // skip vertexBuffer pointer + *(bool32*)p = header->vertexAlpha; p += 4; + p += 8; // skip begin, end pointers + + InstanceData *inst = header->begin; + for(int i = 0; i < header->numMeshes; i++){ + *(uint32*)p = inst->minVert; p += 4; + *(int32*)p = inst->numVertices; p += 4; + *(int32*)p = inst->numIndices; p += 4; + *(uint32*)p = (uint8*)inst->indexBuffer - header->data; p += 4; + p += 8; // skip material and vertexShader + // pixelShader in 3.3 here + inst++; + } + + stream->write8(header->data+0x18, header->size); + stream->write8(header->vertexBuffer, header->stride*header->numVertices); + return stream; +} + +int32 +getSizeNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_XBOX) + return 0; + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + return 12 + 4 + header->size + header->stride*header->numVertices; +} + +void +registerNativeDataPlugin(void) +{ + Geometry::registerPlugin(0, ID_NATIVEDATA, + nil, destroyNativeData, nil); + Geometry::registerPluginStream(ID_NATIVEDATA, + readNativeData, + writeNativeData, + getSizeNativeData); +} + +enum { + D3DPT_TRIANGLELIST = 5, + D3DPT_TRIANGLESTRIP = 6 +}; + +static void +instance(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + // TODO: allow for REINSTANCE (or not, xbox can't render) + if(geo->instData) + return; + InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY); + MeshHeader *meshh = geo->meshHeader; + geo->instData = header; + header->platform = PLATFORM_XBOX; + + header->size = 0x24 + meshh->numMeshes*0x18 + 0x10; + Mesh *mesh = meshh->getMeshes(); + for(uint32 i = 0; i < meshh->numMeshes; i++) + header->size += (mesh++->numIndices*2 + 0xF) & ~0xF; + // The 0x18 byte are the resentryheader. + // We don't have it but it's used for alignment. + header->data = rwNewT(uint8, header->size + 0x18, MEMDUR_EVENT | ID_GEOMETRY); + header->serialNumber = meshh->serialNum; + header->numMeshes = meshh->numMeshes; + header->primType = meshh->flags == MeshHeader::TRISTRIP ? + D3DPT_TRIANGLESTRIP : D3DPT_TRIANGLELIST; + header->numVertices = geo->numVertices; + header->vertexAlpha = 0; + // set by the instanceCB + header->stride = 0; + header->vertexBuffer = nil; + + InstanceData *inst = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY); + header->begin = inst; + mesh = meshh->getMeshes(); + uint8 *indexbuf = (uint8*)header->data + ((0x18 + 0x24 + header->numMeshes*0x18 + 0xF)&~0xF); + for(uint32 i = 0; i < header->numMeshes; i++){ + findMinVertAndNumVertices(mesh->indices, mesh->numIndices, + &inst->minVert, &inst->numVertices); + inst->numIndices = mesh->numIndices; + inst->indexBuffer = indexbuf; + memcpy(inst->indexBuffer, mesh->indices, inst->numIndices*sizeof(uint16)); + indexbuf += (inst->numIndices*2 + 0xF) & ~0xF; + inst->material = mesh->material; + inst->vertexShader = 0; // TODO? + mesh++; + inst++; + } + header->end = inst; + + pipe->instanceCB(geo, header); +} + +static void +uninstance(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_XBOX); + + InstanceDataHeader *header = (InstanceDataHeader*)geo->instData; + InstanceData *inst = header->begin; + Mesh *mesh = geo->meshHeader->getMeshes(); + // For some reason numIndices in mesh and instance data are not always equal + // And primitive isn't always correct either. Maybe some internal conversion... + geo->meshHeader->totalIndices = 0; + for(uint32 i = 0; i < header->numMeshes; i++){ + mesh[i].numIndices = inst[i].numIndices; + geo->meshHeader->totalIndices += mesh[i].numIndices; + } + geo->meshHeader->flags = header->primType == D3DPT_TRIANGLESTRIP ? + MeshHeader::TRISTRIP : 0; + + geo->numTriangles = geo->meshHeader->guessNumTriangles(); + geo->allocateData(); + geo->allocateMeshes(geo->meshHeader->numMeshes, geo->meshHeader->totalIndices, 0); + + mesh = geo->meshHeader->getMeshes(); + for(uint32 i = 0; i < header->numMeshes; i++){ + uint16 *indices = (uint16*)inst->indexBuffer; + memcpy(mesh->indices, indices, inst->numIndices*2); + mesh++; + inst++; + } + + pipe->uninstanceCB(geo, header); + geo->generateTriangles(); + geo->flags &= ~Geometry::NATIVE; + destroyNativeData(geo, 0, 0); +} + +void +ObjPipeline::init(void) +{ + this->rw::ObjPipeline::init(PLATFORM_XBOX); + this->impl.instance = xbox::instance; + this->impl.uninstance = xbox::uninstance; + this->instanceCB = nil; + this->uninstanceCB = nil; +} + +ObjPipeline* +ObjPipeline::create(void) +{ + ObjPipeline *pipe = rwNewT(ObjPipeline, 1, MEMDUR_GLOBAL); + pipe->init(); + return pipe; +} + +int v3dFormatMap[] = { + -1, VERT_BYTE3, VERT_SHORT3, VERT_NORMSHORT3, VERT_COMPNORM, VERT_FLOAT3 +}; + +int v2dFormatMap[] = { + -1, VERT_BYTE2, VERT_SHORT2, VERT_NORMSHORT2, VERT_COMPNORM, VERT_FLOAT2 +}; + +void +defaultInstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + uint32 *vertexFmt = getVertexFmt(geo); + if(*vertexFmt == 0) + *vertexFmt = makeVertexFmt(geo->flags, geo->numTexCoordSets); + header->stride = getVertexFmtStride(*vertexFmt); + header->vertexBuffer = rwNewT(uint8, header->stride*header->numVertices, MEMDUR_EVENT | ID_GEOMETRY); + uint8 *dst = (uint8*)header->vertexBuffer; + + uint32 fmt = *vertexFmt; + uint32 sel = fmt & 0xF; + instV3d(v3dFormatMap[sel], dst, geo->morphTargets[0].vertices, + header->numVertices, header->stride); + dst += sel == 4 ? 4 : 3*vertexFormatSizes[sel]; + + sel = (fmt >> 4) & 0xF; + if(sel){ + instV3d(v3dFormatMap[sel], dst, geo->morphTargets[0].normals, + header->numVertices, header->stride); + dst += sel == 4 ? 4 : 3*vertexFormatSizes[sel]; + } + + if(fmt & 0x1000000){ + header->vertexAlpha = instColor(VERT_ARGB, dst, geo->colors, + header->numVertices, header->stride); + dst += 4; + } + + for(int i = 0; i < 4; i++){ + sel = (fmt >> (i*4 + 8)) & 0xF; + if(sel == 0) + break; + instTexCoords(v2dFormatMap[sel], dst, geo->texCoords[i], + header->numVertices, header->stride); + dst += sel == 4 ? 4 : 2*vertexFormatSizes[sel]; + } + + if(fmt & 0xE000000) + assert(0 && "can't instance tangents or whatever it is"); +} + +void +defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + uint32 *vertexFmt = getVertexFmt(geo); + uint32 fmt = *vertexFmt; + assert(fmt != 0); + uint8 *src = (uint8*)header->vertexBuffer; + + uint32 sel = fmt & 0xF; + uninstV3d(v3dFormatMap[sel], geo->morphTargets[0].vertices, src, + header->numVertices, header->stride); + src += sel == 4 ? 4 : 3*vertexFormatSizes[sel]; + + sel = (fmt >> 4) & 0xF; + if(sel){ + uninstV3d(v3dFormatMap[sel], geo->morphTargets[0].normals, src, + header->numVertices, header->stride); + src += sel == 4 ? 4 : 3*vertexFormatSizes[sel]; + } + + if(fmt & 0x1000000){ + uninstColor(VERT_ARGB, geo->colors, src, + header->numVertices, header->stride); + src += 4; + } + + for(int i = 0; i < 4; i++){ + sel = (fmt >> (i*4 + 8)) & 0xF; + if(sel == 0) + break; + uninstTexCoords(v2dFormatMap[sel], geo->texCoords[i], src, + header->numVertices, header->stride); + src += sel == 4 ? 4 : 2*vertexFormatSizes[sel]; + } +} + +ObjPipeline* +makeDefaultPipeline(void) +{ + ObjPipeline *pipe = ObjPipeline::create(); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + return pipe; +} + +// Native Texture and Raster + +int32 nativeRasterOffset; + +static uint32 +calculateTextureSize(uint32 width, uint32 height, uint32 depth, uint32 format) +{ +#define D3DFMT_W11V11U10 65 + switch(format){ + default: + case D3DFMT_UNKNOWN: + return 0; + case D3DFMT_A8: + case D3DFMT_P8: + case D3DFMT_L8: + case D3DFMT_AL8: + case D3DFMT_LIN_A8: + case D3DFMT_LIN_AL8: + case D3DFMT_LIN_L8: + return width * height * depth; + case D3DFMT_R5G6B5: + case D3DFMT_R6G5B5: + case D3DFMT_X1R5G5B5: + case D3DFMT_A1R5G5B5: + case D3DFMT_A4R4G4B4: + case D3DFMT_R4G4B4A4: + case D3DFMT_R5G5B5A1: + case D3DFMT_R8B8: + case D3DFMT_G8B8: + case D3DFMT_A8L8: + case D3DFMT_L16: + //case D3DFMT_V8U8: + //case D3DFMT_L6V5U5: + case D3DFMT_D16_LOCKABLE: + //case D3DFMT_D16: + case D3DFMT_F16: + case D3DFMT_YUY2: + case D3DFMT_UYVY: + case D3DFMT_LIN_A1R5G5B5: + case D3DFMT_LIN_A4R4G4B4: + case D3DFMT_LIN_G8B8: + case D3DFMT_LIN_R4G4B4A4: + case D3DFMT_LIN_R5G5B5A1: + case D3DFMT_LIN_R5G6B5: + case D3DFMT_LIN_R6G5B5: + case D3DFMT_LIN_R8B8: + case D3DFMT_LIN_X1R5G5B5: + case D3DFMT_LIN_A8L8: + case D3DFMT_LIN_L16: + //case D3DFMT_LIN_V8U8: + //case D3DFMT_LIN_L6V5U5: + case D3DFMT_LIN_D16: + case D3DFMT_LIN_F16: + return width * 2 * height * depth; + case D3DFMT_A8R8G8B8: + case D3DFMT_X8R8G8B8: + case D3DFMT_A8B8G8R8: + case D3DFMT_B8G8R8A8: + case D3DFMT_R8G8B8A8: + //case D3DFMT_X8L8V8U8: + //case D3DFMT_Q8W8V8U8: + case D3DFMT_V16U16: + case D3DFMT_D24S8: + case D3DFMT_F24S8: + case D3DFMT_LIN_A8B8G8R8: + case D3DFMT_LIN_A8R8G8B8: + case D3DFMT_LIN_B8G8R8A8: + case D3DFMT_LIN_R8G8B8A8: + case D3DFMT_LIN_X8R8G8B8: + case D3DFMT_LIN_V16U16: + //case D3DFMT_LIN_X8L8V8U8: + //case D3DFMT_LIN_Q8W8V8U8: + case D3DFMT_LIN_D24S8: + case D3DFMT_LIN_F24S8: + return width * 4 * height * depth; + case D3DFMT_DXT1: + assert(depth <= 1); + return ((width + 3) >> 2) * ((height + 3) >> 2) * 8; + //case D3DFMT_DXT2: + case D3DFMT_DXT3: + //case D3DFMT_DXT4: + case D3DFMT_DXT5: + assert(depth <= 1); + return ((width + 3) >> 2) * ((height + 3) >> 2) * 16; + } +} + +static void* +createTexture(int32 width, int32 height, int32 numlevels, uint32 format) +{ + int32 w = width; + int32 h = height; + int32 size = 0; + for(int32 i = 0; i < numlevels; i++){ + size += calculateTextureSize(w, h, 1, format); + w /= 2; + if(w == 0) w = 1; + h /= 2; + if(h == 0) h = 1; + } + size = (size+3)&~3; + uint8 *data = (uint8*)rwNew(sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(numlevels-1)+size, + MEMDUR_EVENT | ID_DRIVER); + RasterLevels *levels = (RasterLevels*)data; + data += sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(numlevels-1); + levels->numlevels = numlevels; + levels->format = format; + w = width; + h = height; + for(int32 i = 0; i < numlevels; i++){ + levels->levels[i].width = w; + levels->levels[i].height = h; + levels->levels[i].data = data; + levels->levels[i].size = calculateTextureSize(w, h, 1, format); + data += levels->levels[i].size; + w /= 2; + if(w == 0) w = 1; + h /= 2; + if(h == 0) h = 1; + } + return levels; +} + +struct RasterFormatInfo +{ + uint32 d3dformat; + int32 depth; + bool32 hasAlpha; + uint32 rwFormat; +}; + +// indexed directly by RW format +static RasterFormatInfo formatInfoRW[16] = { + { 0, 0, 0, 0}, + { D3DFMT_A1R5G5B5, 16, 1, Raster::C1555 }, + { D3DFMT_R5G6B5, 16, 0, Raster::C565 }, + { D3DFMT_A4R4G4B4, 16, 1, Raster::C4444 }, + { D3DFMT_L8, 8, 0, Raster::LUM8 }, + { D3DFMT_A8R8G8B8, 32, 1, Raster::C8888 }, + { D3DFMT_X8R8G8B8, 32, 0, Raster::C888 }, + { D3DFMT_UNKNOWN, 16, 0, Raster::D16 }, + { D3DFMT_UNKNOWN, 32, 0, Raster::D24 }, + { D3DFMT_UNKNOWN, 32, 0, Raster::D32 }, + { D3DFMT_X1R5G5B5, 16, 0, Raster::C555 } +}; + +static void +rasterSetFormat(Raster *raster) +{ + assert(raster->format != 0); // no default yet + + XboxRaster *natras = GETXBOXRASTEREXT(raster); + if(raster->format & (Raster::PAL4 | Raster::PAL8)){ + natras->format = D3DFMT_P8; + raster->depth = 8; + }else{ + natras->format = formatInfoRW[(raster->format >> 8) & 0xF].d3dformat; + raster->depth = formatInfoRW[(raster->format >> 8) & 0xF].depth; + } + natras->bpp = raster->depth/8; + natras->hasAlpha = formatInfoRW[(raster->format >> 8) & 0xF].hasAlpha; + raster->stride = raster->width&natras->bpp; +} + +static Raster* +rasterCreateTexture(Raster *raster) +{ + XboxRaster *natras = GETXBOXRASTEREXT(raster); + int32 levels; + + if(natras->format == D3DFMT_P8) + natras->palette = (uint8*)rwNew(4*256, MEMDUR_EVENT | ID_DRIVER); + levels = Raster::calculateNumLevels(raster->width, raster->height); + assert(natras->texture == nil); + natras->texture = createTexture(raster->width, raster->height, + raster->format & Raster::MIPMAP ? levels : 1, + natras->format); + if(natras->texture == nil){ + RWERROR((ERR_NOTEXTURE)); + return nil; + } + return raster; +} + +Raster* +rasterCreate(Raster *raster) +{ + rasterSetFormat(raster); + + Raster *ret = raster; + + // Dummy to use as subraster + if(raster->width == 0 || raster->height == 0){ + raster->flags |= Raster::DONTALLOCATE; + raster->stride = 0; + goto ret; + } + if(raster->flags & Raster::DONTALLOCATE) + goto ret; + + switch(raster->type){ + case Raster::NORMAL: + case Raster::TEXTURE: + ret = rasterCreateTexture(raster); + break; + default: + RWERROR((ERR_INVRASTER)); + return nil; + } + +ret: + raster->originalWidth = raster->width; + raster->originalHeight = raster->height; + raster->originalStride = raster->stride; + raster->originalPixels = raster->pixels; + return ret; +} + +uint8* +rasterLock(Raster *raster, int32 level, int32 lockMode) +{ + XboxRaster *natras = GETXBOXRASTEREXT(raster); + + // check if already locked + if(raster->privateFlags & (Raster::PRIVATELOCK_READ|Raster::PRIVATELOCK_WRITE)) + return nil; + + RasterLevels *levels = (RasterLevels*)natras->texture; + raster->pixels = levels->levels[level].data; + raster->width = levels->levels[level].width; + raster->height = levels->levels[level].height; + raster->stride = raster->width*natras->bpp; + + 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) +{ + raster->width = raster->originalWidth; + raster->height = raster->originalHeight; + raster->stride = raster->originalStride; + raster->pixels = raster->originalPixels; + + raster->privateFlags &= ~(Raster::PRIVATELOCK_READ|Raster::PRIVATELOCK_WRITE); +} + +int32 +rasterNumLevels(Raster *raster) +{ + XboxRaster *natras = GETXBOXRASTEREXT(raster); + RasterLevels *levels = (RasterLevels*)natras->texture; + return levels->numlevels; +} + +static void +unswizzle(uint8 *dst, uint8 *src, int32 w, int32 h, int32 bpp) +{ + uint32 maskU = 0; + uint32 maskV = 0; + int32 i = 1; + int32 j = 1; + int32 c; + do{ + c = 0; + if(i < w){ + maskU |= j; + j <<= 1; + c = j; + } + if(i < h){ + maskV |= j; + j <<= 1; + c = j; + } + i <<= 1; + }while(c); + int32 x, y, u, v; + v = 0; + for(y = 0; y < h; y++){ + u = 0; + for(x = 0; x < w; x++){ + memcpy(&dst[(y*w + x)*bpp], &src[(u|v)*bpp], bpp); + u = (u - maskU) & maskU; + } + v = (v - maskV) & maskV; + } +} + +Image* +rasterToImage(Raster *raster) +{ + int32 depth; + Image *image; + + bool unlock = false; + if(raster->pixels == nil){ + raster->lock(0, Raster::LOCKREAD); + unlock = true; + } + + XboxRaster *natras = GETXBOXRASTEREXT(raster); + if(natras->customFormat){ + int w = raster->width; + int h = raster->height; + // pixels are in the upper right corner + if(w < 4) w = 4; + if(h < 4) h = 4; + image = Image::create(w, h, 32); + image->allocate(); + uint8 *pix = raster->pixels; + switch(natras->format){ + case D3DFMT_DXT1: + image->setPixelsDXT(1, pix); + // TODO: is this correct? + if(!natras->hasAlpha) + image->removeMask(); + break; + case D3DFMT_DXT3: + image->setPixelsDXT(3, pix); + break; + case D3DFMT_DXT5: + image->setPixelsDXT(5, pix); + break; + default: + assert(0 && "unknown format"); + image->destroy(); + if(unlock) + raster->unlock(0); + return nil; + } + // fix it up again + image->width = raster->width; + image->height = raster->height; + + if(unlock) + raster->unlock(0); + return image; + } + + switch(raster->format & 0xF00){ + 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 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; + } + + image = Image::create(raster->width, raster->height, depth); + image->allocate(); + + if(pallength){ + uint8 *out = image->palette; + uint8 *in = (uint8*)natras->palette; + // bytes are BGRA unlike regular d3d! + for(int32 i = 0; i < pallength; i++){ + conv_BGRA8888_from_RGBA8888(out, in); + in += 4; + out += 4; + } + } + + uint8 *imgpixels = image->pixels; + uint8 *pixels = raster->pixels; + + // NB: + assert(image->bpp == (int)natras->bpp); + assert(image->stride == raster->stride); + unswizzle(imgpixels, pixels, image->width, image->height, image->bpp); + // Fix RGB order + uint8 tmp; + if(depth > 8) + for(int32 y = 0; y < image->height; y++){ + uint8 *imgrow = imgpixels; + // uint8 *rasrow = pixels; + for(int32 x = 0; x < image->width; x++){ + switch(raster->format & 0xF00){ + case Raster::C8888: + case Raster::C888: + tmp = imgrow[0]; + imgrow[0] = imgrow[2]; + imgrow[2] = tmp; + imgrow += image->bpp; + break; + } + } + imgpixels += image->stride; + // pixels += raster->stride; + } + image->compressPalette(); + + if(unlock) + raster->unlock(0); + + return image; +} + +int32 +getLevelSize(Raster *raster, int32 level) +{ + XboxRaster *ras = GETXBOXRASTEREXT(raster); + RasterLevels *levels = (RasterLevels*)ras->texture; + return levels->levels[level].size; +} + +static void* +createNativeRaster(void *object, int32 offset, int32) +{ + XboxRaster *raster = PLUGINOFFSET(XboxRaster, object, offset); + raster->texture = nil; + raster->palette = nil; + raster->format = 0; + raster->hasAlpha = 0; + raster->customFormat = 0; + raster->unknownFlag = 0; + return object; +} + +static void* +destroyNativeRaster(void *object, int32, int32) +{ + // TODO: + return object; +} + +static void* +copyNativeRaster(void *dst, void *, int32 offset, int32) +{ + XboxRaster *raster = PLUGINOFFSET(XboxRaster, dst, offset); + raster->texture = nil; + raster->palette = nil; + raster->format = 0; + raster->hasAlpha = 0; + raster->unknownFlag = 0; + return dst; +} + +void +registerNativeRaster(void) +{ + nativeRasterOffset = Raster::registerPlugin(sizeof(XboxRaster), + ID_RASTERXBOX, + createNativeRaster, + destroyNativeRaster, + copyNativeRaster); +} + +Texture* +readNativeTexture(Stream *stream) +{ + uint32 vers, platform; + if(!findChunk(stream, ID_STRUCT, nil, &vers)){ + RWERROR((ERR_CHUNK, "STRUCT")); + return nil; + } + platform = stream->readU32(); + if(platform != PLATFORM_XBOX){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + if(vers < 0x34001){ + RWERROR((ERR_VERSION, vers)); + return nil; + } + Texture *tex = Texture::create(nil); + if(tex == nil) + return nil; + + + // Texture + tex->filterAddressing = stream->readU32(); + stream->read8(tex->name, 32); + stream->read8(tex->mask, 32); + +//if(strcmp(tex->name, "bluallu") == 0) +//__debugbreak(); + + // Raster + int32 format = stream->readI32(); + bool32 hasAlpha = stream->readI16(); + bool32 unknownFlag = stream->readI16(); + int32 width = stream->readU16(); + int32 height = stream->readU16(); + int32 depth = stream->readU8(); + int32 numLevels = stream->readU8(); + int32 type = stream->readU8(); + int32 compression = stream->readU8(); + int32 totalSize = stream->readI32(); + + // isn't this the cube map flag? + assert(unknownFlag == 0); + Raster *raster; + if(compression){ + raster = Raster::create(width, height, depth, format | type | Raster::DONTALLOCATE, PLATFORM_XBOX); + XboxRaster *ras = GETXBOXRASTEREXT(raster); + ras->format = compression; + ras->hasAlpha = hasAlpha; + ras->texture = createTexture(raster->width, raster->height, + raster->format & Raster::MIPMAP ? numLevels : 1, + ras->format); + ras->customFormat = 1; + raster->flags &= ~Raster::DONTALLOCATE; + }else + raster = Raster::create(width, height, depth, format | type, PLATFORM_XBOX); + XboxRaster *ras = GETXBOXRASTEREXT(raster); + tex->raster = raster; + + if(raster->format & Raster::PAL4) + stream->read8(ras->palette, 4*32); + else if(raster->format & Raster::PAL8) + stream->read8(ras->palette, 4*256); + + // exploit the fact that mipmaps are allocated consecutively + uint8 *data = raster->lock(0, Raster::LOCKWRITE|Raster::LOCKNOFETCH); + stream->read8(data, totalSize); + raster->unlock(0); + + return tex; +} + +void +writeNativeTexture(Texture *tex, Stream *stream) +{ + int32 chunksize = getSizeNativeTexture(tex); + writeChunkHeader(stream, ID_STRUCT, chunksize-12); + stream->writeU32(PLATFORM_XBOX); + + // Texture + stream->writeU32(tex->filterAddressing); + stream->write8(tex->name, 32); + stream->write8(tex->mask, 32); + + // Raster + Raster *raster = tex->raster; + XboxRaster *ras = GETXBOXRASTEREXT(raster); + int32 numLevels = raster->getNumLevels(); + stream->writeI32(raster->format); + stream->writeI16(ras->hasAlpha); + stream->writeI16(ras->unknownFlag); + stream->writeU16(raster->width); + stream->writeU16(raster->height); + stream->writeU8(raster->depth); + stream->writeU8(numLevels); + stream->writeU8(raster->type); + stream->writeU8(ras->format); + + int32 totalSize = 0; + for(int32 i = 0; i < numLevels; i++) + totalSize += getLevelSize(tex->raster, i); + totalSize = (totalSize+3)&~3; + stream->writeI32(totalSize); + + if(raster->format & Raster::PAL4) + stream->write8(ras->palette, 4*32); + else if(raster->format & Raster::PAL8) + stream->write8(ras->palette, 4*256); + + // exploit the fact that mipmaps are allocated consecutively + uint8 *data = raster->lock(0, Raster::LOCKREAD); + stream->write8(data, totalSize); + raster->unlock(0); +} + +uint32 +getSizeNativeTexture(Texture *tex) +{ + uint32 size = 12 + 72 + 16 + 4; + int32 levels = tex->raster->getNumLevels(); + for(int32 i = 0; i < levels; i++) + size += getLevelSize(tex->raster, i); + size = (size+3)&~3; + if(tex->raster->format & Raster::PAL4) + size += 4*32; + else if(tex->raster->format & Raster::PAL8) + size += 4*256; + return size; +} + +} +} |
