mirror of
https://github.com/aap/librw.git
synced 2025-03-13 05:14:43 +00:00
2239 lines
61 KiB
C++
2239 lines
61 KiB
C++
#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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset);
|
|
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 2 qwords
|
|
}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 = paltrxpos;
|
|
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];
|
|
// Add to last transfer
|
|
xferchain[-16] = 0x30000000 | (xferchain[-16]&0xFFFF) + sz+5; // last DMAref
|
|
xferchain[-13] = 0x50000000 | (xferchain[-13]&0xFFFF) + sz+5; // last DIRECT
|
|
xferchain += 4;
|
|
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 2 qwords
|
|
}
|
|
}
|
|
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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset);
|
|
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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset);
|
|
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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset);
|
|
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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset);
|
|
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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset);
|
|
|
|
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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset);
|
|
|
|
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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset);
|
|
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 = PLUGINOFFSET(Ps2Raster, object, offset);
|
|
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 = PLUGINOFFSET(Ps2Raster, object, offset);
|
|
freealign(raster->data);
|
|
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){
|
|
stream->writeI32(defaultMipMapKL);
|
|
return stream;
|
|
}
|
|
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 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 = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset);
|
|
//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 = 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->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 = PLUGINOFFSET(Ps2Raster, tex->raster, nativeRasterOffset);
|
|
size += 12 + ras->pixelSize + ras->paletteSize;
|
|
return size;
|
|
}
|
|
|
|
}
|
|
}
|