PDA

View Full Version : Ever17 GCPS (Graphics) reinsertion


dsp2003
2007-10-18, 06:26
Is there someone who can help with the KID Engine games graphics reinsertion (yup, Russian Ever17 translation here again, doh :P) (please, don't mention the KID Tools GUI - it messes up the alpha channel and cannot re-convert the data)? If someone have already created the TGA\BMP-32 <-> GCPS converter, then please let me know asap. :) Thank you.

The format is GCPS. Here's the header's description (Pascal):

TGCPS = record
CPS : array[1..4] of char; // 'CPS'+#0 header (4 bytes)
CPSFileSize : longword; // CPS file size (4 bytes)
Unknown_0 : longword; // Count of colors | CRC32 ?? (4 bytes)
BMPFileSize : longword; // Decompressed stream size (4 bytes)
BMPSubhead : array[1..4] of char; // 'bmp'+#0 subheader (4 bytes)
CompressedStream : TStream; // Data container
end;

Serke
2007-11-07, 16:58
The following code is a result of my disassembling the executable and reconstructing some of its code directly related to decrypting and unpacking CPS files (I wish I could afford Hex-Rays decompiler so that I didn't have to decompile it in my head >_<). The unpacking/packing algorithm is rather peculiar (or so I think because I can't recognize it ^_^). It looks like it combines RLE approach with dictionary-based compression/decompression method (something akin to LZSS?). Other methods are used as well. Yep, this is indeed a strange animal. See for yourself:

#include <windows.h>
#include <stdio.h>

typedef struct t_cps_header {
char CPS[4];
DWORD CPSFileSize;
WORD w_field_A;
WORD w_field_B;
DWORD PRTFileSize;
char PRTSubhead[4];
} cps_header;

void decodeCPS( DWORD*, DWORD );
void *unpackCPS_method1( BYTE*, DWORD* );

int main( int argc, char *argv[] )
{
HANDLE hcps, outf;
DWORD fsize, NumOfBytesRead, NumOfBytesWritten;
DWORD *cps_data;
char szdecfn[MAX_PATH];
DWORD PRT_size = 0;
LPVOID hBlock;

if( argc > 1 )
{
hcps = CreateFile( argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL );
if( hcps != INVALID_HANDLE_VALUE )
{
fsize = GetFileSize( hcps, NULL );
cps_data = (DWORD *) malloc( fsize );
ReadFile( hcps, cps_data, fsize, &NumOfBytesRead, NULL );
decodeCPS( cps_data, ((struct t_cps_header *)cps_data)->CPSFileSize );
hBlock = unpackCPS_method1((BYTE *)cps_data, &PRT_size);
wsprintf( szdecfn, "&#37;s%s", argv[1], ".unpacked" );
outf = CreateFile( szdecfn, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL );
WriteFile( outf, hBlock, PRT_size, &NumOfBytesWritten, NULL );
CloseHandle( hcps );
CloseHandle( outf );
free( cps_data );
HeapFree( GetProcessHeap(), 0, hBlock );
printf("Done!\n");
}
else
printf("Cannot open file %s\n", argv[1]);
}
else
printf("Usage: unpackCPS filename.cps\n");

return 0;
}

void decodeCPS( DWORD *cps_ptr, DWORD cps_size )
{
int eax, ecx, edx, edi;

if( (edi = cps_ptr[cps_size/4-1] - 0x7534682) )
{
eax = 0x10;
edx = 4;
ecx = cps_ptr[edi/4] + edi + 0x3786425;
if ((cps_size - 4) > 0x10)
do {
if( eax != edi )
cps_ptr[edx] -= (cps_size+ecx);
ecx = ecx * 0x41C64E6D + 0x9B06;
eax += 4;
edx++;
} while (eax < (cps_size - 4));
cps_ptr[cps_size/4-1] = 0;
}
}

void *unpackCPS_method1( BYTE *cps_ptr, DWORD *prt_size_ptr )
{
DWORD c, v1_32, v2_32, lCount32, lCount32_2, gCount32 = 0, PRTFileSize;
BYTE *pPackedData = cps_ptr + sizeof( cps_header );
BYTE *pBlock = NULL, *ptr = NULL;

if ( ! (((struct t_cps_header *)cps_ptr)->w_field_B & 1) ||
strcmp((char *)cps_ptr, "CPS") ||
(((struct t_cps_header *)cps_ptr)->w_field_A != 0x66) )
return NULL;

PRTFileSize = ((struct t_cps_header *)cps_ptr)->PRTFileSize;
pBlock = (BYTE *)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, PRTFileSize+0x10 );
*prt_size_ptr = PRTFileSize;
cps_ptr = pBlock;

do {
c = *pPackedData++;
if (c & 0x80)
if (c & 0x40)
{
v1_32 = (c & 0x1F) + 2;
if (c & 0x20)
{
v1_32 += (*pPackedData << 5);
pPackedData++;
}
c = *pPackedData++;
lCount32 = 0;
while( lCount32 < v1_32 )
if (gCount32 <= PRTFileSize)
{
*pBlock++ = c;
gCount32++;
lCount32++;
}
else
return cps_ptr;
}
else
{
ptr = pBlock - ((c & 3) << 8) - *pPackedData - 1;
v1_32 = ((c >> 2) & 0x0F) + 2;
pPackedData++;
lCount32 = 0;
while( lCount32 < v1_32 )
if (gCount32 <= PRTFileSize)
{
*pBlock++ = *ptr++;
gCount32++;
lCount32++;
}
else
return cps_ptr;
}
else
if (c & 0x40)
{
lCount32_2 = 0;
v1_32 = (c & 0x3F) + 2;
v2_32 = *pPackedData + 1;
pPackedData++;
while( lCount32_2 < v2_32 )
{
lCount32 = 0;
while( lCount32 < v1_32 )
if (gCount32 <= PRTFileSize)
{
*pBlock++ = pPackedData[ lCount32 ];
lCount32++;
gCount32++;
}
else
return cps_ptr;
lCount32_2++;
}
pPackedData += v1_32;
}
else
{
v1_32 = (c & 0x1F) + 1;
if (c & 0x20)
{
v1_32 += ((*pPackedData) << 5);
pPackedData++;
}
lCount32 = 0;
while( lCount32 < v1_32 )
if (gCount32 <= PRTFileSize)
{
*pBlock++ = *pPackedData++;
lCount32++;
gCount32++;
}
else
return cps_ptr;
}
} while( gCount32 < PRTFileSize );
return cps_ptr;
}