The Beginning (?) of The End 3 (Progress Report)

Time for the weekly progress report! This week I have finals, so studies had to be prioritized, but here is what has progressed since the last report:

Bugs

Here are the newly identified bugs:

BugSteps to reproduceExplanationSolved
The first track played when the game start is randomWhen running in DOSBox, it always starts in track 4No
Battle sound effects not synchronizingSometimes the battle sound effects last after the battle is overNo
Crash when starting new game after endingThe New Game dialog pops up after an endingYes
Non-printable keys being printed in savefilesFor instance, SHIFT inserts a character when inserting the savefile nameYes
The wrong game version shows when viewing creditsCredits show vW95 on Scummvm (vDOS on DOSBox)No
Attack after death being possibleWait until your HP is 0 but the enemy attack animation/sound isn’t over, then attack backSometimes your attack animation can span until after you’re dead. This requires a few retries. Something similar can be achieved in the original (DOSBox), so maybe this is expected behaviorNo
Crash on reading documentsRead every single document in the innHeap-buffer overflow caused by pointer arithmeticYes
Doors not openingTry to open the first door to the upper right in the starting screenThat (and a few other) doors are supposed to be openableNo
Crash after savingSave the game once and walk around the mapNo

Some of these have already been solved, while the third and fourth seemed low-priority, so I left them for later. The last two bugs were discovered recently and they’re quite high-priority, due to affecting the gameplay (doors not opening) and being a common source of crash (may crash after saving).

Meanwhile, some of the bugs I mentioned in the last post have also been solved:

Sound Issues

  • Music volume not syncing to what is on the config at game startup

How it was fixed:

Music::Music(hResContext *musicRes) : _musicContext(musicRes), _parser(0) {
...

	_currentVolume = 255;
	_currentMusicBuffer = nullptr;

	_trackNumber = 0;

+++	syncSoundSettings();
}

syncSoundSettings is a method for syncing the internal volume with the config.

Notify the engine that the sound settings in the config manager might have changed and that it should adjust any internal volume (and other) values accordingly.

ScummVM Doxygen

Therefore we call it once in the Music constructor to initialize its values accordingly to the config manager.

  • Music does not cut out at zero

How it was fixed:

inline int16 quantizedVolume(uint16 trueVolume) {
	int16 quantized = trueVolume & 0xFFF8;
	quantized += (quantized / 16);


---	quantized += 2; // In ScummVM the range is 0..255
+++	quantized = CLIP(quantized, (int16)0, (int16)255);

	return quantized;
}

Here quantized is the value used for the config manager, which is 0-255 for ScummVM. Thankfully quantized already goes from 0-255 (except when fully maxed, at which point it slightly caps over 255, which is why we clip it).

Document Issues

  • Arrow sprites not changing

//  Sets the mouse pointer imagery
void gMousePointer::setImage(
    gPixelMap       &img,
    int             x,
    int             y) {
	Point16         pos = currentPosition - offsetPosition;

	if (pointerImage != &img
	        ||  x != offsetPosition.x
	        ||  y != offsetPosition.y
	        ||  img.size != saveMap.size) {
		offsetPosition.x = x;
		offsetPosition.y = y;
        ...
        }
...
}

When changing the pointer image (done with gMousePointer::setImage) we check whether the img pointer changed. However in mouseimg.cpp where we call setImage,

createStackedImage(combinedImage, &combinedImageCenter, imageArray, imageCenterArray, imageIndex);

imageOffset.x = combinedImageCenter - mouseImage->size.x / 2;
imageOffset.y = 0;

//  Set the combined image as the new mouse cursor
g_vm->_pointer->hide();
g_vm->_pointer->setImage(combinedImage, mouseImageOffset.x - imageOffset.x, mouseImageOffset.y - imageOffset.y);
g_vm->_pointer->show();

combinedImage never actually changes value. Therefore we change createStackedImage to modify combined combinedImage everytime it is called:

void createStackedImage(gPixelMap **newImage, int *newImageCenter, gPixelMap **imageArray, int *imageCenterArray, int images) {
	assert(images != 0);

	if (*newImage)
		delete *newImage;

	*newImage = new gPixelMap;
...
}

Of course, we modify the usage in the rest of code for this.

  • Close parchment button not working

The cause for this was a typo. Oops.

CDocumentAppearance parchAppearance = {
	{202, 54, 208, 256},
	1,
	pageOrientVertical,
	bookTextColors,
	{ {27, 18, 149, 212}, {0, 0, 0, 0} },
--	{64, 229,  20,  20},
++	{164, 229,  20,  20},
	parchDecorations,
	ARRAYSIZE(parchDecorations),
	MKTAG('P', 'A', 'R', 'C'),
	MKTAG('P', 'C', 'H', 0)
};
  • Crash on reading Kaidar’s book

The cause for this was a unsigned to signed conversion. Therefore, we make a cast to uint16.

uint8 *Thread::strAddress(int strNum) {
	uint16 seg    = READ_LE_INT16(codeSeg + 2);
	uint16 offset = READ_LE_INT16(codeSeg + 4);
	uint8 *strSeg = segmentAddress(seg, offset);

	assert(strNum >= 0);
	assert(codeSeg);
	assert(strSeg);

---	return strSeg + READ_LE_INT16(strSeg + 2 * strNum);
+++	return strSeg + (uint16)READ_LE_INT16(strSeg + 2 * strNum);
}
  • Crash on Credits

The cause for this was a strcat overlap. How did this work in the original, even?

bool CDocument::checkForPageBreak(char *string, uint16 index, int32 &offset) {

	// get the current index into the string
	char    *strIndex       = string + index;
+++	char *strAfter;

	// page break detected
	if (strIndex[1] == dPageBreak[0] &&
	        strIndex[2] == dPageBreak[1]) {
		// eat the page breaks chars
		// tie off the end
		strIndex[0] = 0;
+++		strAfter = new char[textSize];
+++		Common::strlcpy(strAfter, &strIndex[3], textSize);

		// string them together
---		strcat(&strIndex[0], &strIndex[2 + 1]);
+++		strcat(&strIndex[0], strAfter);

		// take the offset to the end of this line
		offset = index;

		// and set the new page flag

+++		delete[] strAfter;
		return true;
	}

	return false;
}

Keyboard

We change the original game’s scheme of key recognition to the one found in common/keyboard.h. Therefore we change things like SpecialKey(upArrowKey) to Common::KEYCODE_UP.

switch (key) {
case Common::KEYCODE_UP:
	selectionUp(1);
	return true;

case Common::KEYCODE_DOWN:
	selectionDown(1);
	return true;

case Common::KEYCODE_PAGEUP:
	selectionUp(linesPerPage);
	return true;

case Common::KEYCODE_PAGEDOWN:
	selectionDown(linesPerPage);
	return true;
}

A few weeks left until GSoC ends, while summer break is about to start here. Let’s do our best.


Leave a Reply

Your email address will not be published. Required fields are marked *