i tried to dump the graphics from the CRUISE engine thinking it would be easy, but I wrong.
I managed to export the list of internal files (the ones that are stored in D1, D2, D3, D4 and D5) and I even managed to export all 867 of them (XX0.SET, etc.), by fiddling with macro "dumpResources" in function readVolCnf() .
So far so good.
But I'm struggling with the export of the actual sprites. I don't know where I should hook myself in the main game loop to comfortably display the sprites I want. I tried to start easy by calling loadBackground inside CruiseEngine::mainLoop but it does work as expected because the game keeps running its scripts in the background.
By the way I'm aware that some of the graphics are vectorial, but I want to start easy with the regular sprites and backgrounds.
Can somone explain what's an "overlay" in this engine? Is it an animation sequence?
EDIt: at the moment I'm exporting the sprites as they get rendered in function drawSprite, but the backslash of this is that I'm saving every sprite that gets rendered. I can't tell if the same sprite has already been exported (rendered) before. Also I don't choose which sprites get exported, they get exported when they get drawn. (I'm getting nice results though, I programmed a little viewer).
CRUISE engine : dump graphics
Moderator: ScummVM Team
-
- Posts: 80
- Joined: Fri Oct 19, 2007 5:48 pm
Re: CRUISE engine : dump graphics
OK so here is what I did :
(modified code from ScummVM, in engine "CRUISE")
(everything between //TEST and //~TEST is my custom code)
File "maindraw.cpp"
In file background.cpp
This generates files "export0.dmp, export1.dmp, export2.dmp, ..." for sprites and files "export100000.dmp, export100001.dmp, export100002.dmp, ... for backgrounds.
By the way I'm prefectly aware that 8-bits ints would be more appropriate for all this but I favored 32-bits ints for personal reasons.
(modified code from ScummVM, in engine "CRUISE")
(everything between //TEST and //~TEST is my custom code)
File "maindraw.cpp"
Code: Select all
//TEST
int exportCount = 0;
//~TEST
void drawSprite(int width, int height, cellStruct *currentObjPtr, const uint8 *dataIn, int ys, int xs, uint8 *output, const uint8 *dataBuf) {
int x = 0;
int y = 0;
// Flag the given area as having been changed
Common::Point ps = Common::Point(MAX(MIN(xs, 320), 0), MAX(MIN(ys, 200), 0));
Common::Point pe = Common::Point(MAX(MIN(xs + width, 320), 0), MAX(MIN(ys + height, 200), 0));
if ((ps.x != pe.x) && (ps.y != pe.y))
// At least part of sprite is on-screen
gfxModuleData_addDirtyRect(Common::Rect(ps.x, ps.y, pe.x, pe.y));
cellStruct* plWork = currentObjPtr;
int workBufferSize = height * (width / 8);
unsigned char* workBuf = (unsigned char*)MemAlloc(workBufferSize);
memcpy(workBuf, dataBuf, workBufferSize);
int numPasses = 0;
while (plWork) {
if (plWork->type == OBJ_TYPE_BGMASK && plWork->freeze == 0) {
objectParamsQuery params;
getMultipleObjectParam(plWork->overlay, plWork->idx, ¶ms);
int maskX = params.X;
int maskY = params.Y;
int maskFrame = params.fileIdx;
if (filesDatabase[maskFrame].subData.resourceType == OBJ_TYPE_BGMASK && filesDatabase[maskFrame].subData.ptrMask) {
drawMask(workBuf, width / 8, height, filesDatabase[maskFrame].subData.ptrMask, filesDatabase[maskFrame].width / 8, filesDatabase[maskFrame].height, maskX - xs, maskY - ys, numPasses++);
} else
if (filesDatabase[maskFrame].subData.resourceType == OBJ_TYPE_SPRITE && filesDatabase[maskFrame].subData.ptrMask) {
drawMask(workBuf, width / 8, height, filesDatabase[maskFrame].subData.ptrMask, filesDatabase[maskFrame].width / 8, filesDatabase[maskFrame].height, maskX - xs, maskY - ys, numPasses++);
}
}
plWork = plWork->next;
}
//TEST
Common::DumpFile fout;
if (exportCount < 2000) {
char nameBuffer[256];
//fileEntry *buffer;
sprintf(nameBuffer, "./export3/export%d.dmp", exportCount);
//fout.open(nameBuffer, Common::File::kFileWriteMode);
fout.open(nameBuffer);
if (fout.isOpen()) {
fout.writeUint32LE(width);
fout.writeUint32LE(height);
//dump palette
for (int i = 0; i < 256 * 3; i++) {
fout.writeUint32LE(palScreen[0][i]);
}
}
}
// fout.write(uncompBuffer, buffer[j].extSize);
bool drawn = false;
//~TEST
for (y = 0; y < height; y++) {
for (x = 0; x < (width); x++) {
drawn = false;
uint8 color = *dataIn++;
if ((x + xs) >= 0 && (x + xs) < 320 && (y + ys) >= 0 && (y + ys) < 200) {
if (testMask(x, y, workBuf, width / 8)) {
output[320 * (y + ys) + x + xs] = color;
//TEST
drawn = true;
if (fout.isOpen()) {
fout.writeUint32LE(color);
}
//~TEST
}
}
//TEST
if (!drawn && fout.isOpen()) {
fout.writeUint32LE(0);
}
//~TEST
}
}
//TEST
//if (fout.isOpen())
fout.close();
exportCount++;
//~TEST
MemFree(workBuf);
}
In file background.cpp
Code: Select all
//TEST
int exportCount2 = 100000;
//~TEST
int loadBackground(const char *name, int idx) {
uint8 *ptr;
uint8 *ptr2;
uint8 *ptrToFree;
debug(1, "Loading BG: %s", name);
if (!backgroundScreens[idx]) {
backgroundScreens[idx] = (uint8 *)mallocAndZero(320 * 200);
}
if (!backgroundScreens[idx]) {
backgroundTable[idx].name[0] = 0;
return (-2);
}
backgroundChanged[idx] = true;
ptrToFree = gfxModuleData.pPage10;
if (loadFileSub1(&ptrToFree, name, NULL) < 0) {
if (ptrToFree != gfxModuleData.pPage10)
MemFree(ptrToFree);
return (-18);
}
if (lastFileSize == 32078 || lastFileSize == 32080 || lastFileSize == 32034) {
colorMode = 0;
} else {
colorMode = 1;
}
ptr = ptrToFree;
ptr2 = ptrToFree;
if (!strcmp(name, "LOGO.PI1")) {
oldSpeedGame = speedGame;
flagSpeed = 1;
speedGame = 1;
} else if (flagSpeed) {
speedGame = oldSpeedGame;
flagSpeed = 0;
}
if (!strcmp((char *)ptr, "PAL")) {
memcpy(palScreen[idx], ptr + 4, 256*3);
gfxModuleData_setPal256(palScreen[idx]);
} else {
int mode = ptr2[1];
ptr2 += 2;
// read palette
switch (mode) {
case 0:
case 4: { // color on 3 bit
uint16 oldPalette[32];
memcpy(oldPalette, ptr2, 0x20);
ptr2 += 0x20;
flipGen(oldPalette, 0x20);
for (unsigned long int i = 0; i < 32; i++) {
gfxModuleData_convertOldPalColor(oldPalette[i], &palScreen[idx][i*3]);
}
// duplicate the palette
for (unsigned long int i = 1; i < 8; i++) {
memcpy(&palScreen[idx][32*i*3], &palScreen[idx][0], 32*3);
}
break;
}
case 5: { // color on 4 bit
for (unsigned long int i = 0; i < 32; i++) {
uint8* inPtr = ptr2 + i * 2;
uint8* outPtr = palScreen[idx] + i * 3;
outPtr[2] = ((inPtr[1]) & 0x0F) * 17;
outPtr[1] = (((inPtr[1]) & 0xF0) >> 4) * 17;
outPtr[0] = ((inPtr[0]) & 0x0F) * 17;
}
ptr2 += 2 * 32;
// duplicate the palette
for (unsigned long int i = 1; i < 8; i++) {
memcpy(&palScreen[idx][32*i*3], &palScreen[idx][0], 32*3);
}
break;
}
case 8:
memcpy(palScreen[idx], ptr2, 256*3);
ptr2 += 256 * 3;
break;
default:
assert(0);
}
gfxModuleData_setPal256(palScreen[idx]);
// read image data
gfxModuleData_gfxClearFrameBuffer(backgroundScreens[idx]);
switch (mode) {
case 0:
case 4:
convertGfxFromMode4(ptr2, 320, 200, backgroundScreens[idx]);
ptr2 += 32000;
break;
case 5:
convertGfxFromMode5(ptr2, 320, 200, backgroundScreens[idx]);
break;
case 8:
memcpy(backgroundScreens[idx], ptr2, 320 * 200);
ptr2 += 320 * 200;
break;
}
loadMEN(&ptr2);
loadCVT(&ptr2);
}
MemFree(ptrToFree);
// NOTE: the following is really meant to compare pointers and not the actual
// strings. See r48092 and r48094.
if (name != backgroundTable[idx].name) {
if (strlen(name) >= sizeof(backgroundTable[idx].name))
warning("background name length exceeded allowable maximum");
Common::strlcpy(backgroundTable[idx].name, name, sizeof(backgroundTable[idx].name));
}
//TEST
Common::DumpFile fout;
if (exportCount2 - 100000 < 10) {
char nameBuffer[256];
fileEntry *buffer;
sprintf(nameBuffer, "./export4/export%d.dmp", exportCount2);
//fout.open(nameBuffer, Common::File::kFileWriteMode);
fout.open(nameBuffer);
if (fout.isOpen()) {
fout.writeUint32LE(320);
fout.writeUint32LE(200);
//dump palette
for (int i = 0; i < 256 * 3; i++) {
fout.writeUint32LE(palScreen[idx][i]);
}
for (int y = 0; y < 200; y++) {
for (int x = 0; x < (320); x++) {
fout.writeUint32LE(backgroundScreens[idx][320 * y + x]);
}
}
//if (fout.isOpen())
fout.close();
exportCount2++;
}
}
// fout.write(uncompBuffer, buffer[j].extSize);
//~TEST
return (0);
}
By the way I'm prefectly aware that 8-bits ints would be more appropriate for all this but I favored 32-bits ints for personal reasons.
-
- Posts: 80
- Joined: Fri Oct 19, 2007 5:48 pm
Re: CRUISE engine : dump graphics
I've extracted all the backgrounds this way :
the output format is a .dmp file that as this format :
- 4 bytes (LE 32-bit) : width
- 4 bytes (LE 32-bit) : height
- 3*256 bytes : palette
- width*height bytes : all the pixels
Code: Select all
int exportBackgrounds() {
char* names[100];
names[0] = "1A5.PI1";
names[1] = "20D0.PI1";
names[2] = "CFAC2.PI1";
names[3] = "DGF1.PI1";
names[4] = "DISPO.PI1";
names[5] = "END1.PI1";
names[6] = "END4F.PI1";
names[7] = "END5.PI1";
names[8] = "END6.PI1";
names[9] = "END7.PI1";
names[10] = "END9.PI1";
names[11] = "F10.PI1";
names[12] = "F14.PI1";
names[13] = "FABIANI.PI1";
names[14] = "FINFO.PI1";
names[15] = "FOND.PI1";
names[16] = "FOND11.PI1";
names[17] = "FOND8.PI1";
names[18] = "FONDBO.PI1";
names[19] = "FONDCR.PI1";
names[20] = "FS01D.PI1";
names[21] = "FS27E.PI1";
names[22] = "FS27F1.PI1";
names[23] = "GPPONT.PI1";
names[24] = "HORLOFND.PI1";
names[25] = "I00.PI1";
names[26] = "I01.PI1";
names[27] = "I02.PI1";
names[28] = "I03.PI1";
names[29] = "I04.PI1";
names[30] = "I05.PI1";
names[31] = "I06.PI1";
names[32] = "I07A.PI1";
names[33] = "JUL0.PI1";
names[34] = "LETTRE3.PI1";
names[35] = "LOGO.PI1";
names[36] = "MAIN.PI1";
names[37] = "MANOIR0.PI1";
names[38] = "MANOIR1.PI1";
names[39] = "MANOIR2.PI1";
names[40] = "MANOIR3.PI1";
names[41] = "POK.PI1";
names[42] = "POKER1.PI1";
names[43] = "PRISON2.PI1";
names[44] = "REB.PI1";
names[45] = "REBECCA1.PI1";
names[46] = "ROSE1.PI1";
names[47] = "S01.PI1";
names[48] = "S01A.PI1";
names[49] = "S01B.PI1";
names[50] = "S01C.PI1";
names[51] = "S01PAL.PI1";
names[52] = "S02.PI1";
names[53] = "S03.PI1";
names[54] = "S03A.PI1";
names[55] = "S03B.PI1";
names[56] = "S03D.PI1";
names[57] = "S03E.PI1";
names[58] = "S03F.PI1";
names[59] = "S03G.PI1";
names[60] = "S03H.PI1";
names[61] = "S03L.PI1";
names[62] = "S04.PI1";
names[63] = "S06.PI1";
names[64] = "S06A.PI1";
names[65] = "S06B.PI1";
names[66] = "S07.PI1";
names[67] = "S07L.PI1";
names[68] = "S08.PI1";
names[69] = "S09.PI1";
names[70] = "S10.PI1";
names[71] = "S11.PI1";
names[72] = "S11A.PI1";
names[73] = "S11A00.PI1";
names[74] = "S12.PI1";
names[75] = "S16.PI1";
names[76] = "S17.PI1";
names[77] = "S18.PI1";
names[78] = "S19.PI1";
names[79] = "S20.PI1";
names[80] = "S21.PI1";
names[81] = "S22.PI1";
names[82] = "S23.PI1";
names[83] = "S24.PI1";
names[84] = "S25.PI1";
names[85] = "S25MSG.PI1";
names[86] = "S26.PI1";
names[87] = "S26A.PI1";
names[88] = "S26B.PI1";
names[89] = "S27.PI1";
names[90] = "S27A.PI1";
names[91] = "S27C.PI1";
names[92] = "S27D.PI1";
names[93] = "S27E.PI1";
names[94] = "S29.PI1";
names[95] = "S29A.PI1";
names[96] = "S30.PI1";
names[97] = "S31.PI1";
names[98] = "S31A.PI1";
names[99] = "S31A00.PI1";
names[100] = "S31B.PI1";
names[101] = "S31B00.PI1";
names[102] = "S31C00.PI1";
names[103] = "S32.PI1";
names[104] = "S32A.PI1";
names[105] = "S33.PI1";
names[106] = "S33A.PI1";
names[107] = "S33B.PI1";
names[108] = "S33C.PI1";
names[109] = "S33D.PI1";
names[110] = "S33E.PI1";
names[111] = "S33F.PI1";
names[112] = "S34.PI1";
names[113] = "S34A.PI1";
names[114] = "S35.PI1";
names[115] = "S36.PI1";
names[116] = "S37.PI1";
names[117] = "S37A.PI1";
names[118] = "SHIP.PI1";
names[119] = "SUZAN.PI1";
names[120] = "TOM1.PI1";
names[121] = "VIDE.PI1";
names[122] = "VIDE2.PI1";
names[123] = "WEDDING1.PI1";
names[124] = "XX2.PI1";
int nbBg = 125;
for (int b = 0; b < nbBg; b++)
{
char *name = names[b];
int idx = 0;
uint8 *ptr;
uint8 *ptr2;
uint8 *ptrToFree;
debug(1, "Loading BG: %s", name);
if (!backgroundScreens[idx]) {
backgroundScreens[idx] = (uint8 *)mallocAndZero(320 * 200);
}
if (!backgroundScreens[idx]) {
backgroundTable[idx].name[0] = 0;
return (-2);
}
backgroundChanged[idx] = true;
ptrToFree = gfxModuleData.pPage10;
if (loadFileSub1(&ptrToFree, name, NULL) < 0) {
if (ptrToFree != gfxModuleData.pPage10)
MemFree(ptrToFree);
return (-18);
}
if (lastFileSize == 32078 || lastFileSize == 32080 || lastFileSize == 32034) {
colorMode = 0;
}
else {
colorMode = 1;
}
ptr = ptrToFree;
ptr2 = ptrToFree;
if (!strcmp(name, "LOGO.PI1")) {
oldSpeedGame = speedGame;
flagSpeed = 1;
speedGame = 1;
}
else if (flagSpeed) {
speedGame = oldSpeedGame;
flagSpeed = 0;
}
if (!strcmp((char *)ptr, "PAL")) {
memcpy(palScreen[idx], ptr + 4, 256 * 3);
gfxModuleData_setPal256(palScreen[idx]);
}
else {
int mode = ptr2[1];
ptr2 += 2;
// read palette
switch (mode) {
case 0:
case 4: { // color on 3 bit
uint16 oldPalette[32];
memcpy(oldPalette, ptr2, 0x20);
ptr2 += 0x20;
flipGen(oldPalette, 0x20);
for (unsigned long int i = 0; i < 32; i++) {
gfxModuleData_convertOldPalColor(oldPalette[i], &palScreen[idx][i * 3]);
}
// duplicate the palette
for (unsigned long int i = 1; i < 8; i++) {
memcpy(&palScreen[idx][32 * i * 3], &palScreen[idx][0], 32 * 3);
}
break;
}
case 5: { // color on 4 bit
for (unsigned long int i = 0; i < 32; i++) {
uint8* inPtr = ptr2 + i * 2;
uint8* outPtr = palScreen[idx] + i * 3;
outPtr[2] = ((inPtr[1]) & 0x0F) * 17;
outPtr[1] = (((inPtr[1]) & 0xF0) >> 4) * 17;
outPtr[0] = ((inPtr[0]) & 0x0F) * 17;
}
ptr2 += 2 * 32;
// duplicate the palette
for (unsigned long int i = 1; i < 8; i++) {
memcpy(&palScreen[idx][32 * i * 3], &palScreen[idx][0], 32 * 3);
}
break;
}
case 8:
memcpy(palScreen[idx], ptr2, 256 * 3);
ptr2 += 256 * 3;
break;
default:
assert(0);
}
gfxModuleData_setPal256(palScreen[idx]);
// read image data
gfxModuleData_gfxClearFrameBuffer(backgroundScreens[idx]);
switch (mode) {
case 0:
case 4:
convertGfxFromMode4(ptr2, 320, 200, backgroundScreens[idx]);
ptr2 += 32000;
break;
case 5:
convertGfxFromMode5(ptr2, 320, 200, backgroundScreens[idx]);
break;
case 8:
memcpy(backgroundScreens[idx], ptr2, 320 * 200);
ptr2 += 320 * 200;
break;
}
loadMEN(&ptr2);
loadCVT(&ptr2);
}
MemFree(ptrToFree);
// NOTE: the following is really meant to compare pointers and not the actual
// strings. See r48092 and r48094.
if (name != backgroundTable[idx].name) {
if (strlen(name) >= sizeof(backgroundTable[idx].name))
warning("background name length exceeded allowable maximum");
Common::strlcpy(backgroundTable[idx].name, name, sizeof(backgroundTable[idx].name));
}
//TEST
Common::DumpFile fout;
char nameBuffer[256];
fileEntry *buffer;
sprintf(nameBuffer, "./exportbg/bg_%s.dmp", name);
//fout.open(nameBuffer, Common::File::kFileWriteMode);
fout.open(nameBuffer);
if (fout.isOpen()) {
fout.writeUint32LE(320);
fout.writeUint32LE(200);
//dump palette
for (int i = 0; i < 256 * 3; i++) {
fout.writeByte(palScreen[idx][i]);
}
for (int y = 0; y < 200; y++) {
for (int x = 0; x < (320); x++) {
fout.writeByte(backgroundScreens[idx][320 * y + x]);
}
}
//if (fout.isOpen())
fout.close();
}
// fout.write(uncompBuffer, buffer[j].extSize);
//~TEST
}
return (0);
}
- 4 bytes (LE 32-bit) : width
- 4 bytes (LE 32-bit) : height
- 3*256 bytes : palette
- width*height bytes : all the pixels
-
- Posts: 80
- Joined: Fri Oct 19, 2007 5:48 pm
-
- Posts: 80
- Joined: Fri Oct 19, 2007 5:48 pm
Re: CRUISE engine : dump graphics
Unfortunately I screwed up the palette so i'll have to do it again, but this is a good start :
https://imgur.com/gallery/vBBUpls
That's all 1723 sprites
https://imgur.com/gallery/vBBUpls
That's all 1723 sprites