CRUISE engine : dump graphics

Ask for help with ScummVM problems

Moderator: ScummVM Team

Post Reply
monsieurouxx
Posts: 79
Joined: Fri Oct 19, 2007 5:48 pm

CRUISE engine : dump graphics

Post by monsieurouxx » Tue Nov 20, 2018 10:16 pm

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).

monsieurouxx
Posts: 79
Joined: Fri Oct 19, 2007 5:48 pm

Re: CRUISE engine : dump graphics

Post by monsieurouxx » Wed Nov 21, 2018 3:07 pm

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"

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, &params);

			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);
}

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.

monsieurouxx
Posts: 79
Joined: Fri Oct 19, 2007 5:48 pm

Re: CRUISE engine : dump graphics

Post by monsieurouxx » Wed Nov 21, 2018 7:12 pm

I've extracted all the backgrounds this way :

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);
}


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

monsieurouxx
Posts: 79
Joined: Fri Oct 19, 2007 5:48 pm

Re: CRUISE engine : dump graphics

Post by monsieurouxx » Wed Nov 21, 2018 7:14 pm


monsieurouxx
Posts: 79
Joined: Fri Oct 19, 2007 5:48 pm

Re: CRUISE engine : dump graphics

Post by monsieurouxx » Thu Nov 22, 2018 12:53 am

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

Image
Image
Image
Image


That's all 1723 sprites

Post Reply