474 lines
16 KiB
C++
474 lines
16 KiB
C++
/************************************************************************
|
|
* Copyright (c) 2005-2006 tok@openlinux.org.uk *
|
|
* *
|
|
* This file contains code derived from information copyrighted by *
|
|
* DMA Design. It may not be used in a commercial product. *
|
|
* *
|
|
* See license.txt for details. *
|
|
* *
|
|
* This notice may not be removed or altered. *
|
|
************************************************************************/
|
|
#include <iostream>
|
|
#include <cassert>
|
|
#include "opengta.h"
|
|
#include "buffercache.h"
|
|
#include "log.h"
|
|
|
|
using namespace Util;
|
|
namespace OpenGTA {
|
|
|
|
#define GTA_GRAPHICS_GRX 290
|
|
#define GTA_GRAPHICS_GRY 325
|
|
#define GTA_GRAPHICS_G24 336
|
|
|
|
Graphics24Bit::Graphics24Bit(const std::string& style) : GraphicsBase() {
|
|
fd = PHYSFS_openRead(style.c_str());
|
|
assert(fd!=NULL);
|
|
_topHeaderSize = 64;
|
|
rawClut = NULL;
|
|
palIndex = NULL;
|
|
loadHeader();
|
|
setupBlocking(style);
|
|
}
|
|
|
|
Graphics24Bit::~Graphics24Bit() {
|
|
if (rawClut)
|
|
delete [] rawClut;
|
|
if (palIndex)
|
|
delete [] palIndex;
|
|
PHYSFS_close(fd);
|
|
}
|
|
|
|
void Graphics24Bit::loadHeader() {
|
|
PHYSFS_uint32 vc;
|
|
PHYSFS_readULE32(fd, &vc);
|
|
if(vc != GTA_GRAPHICS_G24) {
|
|
ERROR << "graphics file specifies version " << vc <<
|
|
" instead of " << GTA_GRAPHICS_G24 << std::endl;
|
|
return;
|
|
}
|
|
PHYSFS_readULE32(fd, &sideSize);
|
|
PHYSFS_readULE32(fd, &lidSize);
|
|
PHYSFS_readULE32(fd, &auxSize);
|
|
PHYSFS_readULE32(fd, &animSize);
|
|
PHYSFS_readULE32(fd, &clutSize);
|
|
PHYSFS_readULE32(fd, &tileclutSize);
|
|
PHYSFS_readULE32(fd, &spriteclutSize);
|
|
PHYSFS_readULE32(fd, &newcarclutSize);
|
|
PHYSFS_readULE32(fd, &fontclutSize);
|
|
PHYSFS_readULE32(fd, &paletteIndexSize);
|
|
PHYSFS_readULE32(fd, &objectInfoSize);
|
|
PHYSFS_readULE32(fd, &carInfoSize);
|
|
PHYSFS_readULE32(fd, &spriteInfoSize);
|
|
PHYSFS_readULE32(fd, &spriteGraphicsSize);
|
|
PHYSFS_readULE32(fd, &spriteNumberSize);
|
|
|
|
/*
|
|
INFO << "Version: " << vc << std::endl << " Block textures: S " << sideSize / 4096 << " L " <<
|
|
lidSize / 4096 << " A " << auxSize / 4096 << std::endl;
|
|
*/
|
|
if (sideSize % 4096 != 0) {
|
|
ERROR << "Side-Block texture size is not a multiple of 4096" << std::endl;
|
|
return;
|
|
}
|
|
if (lidSize % 4096 != 0) {
|
|
ERROR << "Lid-Block texture size is not a multiple of 4096" << std::endl;
|
|
return;
|
|
}
|
|
if (auxSize % 4096 != 0) {
|
|
ERROR << "Aux-Block texture size is not a multiple of 4096" << std::endl;
|
|
return;
|
|
}
|
|
|
|
PHYSFS_uint32 tmp = sideSize / 4096 + lidSize / 4096 + auxSize / 4096;
|
|
tmp = tmp % 4;
|
|
if (tmp) {
|
|
auxBlockTrailSize = (4 - tmp) * 4096;
|
|
INFO << "adjusting aux-block by " << auxBlockTrailSize << std::endl;
|
|
}
|
|
INFO << "Anim size: " << animSize << std::endl;
|
|
INFO << "Obj-info size: " << objectInfoSize << " car-size: " << carInfoSize <<
|
|
" sprite-info size: " << spriteInfoSize << " graphic size: " << spriteGraphicsSize <<
|
|
" numbers s: " << spriteNumberSize << std::endl;
|
|
if (spriteNumberSize != 42) {
|
|
ERROR << "spriteNumberSize is " << spriteNumberSize << " (should be 42)" << std::endl;
|
|
return;
|
|
}
|
|
|
|
INFO << " clut: " << clutSize << " tileclut: " << tileclutSize << " spriteclut: "<< spriteclutSize <<
|
|
" newcar: " << newcarclutSize << " fontclut: " << fontclutSize << std::endl <<
|
|
|
|
"Obj-info size: " << objectInfoSize << " car-size: " << carInfoSize <<
|
|
" pal-index size: " << paletteIndexSize <<
|
|
std::endl;
|
|
|
|
loadTileTextures();
|
|
loadAnim();
|
|
loadClut();
|
|
loadPalIndex();
|
|
loadObjectInfo();
|
|
loadCarInfo();
|
|
loadSpriteInfo();
|
|
loadSpriteGraphics();
|
|
loadSpriteNumbers();
|
|
}
|
|
|
|
void Graphics24Bit::loadClut() {
|
|
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
|
|
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize;
|
|
PHYSFS_seek(fd, st);
|
|
pagedClutSize = clutSize;
|
|
if (clutSize % 65536 != 0)
|
|
pagedClutSize += (65536 - (clutSize % 65536));
|
|
rawClut = new unsigned char[pagedClutSize];
|
|
assert(rawClut);
|
|
PHYSFS_read(fd, rawClut, 1, pagedClutSize);
|
|
//write(2, rawClut, pagedClutSize);
|
|
}
|
|
|
|
void Graphics24Bit::loadPalIndex() {
|
|
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
|
|
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
|
|
pagedClutSize;
|
|
PHYSFS_seek(fd, st);
|
|
PHYSFS_uint16 pal_index_count = paletteIndexSize / 2;
|
|
assert(paletteIndexSize % 2 == 0);
|
|
palIndex = new PHYSFS_uint16[pal_index_count];
|
|
for (PHYSFS_uint16 i = 0; i < pal_index_count; i++) {
|
|
PHYSFS_readULE16(fd, &palIndex[i]);
|
|
}
|
|
}
|
|
|
|
void Graphics24Bit::loadCarInfo() {
|
|
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
|
|
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
|
|
pagedClutSize + paletteIndexSize + objectInfoSize;
|
|
loadCarInfo_shared(st);
|
|
}
|
|
|
|
void Graphics24Bit::loadSpriteInfo() {
|
|
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
|
|
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
|
|
pagedClutSize + paletteIndexSize + objectInfoSize +
|
|
carInfoSize;
|
|
PHYSFS_seek(fd, st);
|
|
|
|
PHYSFS_uint8 v;
|
|
PHYSFS_uint32 w;
|
|
PHYSFS_uint32 _bytes_read = 0;
|
|
while (_bytes_read < spriteInfoSize) {
|
|
SpriteInfo *si = new SpriteInfo();
|
|
PHYSFS_read(fd, static_cast<void*>(&si->w), 1, 1);
|
|
PHYSFS_read(fd, static_cast<void*>(&si->h), 1, 1);
|
|
PHYSFS_read(fd, static_cast<void*>(&si->deltaCount), 1, 1);
|
|
PHYSFS_read(fd, static_cast<void*>(&v), 1, 1);
|
|
PHYSFS_readULE16(fd, &si->size);
|
|
_bytes_read += 6;
|
|
PHYSFS_readULE16(fd, &si->clut);
|
|
PHYSFS_read(fd, static_cast<void*>(&si->xoffset), 1, 1);
|
|
PHYSFS_read(fd, static_cast<void*>(&si->yoffset), 1, 1);
|
|
PHYSFS_readULE16(fd, &si->page);
|
|
_bytes_read += 6;
|
|
/*
|
|
std::cout << "sprite: " << int(si->w) << "x" << int(si->h) << " deltas: " << int(si->deltaCount)
|
|
<< " clut: " << si->clut << " x: " << int(si->xoffset) << " y: " << int(si->yoffset) <<
|
|
" page: " << si->page << std::endl;
|
|
*/
|
|
// sanity check
|
|
if (v)
|
|
WARN << "Compression flag active in sprite!" << std::endl;
|
|
if (int(si->w) * int(si->h) != int(si->size)) {
|
|
ERROR << "Sprite info size mismatch: " << int(si->w) << "x" << int(si->h) <<
|
|
" != " << si->size << std::endl;
|
|
return;
|
|
}
|
|
if (si->deltaCount > 32) {
|
|
ERROR << "Delta count of sprite is " << si->deltaCount << std::endl;
|
|
return;
|
|
}
|
|
for (PHYSFS_uint8 j = 0; j < si->deltaCount; j++) {
|
|
si->delta[j].size = 0;
|
|
si->delta[j].ptr = 0;
|
|
if (si->deltaCount && (j < si->deltaCount)) {
|
|
PHYSFS_readULE16(fd, &si->delta[j].size);
|
|
PHYSFS_readULE32(fd, &w);
|
|
_bytes_read += 6;
|
|
si->delta[j].ptr = reinterpret_cast<unsigned char*>(w);
|
|
}
|
|
}
|
|
spriteInfos.push_back(si);
|
|
}
|
|
st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
|
|
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
|
|
pagedClutSize + paletteIndexSize + objectInfoSize +
|
|
carInfoSize + spriteInfoSize;
|
|
assert(PHYSFS_tell(fd) == PHYSFS_sint64(st));
|
|
|
|
}
|
|
|
|
void Graphics24Bit::loadSpriteNumbers() {
|
|
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
|
|
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
|
|
pagedClutSize + paletteIndexSize + objectInfoSize +
|
|
carInfoSize + spriteInfoSize + spriteGraphicsSize;
|
|
loadSpriteNumbers_shared(st);
|
|
}
|
|
|
|
void Graphics24Bit::loadSpriteGraphics() {
|
|
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
|
|
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
|
|
pagedClutSize + paletteIndexSize + objectInfoSize +
|
|
carInfoSize + spriteInfoSize;
|
|
PHYSFS_seek(fd, st);
|
|
|
|
rawSprites = new unsigned char[spriteGraphicsSize];
|
|
assert(rawSprites != NULL);
|
|
PHYSFS_read(fd, static_cast<void*>(rawSprites), spriteGraphicsSize, 1);
|
|
|
|
std::vector<SpriteInfo*>::const_iterator i = spriteInfos.begin();
|
|
std::vector<SpriteInfo*>::const_iterator end = spriteInfos.end();
|
|
PHYSFS_uint32 _pagewise = 256 * 256;
|
|
while (i != end) {
|
|
SpriteInfo *info = *i;
|
|
for (uint8_t k = 0; k < info->deltaCount; ++k) {
|
|
PHYSFS_uint32 offset = reinterpret_cast<PHYSFS_uint32>(info->delta[k].ptr);
|
|
PHYSFS_uint32 page = offset / 65536;
|
|
PHYSFS_uint32 y = (offset % 65536) / 256;
|
|
PHYSFS_uint32 x = (offset % 65536) % 256;
|
|
info->delta[k].ptr = rawSprites + page * _pagewise + 256 * y + x;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void Graphics24Bit::loadObjectInfo() {
|
|
PHYSFS_uint64 st = static_cast<PHYSFS_uint64>(_topHeaderSize) +
|
|
sideSize + lidSize + auxSize + auxBlockTrailSize + animSize +
|
|
pagedClutSize + paletteIndexSize;
|
|
loadObjectInfo_shared(st);
|
|
}
|
|
|
|
void Graphics24Bit::applyClut(unsigned char* src, unsigned char* dst,
|
|
const size_t & len, const PHYSFS_uint16 & clutIdx, bool rgba) {
|
|
PHYSFS_uint32 off = 65536 * (clutIdx / 64) + 4 * (clutIdx % 64);
|
|
for (size_t i= 0; i < len; i++) {
|
|
PHYSFS_uint32 coff = PHYSFS_uint32(*src) * 256 + off;
|
|
*dst = rawClut[coff+2];
|
|
++dst;
|
|
*dst = rawClut[coff+1];
|
|
++dst;
|
|
*dst = rawClut[coff+0];
|
|
++dst;
|
|
if (rgba) {
|
|
if (*src == 0)
|
|
*dst = 0;
|
|
else
|
|
*dst = 0xff;
|
|
++dst;
|
|
}
|
|
++src;
|
|
}
|
|
}
|
|
|
|
unsigned char *Graphics24Bit::getLid(unsigned int idx, unsigned int _not_used, bool rgba = false) {
|
|
prepareLidTexture(idx - 1, reinterpret_cast<unsigned char*>(tileTmp));
|
|
unsigned char* src = tileTmp;
|
|
unsigned char* dst = (rgba) ? tileTmpRGBA : tileTmpRGB;
|
|
PHYSFS_uint16 clutIdx = palIndex[4 * (idx + sideSize / 4096)];
|
|
applyClut(src, dst, 4096, clutIdx, rgba);
|
|
|
|
return (rgba) ? tileTmpRGBA : tileTmpRGB;
|
|
}
|
|
|
|
unsigned char *Graphics24Bit::getSide(unsigned int idx, unsigned int _not_used, bool rgba = false) {
|
|
prepareSideTexture(idx-1, reinterpret_cast<unsigned char*>(tileTmp));
|
|
unsigned char* src = tileTmp;
|
|
unsigned char* dst = (rgba) ? tileTmpRGBA : tileTmpRGB;
|
|
PHYSFS_uint16 clutIdx = palIndex[idx*4];
|
|
applyClut(src, dst, 4096, clutIdx, rgba);
|
|
|
|
return (rgba) ? tileTmpRGBA : tileTmpRGB;
|
|
}
|
|
|
|
unsigned char *Graphics24Bit::getAux(unsigned int idx, unsigned int _not_used, bool rgba = false) {
|
|
prepareAuxTexture(idx - 1, reinterpret_cast<unsigned char*>(tileTmp));
|
|
|
|
unsigned char* src = tileTmp;
|
|
unsigned char* dst = (rgba) ? tileTmpRGBA : tileTmpRGB;
|
|
PHYSFS_uint16 clutIdx = palIndex[4 * (idx + sideSize / 4096 + lidSize / 4096)];
|
|
applyClut(src, dst, 4096, clutIdx, rgba);
|
|
|
|
return (rgba) ? tileTmpRGBA : tileTmpRGB;
|
|
}
|
|
|
|
void Graphics24Bit::dumpClut(const char* fname) {
|
|
assert(pagedClutSize % 1024 == 0);
|
|
//PHYSFS_uint32 num_clut = pagedClutSize / 1024;
|
|
PHYSFS_uint32 num_pal = paletteIndexSize / 2;
|
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
|
#define rmask 0xff000000
|
|
#define gmask 0x00ff0000
|
|
#define bmask 0x0000ff00
|
|
#define amask 0x000000ff
|
|
#else
|
|
#define rmask 0x000000ff
|
|
#define gmask 0x0000ff00
|
|
#define bmask 0x00ff0000
|
|
#define amask 0xff000000
|
|
#endif
|
|
SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA,
|
|
num_pal, 256, 32, rmask, gmask, bmask, amask);
|
|
SDL_LockSurface(s);
|
|
unsigned char* dst = static_cast<unsigned char*>(s->pixels);
|
|
|
|
for (PHYSFS_uint32 color = 0; color < 256; color++) {
|
|
|
|
for (PHYSFS_uint32 pal_id = 0; pal_id < num_pal; pal_id++) {
|
|
PHYSFS_uint32 clut_id = palIndex[pal_id];
|
|
PHYSFS_uint32 off = 65536 * (clut_id / 64) + 4 * (clut_id % 64);
|
|
|
|
*dst = rawClut[off+color*256];
|
|
++dst;
|
|
*dst = rawClut[off+color*256+1];
|
|
++dst;
|
|
*dst = rawClut[off+color*256+2];
|
|
++dst;
|
|
*dst = 0xff;
|
|
++dst;
|
|
}
|
|
|
|
}
|
|
SDL_UnlockSurface(s);
|
|
SDL_SaveBMP(s, fname);
|
|
SDL_FreeSurface(s);
|
|
}
|
|
|
|
unsigned char* Graphics24Bit::getSpriteBitmap(size_t id, int remap = -1, Uint32 delta = 0) {
|
|
const SpriteInfo *info = spriteInfos[id];
|
|
assert(info != NULL);
|
|
const PHYSFS_uint32 y = info->yoffset;
|
|
const PHYSFS_uint32 x = info->xoffset;
|
|
const PHYSFS_uint32 page_size = 256 * 256;
|
|
|
|
unsigned char * page_start = rawSprites + info->page * page_size;
|
|
|
|
BufferCache & bcache = BufferCacheHolder::Instance();
|
|
unsigned char * dest = bcache.requestBuffer(page_size);
|
|
bcache.lockBuffer(dest);
|
|
memcpy(dest, page_start, page_size);
|
|
if (delta > 0) {
|
|
handleDeltas(*info, dest, delta);
|
|
/*
|
|
assert(delta < info->deltaCount);
|
|
DeltaInfo & di = info->delta[delta];
|
|
applyDelta(*info, dest+256*y+x, di);
|
|
*/
|
|
}
|
|
|
|
unsigned char* bigbuf = bcache.requestBuffer(page_size * 4);
|
|
unsigned char* result = dest;
|
|
unsigned int skip_cluts = 0;
|
|
if (remap > -1)
|
|
skip_cluts = spriteclutSize / 1024 + remap + 1;
|
|
|
|
PHYSFS_uint16 clutIdx = palIndex[info->clut + tileclutSize / 1024] + skip_cluts;
|
|
// PHYSFS_uint16 clutIdx = palIndex[info->clut + (spriteclutSize + tileclutSize) / 1024] + (remap > -1 ? remap+2 : 0);
|
|
applyClut(dest, bigbuf, page_size, clutIdx, true);
|
|
assert(page_size > PHYSFS_uint32(info->w * info->h * 4));
|
|
for (uint16_t i = 0; i < info->h; i++) {
|
|
memcpy(result, bigbuf+(256*y+x)*4, info->w * 4);
|
|
result += info->w * 4;
|
|
bigbuf += 256 * 4;
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
}
|
|
|
|
#ifdef G24_DUMPER
|
|
|
|
SDL_Surface* image = NULL;
|
|
|
|
void on_exit() {
|
|
if (image)
|
|
SDL_FreeSurface(image);
|
|
PHYSFS_deinit();
|
|
SDL_Quit();
|
|
}
|
|
|
|
SDL_Surface* get_image(unsigned char* rp, unsigned int w, unsigned int h) {
|
|
assert(rp);
|
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
|
#define rmask 0xff000000
|
|
#define gmask 0x00ff0000
|
|
#define bmask 0x0000ff00
|
|
#define amask 0x000000ff
|
|
#else
|
|
#define rmask 0x000000ff
|
|
#define gmask 0x0000ff00
|
|
#define bmask 0x00ff0000
|
|
#define amask 0xff000000
|
|
#endif
|
|
SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA,
|
|
w, h, 32, rmask, gmask, bmask, amask);
|
|
SDL_LockSurface(s);
|
|
unsigned char* dst = static_cast<unsigned char*>(s->pixels);
|
|
for (unsigned int i=0; i<w*h; i++) {
|
|
*dst = *rp; ++dst;++rp;
|
|
*dst = *rp; ++dst;++rp;
|
|
*dst = *rp; ++dst;++rp;
|
|
*dst = *rp; ++dst;++rp;
|
|
//*dst = 0xff; ++dst;
|
|
}
|
|
SDL_UnlockSurface(s);
|
|
return s;
|
|
}
|
|
|
|
void display_image(SDL_Surface* s) {
|
|
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 32, SDL_DOUBLEBUF);
|
|
SDL_Event event;
|
|
SDL_BlitSurface(s, NULL, screen, NULL);
|
|
SDL_Flip(screen);
|
|
while (1) {
|
|
while (SDL_PollEvent(&event)) {
|
|
switch(event.type) {
|
|
case SDL_QUIT:
|
|
return;
|
|
case SDL_KEYDOWN:
|
|
if (event.key.keysym.sym == SDLK_ESCAPE)
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
SDL_Delay(100);
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc, char* argv[]) {
|
|
PHYSFS_init(argv[0]);
|
|
SDL_Init(SDL_INIT_VIDEO);
|
|
atexit(on_exit);
|
|
int idx = 0;
|
|
|
|
PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1);
|
|
PHYSFS_addToSearchPath("gtadata.zip", 1);
|
|
|
|
OpenGTA::Graphics24Bit graphics(argv[1]);
|
|
graphics.dumpClut("foo.bmp");
|
|
if (argc > 2)
|
|
idx = atoi(argv[2]);
|
|
//image = get_image(graphics.getAux(idx, 0, true), 64,64);
|
|
OpenGTA::GraphicsBase::SpriteInfo * sinfo = graphics.getSprite(idx);
|
|
image = get_image(graphics.getSpriteBitmap(idx, -1, 0), sinfo->w, sinfo->h);
|
|
if (argc == 4)
|
|
SDL_SaveBMP(image, argv[3]);
|
|
else
|
|
display_image(image);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|