#include #include #include #include #include "../rwbase.h" #include "../rwerror.h" #include "../rwplg.h" #include "../rwpipeline.h" #include "../rwobjects.h" #include "../rwengine.h" #include "rwps2.h" #define PLUGIN_ID 0 #define max(a, b) ((a) > (b) ? (a) : (b)) namespace rw { namespace ps2 { int32 nativeRasterOffset; #define MAXLEVEL(r) ((r)->tex1low >> 2) static bool32 noNewStyleRasters; // i don't really understand this, stolen from RW static void ps2MinSize(int32 psm, int32 flags, int32 *minw, int32 *minh) { *minh = 1; switch(psm){ case 0x00: case 0x30: *minw = 2; // 32 bit break; case 0x02: case 0x0A: case 0x32: case 0x3A: *minw = 4; // 16 bit break; case 0x01: case 0x13: case 0x14: case 0x1B: case 0x24: case 0x2C: case 0x31: *minw = 8; // everything else break; } if(flags & 0x2 && psm == 0x13){ // PSMT8 *minw = 16; *minh = 4; } if(flags & 0x4 && psm == 0x14){ // PSMT4 *minw = 32; *minh = 4; } } #define ALIGN16(x) ((x) + 0xF & ~0xF) #define ALIGN64(x) ((x) + 0x3F & ~0x3F) enum Psm { PSMCT32 = 0x0, PSMCT24 = 0x1, PSMCT16 = 0x2, PSMCT16S = 0xA, PSMT8 = 0x13, PSMT4 = 0x14, PSMT8H = 0x1B, PSMT4HL = 0x24, PSMT4HH = 0x2C, }; void* mallocalign(size_t size, int32 alignment) { void *p; void **pp; p = malloc(size + alignment + sizeof(void*)); 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]; free(pp); } void rasterCreate(Raster *raster) { enum { TCC_RGBA = 1 << 2, CLD_1 = 1 << 29, }; uint64 bufferWidth[7], bufferBase[7]; int32 pageWidth, pageHeight; Ps2Raster *ras = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset); if(raster->flags & Raster::DONTALLOCATE) return; //printf("%x %x %x %x\n", raster->format, raster->flags, raster->type, noNewStyleRasters); assert(raster->type == Raster::TEXTURE); switch(raster->depth){ case 4: pageWidth = 128; pageHeight = 128; break; case 8: pageWidth = 128; pageHeight = 64; break; case 16: pageWidth = 64; pageHeight = 64; break; case 32: pageWidth = 64; pageHeight = 32; break; default: assert(0 && "unsupported depth"); } int32 logw = 0, logh = 0; int32 s; for(s = 1; s < raster->width; s *= 2) logw++; for(s = 1; s < raster->height; s *= 2) logh++; ras->kl = 0xFC0; //printf("%d %d %d %d\n", raster->width, logw, raster->height, logh); // round up to page width, set TBW, TW, TH ras->tex0[0] |= (raster->width < pageWidth ? pageWidth : raster->width)/64 << 14; ras->tex0[0] |= logw << 26; ras->tex0[0] |= logh << 30; ras->tex0[1] |= logh >> 2; // set PSM, TCC, CLD, CPSM and figure out palette format int32 paletteWidth, paletteHeight, paletteDepth; int32 palettePagewidth, palettePageheight; if(raster->format & (Raster::PAL4 | Raster::PAL8)) switch(raster->format & 0xF00){ case Raster::C1555: ras->tex0[1] |= PSMCT16S << 19; paletteDepth = 2; palettePagewidth = palettePageheight = 64; break; case Raster::C8888: ras->tex0[1] |= PSMCT32 << 19; paletteDepth = 4; palettePagewidth = 64; palettePageheight = 32; break; default: assert(0 && "unsupported palette format\n"); } if(raster->format & Raster::PAL4){ ras->tex0[0] |= PSMT4 << 20; ras->tex0[1] |= CLD_1 | TCC_RGBA; paletteWidth = 8; paletteHeight = 2; }else if(raster->format & Raster::PAL8){ ras->tex0[0] |= PSMT8 << 20; ras->tex0[1] |= CLD_1 | TCC_RGBA; paletteWidth = paletteHeight = 16; }else{ paletteWidth = 0; paletteHeight = 0; paletteDepth = 0; palettePagewidth = 0; palettePageheight = 0; switch(raster->format & 0xF00){ case Raster::C1555: ras->tex0[0] |= PSMCT16S << 20; ras->tex0[1] |= TCC_RGBA; break; case Raster::C8888: ras->tex0[0] |= PSMCT32 << 20; ras->tex0[1] |= TCC_RGBA; break; case Raster::C888: ras->tex0[0] |= PSMCT24 << 20; break; default: assert(0 && "unsupported raster format\n"); } } for(int i = 0; i < 7; i++){ bufferWidth[i] = 1; bufferBase[i] = 0; } int32 mipw, miph; int32 n; int32 nPagW, nPagH; int32 w = raster->width; int32 h = raster->height; int32 d = raster->depth; raster->stride = w*d/8; if(raster->format & Raster::MIPMAP){ 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 = w; miph = h; lastBufferWidth = max(pageWidth, w)/64; ras->texelSize = 0; int32 gsoffset = 0; int32 gsaddress = 0; for(n = 0; n < 7; n++){ if(w >= 8 && h >= 8 && (mipw < 8 || miph < 8)) break; ras->texelSize += ALIGN64(mipw*miph*d/8); bufferWidth[n] = max(pageWidth, mipw)/64; if(bufferWidth[n] != lastBufferWidth){ nPagW = ((w >> n-1) + pageWidth-1)/pageWidth; nPagH = ((h >> n-1) + pageHeight-1)/pageHeight; gsaddress = (gsoffset + nPagW*nPagH*0x800) & ~0x7FF; } lastBufferWidth = bufferWidth[n]; gsaddress = ALIGN64(gsaddress); uint32 b = gsaddress/256 & 7; switch(ras->tex0[0]>>20 & 0x3F){ case 0: case 1: case 0x13: b = blockOffset32_24_8[b]; break; case 2: case 0x14: b = blockOffset16_4[b]; break; case 0xA: b = blockOffset16S[b]; break; default: // can't happen break; } bufferBase[n] = b + (gsaddress>>11 << 5); int32 stride = bufferWidth[n]/64*d/8; gsaddress = ALIGN64(miph*stride/4 + gsoffset); mipw /= 2; miph /= 2; } assert(0); }else{ // No mipmaps ras->texelSize = ALIGN16(raster->stride*raster->height); ras->paletteSize = paletteWidth*paletteHeight*paletteDepth; ras->miptbp1[0] |= 1<<14; // TBW1 ras->miptbp1[1] |= 1<<2 | 1<<22; // TBW2,3 ras->miptbp2[0] |= 1<<14; // TBW4 ras->miptbp2[1] |= 1<<2 | 1<<22; // TBW5,6 ras->tex1low = 0; // one mipmap level // find out number of pages needed nPagW = (raster->width + pageWidth-1)/pageWidth; nPagH = (raster->height + pageHeight-1)/pageHeight; // calculate buffer width in units of pixels/64 bufferBase[0] = 0; bufferWidth[0] = nPagW*pageWidth / 64; // calculate whole buffer size in words ras->gsSize = nPagW*nPagH*0x800; // calculate palette offset on GS in units of words/64 if(ras->paletteSize){ // Maximum palette size will be 256 words. // If there is still room, use it! // TODO: find out why this check works if(pageWidth*nPagW > raster->width || pageHeight*nPagH > raster->height) ras->paletteOffset = (ras->gsSize - 256)/ 64; else{ // Otherwise allocate more space... ras->paletteOffset = ras->gsSize / 64; // ...using the same calculation as above nPagW = (paletteWidth + palettePagewidth-1)/palettePagewidth; nPagH = (paletteHeight + palettePageheight-1)/palettePageheight; ras->gsSize += nPagW*nPagH*0x800; } }else ras->paletteOffset = 0; } // allocate data and fill with GIF packets ras->texelSize = ALIGN16(ras->texelSize); 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->texelSize; uint8 *data = (uint8*)mallocalign(ras->dataSize, 0x40); ras->data = data; raster->texels = data; if(ras->paletteSize) raster->palette = data + ras->texelSize; if(raster->depth == 8) ras->flags |= Ps2Raster::SWIZZLED8; }else{ ras->flags |= Ps2Raster::HASGIFPACKETS; int32 psm = ras->tex0[0]>>20 & 0x3F; //int32 cpsm = ras->tex0[1]>>19 & 0x3F; if(psm == PSMT8){ ras->flags |= Ps2Raster::SWIZZLED8; // TODO: crazy stuff } if(psm == PSMT4){ // swizzle flag probably depends on version :/ // but which version? .... if(rw::version > 0x31000) ras->flags |= Ps2Raster::SWIZZLED4; // TODO: crazy stuff } ras->texelSize = 0x50*numLevels; // GIF packets int32 minW, minH; ps2MinSize(psm, ras->flags, &minW, &minH); w = raster->width; h = raster->height; n = numLevels; while(n--){ mipw = w < minW ? minW : w; miph = h < minH ? minH : h; ras->texelSize += 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; } // TODO: allocate space for more DMA packets // every upload as 4 qwords: // DMAcnt(2) [NOP, DIRECT] // GIF tag A+D // BITBLTBUF // DMAref(pixel data) [NOP, DIRECT] ras->dataSize = ras->paletteSize+ras->texelSize; uint8 *data = (uint8*)mallocalign(ras->dataSize, 0x40); assert(data); ras->data = data; raster->texels = data + 0x50; if(ras->paletteSize) raster->palette = data + ras->texelSize + 0x50; uint32 *p = (uint32*)data; w = raster->width; h = raster->height; for(n = 0; n < numLevels; n++){ mipw = w < minW ? minW : w; miph = h < minH ? minH : h; // GIF tag *p++ = 3; // NLOOP = 3 *p++ = 0x10000000; // NREG = 1 *p++ = 0xE; // A+D *p++ = 0; // TRXPOS *p++ = 0; // TODO *p++ = 0; // TODO *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 = mipw*miph*raster->depth/8 + 0xF >> 4; *p++ = sz; *p++ = 0x08000000; // IMAGE *p++ = 0; *p++ = 0; p += sz*4; w /= 2; h /= 2; } if(ras->paletteSize){ p = (uint32*)(raster->palette - 0x50); // GIF tag *p++ = 3; // NLOOP = 3 *p++ = 0x10000000; // NREG = 1 *p++ = 0xE; // A+D *p++ = 0; // TRXPOS *p++ = 0; // TODO *p++ = 0; // TODO *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 = ras->paletteSize - 0x50 + 0xF >> 4; *p++ = sz; *p++ = 0x08000000; // IMAGE *p++ = 0; *p++ = 0; } } } uint8* rasterLock(Raster *raster, int32 level) { // TODO (void)raster; (void)level; return nil; } void rasterUnlock(Raster *raster, int32 level) { // TODO (void)raster; (void)level; } int32 rasterNumLevels(Raster *raster) { Ps2Raster *ras = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset); if(raster->texels == nil) return 0; if(raster->format & Raster::MIPMAP) return MAXLEVEL(ras)+1; return 1; } static void* createNativeRaster(void *object, int32 offset, int32) { Ps2Raster *raster = PLUGINOFFSET(Ps2Raster, object, offset); raster->tex0[0] = 0; raster->tex0[1] = 0; raster->paletteOffset = 0; raster->kl = 0xFC0; raster->tex1low = 0; raster->unk2 = 0; raster->miptbp1[0] = 0; raster->miptbp1[1] = 0; raster->miptbp2[0] = 0; raster->miptbp2[1] = 0; raster->texelSize = 0; raster->paletteSize = 0; raster->gsSize = 0; raster->flags = 0; raster->dataSize = 0; raster->data = nil; return object; } static void* destroyNativeRaster(void *object, int32 offset, int32) { // TODO (void)offset; return object; } static void* copyNativeRaster(void *dst, void *src, int32 offset, int32) { Ps2Raster *dstraster = PLUGINOFFSET(Ps2Raster, dst, offset); Ps2Raster *srcraster = PLUGINOFFSET(Ps2Raster, src, offset); *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 = PLUGINOFFSET(Ps2Raster, tex->raster, offset); raster->kl = val; return stream; } static Stream* writeMipmap(Stream *stream, int32, void *object, int32 offset, int32) { Texture *tex = (Texture*)object; if(tex->raster) return nil; Ps2Raster *raster = PLUGINOFFSET(Ps2Raster, tex->raster, offset); 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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset); 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 type; uint32 tex0[2]; uint32 paletteOffset; uint32 tex1low; uint32 miptbp1[2]; uint32 miptbp2[2]; uint32 texelSize; uint32 paletteSize; uint32 gsSize; 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->read(tex->name, length); if(!findChunk(stream, ID_STRING, &length, nil)){ RWERROR((ERR_CHUNK, "STRING")); goto fail; } stream->read(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; } stream->read(&streamExt, 0x40); printf("%X %X %X %X %X %08X%08X %X %X %08X%08X %08X%08X %X %X %X %X\n", streamExt.width, streamExt.height, streamExt.depth, streamExt.rasterFormat, streamExt.type, streamExt.tex0[1], streamExt.tex0[0], streamExt.paletteOffset, streamExt.tex1low, streamExt.miptbp1[1], streamExt.miptbp1[0], streamExt.miptbp2[1], streamExt.miptbp2[0], streamExt.texelSize, streamExt.paletteSize, streamExt.gsSize, streamExt.mipmapVal); noNewStyleRasters = streamExt.type < 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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset); //printf("%X %X\n", natras->paletteOffset, natras->tex1low); // printf("%08X%08X %08X%08X %08X%08X\n", // natras->tex0[1], natras->tex0[0], // natras->miptbp1[0], natras->miptbp1[1], natras->miptbp2[0], natras->miptbp2[1]); // printTEX0(((uint64)natras->tex0[1] << 32) | natras->tex0[0]); uint64 tex1; calcTEX1(raster, &tex1, tex->filterAddressing & 0xF); // printTEX1(tex1); natras->tex0[0] = streamExt.tex0[0]; natras->tex0[1] = streamExt.tex0[1]; natras->paletteOffset = streamExt.paletteOffset; natras->tex1low = streamExt.tex1low; natras->miptbp1[0] = streamExt.miptbp1[0]; natras->miptbp1[1] = streamExt.miptbp1[1]; natras->miptbp2[0] = streamExt.miptbp2[0]; natras->miptbp2[1] = streamExt.miptbp2[1]; natras->texelSize = streamExt.texelSize; natras->paletteSize = streamExt.paletteSize; natras->gsSize = streamExt.gsSize; natras->kl = streamExt.mipmapVal; //printf("%X %X\n", natras->paletteOffset, natras->tex1low); // printf("%08X%08X %08X%08X %08X%08X\n", // natras->tex0[1], natras->tex0[0], // natras->miptbp1[0], natras->miptbp1[1], natras->miptbp2[0], natras->miptbp2[1]); // printTEX0(((uint64)natras->tex0[1] << 32) | natras->tex0[0]); calcTEX1(raster, &tex1, tex->filterAddressing & 0xF); // printTEX1(tex1); if(!findChunk(stream, ID_STRUCT, &length, nil)){ RWERROR((ERR_CHUNK, "STRUCT")); goto fail; } if(streamExt.type < 2){ stream->read(raster->texels, length); }else{ stream->read(raster->texels-0x50, natras->texelSize); stream->read(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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset); 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->write(tex->name, len); len = strlen(tex->mask)+4 & ~3; writeChunkHeader(stream, ID_STRING, len); stream->write(tex->mask, len); int32 sz = ras->texelSize + 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.type = 0; if(ras->flags == Ps2Raster::SWIZZLED8 && raster->depth == 8) streamExt.type = 1; if(ras->flags & Ps2Raster::HASGIFPACKETS) streamExt.type = 2; streamExt.tex0[0] = ras->tex0[0]; streamExt.tex0[1] = ras->tex0[1]; streamExt.paletteOffset = ras->paletteOffset; streamExt.tex1low = ras->tex1low; streamExt.miptbp1[0] = ras->miptbp1[0]; streamExt.miptbp1[1] = ras->miptbp1[1]; streamExt.miptbp2[0] = ras->miptbp2[0]; streamExt.miptbp2[1] = ras->miptbp2[1]; streamExt.texelSize = ras->texelSize; streamExt.paletteSize = ras->paletteSize; streamExt.gsSize = ras->gsSize; streamExt.mipmapVal = ras->kl; stream->write(&streamExt, 64); writeChunkHeader(stream, ID_STRUCT, sz); if(streamExt.type < 2){ stream->write(raster->texels, sz); }else{ stream->write(raster->texels-0x50, ras->texelSize); stream->write(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 = PLUGINOFFSET(Ps2Raster, tex->raster, nativeRasterOffset); size += 12 + ras->texelSize + ras->paletteSize; return size; } } }