[Scummvm-git-logs] scummvm master -> 656a35ee87c6e06fc1a61dff9eb6b1119706466c
athrxx
athrxx at scummvm.org
Fri Jan 22 00:20:33 UTC 2021
This automated email contains information about 10 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
d2b2d509ef AGOS: (ELVIRA/PC98/Jp) - add detection entry
bb8c70f865 AGOS: (ELVIRA/PC98/Jp) - fix startup
574133086d AGOS: (ELVIRA/PC98/Jp) - add Japanese font drawing
8d17584247 AGOS: (ELVIRA/PC98/Jp) - more dirty rect handling
512ae9cfe3 AGOS: (ELVIRA/PC98/Jp) - add static msg strings + some fixes
56d897a100 AGOS: (ELVIRA/PC98/Jp) - more fixes
a3fd19265f AGOS: (ELVIRA/PC98/Jp) - add sound driver
77b316ef58 AGOS: (ELVIRA) - block mouse wheel during save/load/pause dialog
8969fa9c09 AGOS: (ELVIRA/PC98/Jp) - fix game restart
656a35ee87 NEWS: mention Elvira PC-98 support
Commit: d2b2d509ef5a860c741ff64cf78945b85cffc078
https://github.com/scummvm/scummvm/commit/d2b2d509ef5a860c741ff64cf78945b85cffc078
Author: athrxx (athrxx at scummvm.org)
Date: 2021-01-22T00:47:58+01:00
Commit Message:
AGOS: (ELVIRA/PC98/Jp) - add detection entry
Changed paths:
engines/agos/detection_tables.h
diff --git a/engines/agos/detection_tables.h b/engines/agos/detection_tables.h
index 17a3e7cebc..8e4a6ebf9c 100644
--- a/engines/agos/detection_tables.h
+++ b/engines/agos/detection_tables.h
@@ -385,6 +385,28 @@ static const AGOSGameDescription gameDescriptions[] = {
GF_OLD_BUNDLE
},
+ // Elvira 1 - Japanese PC-98
+ {
+ {
+ "elvira1",
+ 0,
+ {
+ { "gamepcj.dat", GAME_BASEFILE, "f170990deafbf9adee360021a6b5f375", -1},
+ { "icon.dat", GAME_ICONFILE, "fda48c9da7f3e72d0313e2f5f760fc45", -1},
+ { "tbllist.dat", GAME_TBLFILE, "319f6b227c7822a551f57d24e70f8149", -1},
+ AD_LISTEND
+ },
+ Common::JA_JPN,
+ Common::kPlatformPC98,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOSPEECH)
+ },
+
+ GType_ELVIRA1,
+ GID_ELVIRA1,
+ GF_OLD_BUNDLE
+ },
+
// Elvira 2 - English Amiga Floppy
{
{
Commit: bb8c70f865eed0475db1d20e20c01a697dceaebd
https://github.com/scummvm/scummvm/commit/bb8c70f865eed0475db1d20e20c01a697dceaebd
Author: athrxx (athrxx at scummvm.org)
Date: 2021-01-22T00:47:58+01:00
Commit Message:
AGOS: (ELVIRA/PC98/Jp) - fix startup
Add necessary file name and file format modifications so that the game at least loads up and runs up to the start scene without glitches.
Changed paths:
engines/agos/agos.cpp
engines/agos/agos.h
engines/agos/debug.cpp
engines/agos/drivers/accolade/adlib.cpp
engines/agos/gfx.cpp
engines/agos/midi.cpp
engines/agos/midi.h
engines/agos/res.cpp
engines/agos/res_ami.cpp
engines/agos/res_snd.cpp
engines/agos/subroutine.cpp
engines/agos/vga.cpp
diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp
index 8c3dd46513..0bc3811b5c 100644
--- a/engines/agos/agos.cpp
+++ b/engines/agos/agos.cpp
@@ -497,6 +497,7 @@ AGOSEngine::AGOSEngine(OSystem *system, const AGOSGameDescription *gd)
memset(_lettersToPrintBuf, 0, sizeof(_lettersToPrintBuf));
_planarBuf = 0;
+ _pak98Buf = 0;
_midiEnabled = false;
@@ -582,11 +583,9 @@ Common::Error AGOSEngine::init() {
if ((getGameType() == GType_SIMON2 && getPlatform() == Common::kPlatformWindows) ||
(getGameType() == GType_SIMON1 && getPlatform() == Common::kPlatformWindows) ||
((getFeatures() & GF_TALKIE) && getPlatform() == Common::kPlatformAcorn) ||
- (getPlatform() == Common::kPlatformDOS)) {
+ (getPlatform() == Common::kPlatformDOS || getPlatform() == Common::kPlatformPC98)) {
- bool isDemo = (getFeatures() & GF_DEMO) ? true : false;
-
- int ret = _midi->open(getGameType(), isDemo);
+ int ret = _midi->open(getGameType(), getPlatform(), (getFeatures() & GF_DEMO));
if (ret)
warning("MIDI Player init failed: \"%s\"", MidiDriver::getErrorName(ret));
@@ -945,6 +944,7 @@ AGOSEngine::~AGOSEngine() {
_backBuf->free();
delete _backBuf;
free(_planarBuf);
+ delete[] _pak98Buf;
if (_scaleBuf)
_scaleBuf->free();
delete _scaleBuf;
diff --git a/engines/agos/agos.h b/engines/agos/agos.h
index f8ad420007..9da4b0cf91 100644
--- a/engines/agos/agos.h
+++ b/engines/agos/agos.h
@@ -551,6 +551,7 @@ protected:
byte *_planarBuf;
byte _videoBuf1[32000];
uint16 _videoWindows[128];
+ const byte *_pak98Buf;
uint8 _window3Flag;
uint8 _window4Flag;
@@ -806,6 +807,9 @@ protected:
uint loadTextFile_simon1(const char *filename, byte *dst);
Common::SeekableReadStream *openTablesFile_simon1(const char *filename);
+ Common::SeekableReadStream *openTablesFile_pak98(const char *filename);
+ Common::SeekableReadStream *createPak98FileStream(const char *filename);
+ void convertPC98Image(VC10_state &state);
uint loadTextFile_gme(const char *filename, byte *dst);
Common::SeekableReadStream *openTablesFile_gme(const char *filename);
@@ -1153,7 +1157,7 @@ protected:
void verticalScroll(VC10_state *state);
int vcReadVarOrWord();
- uint vcReadNextWord();
+ uint vcReadNextWord(bool forceLERead = false);
uint vcReadNextByte();
uint vcReadVar(uint var);
void vcWriteVar(uint var, int16 value);
@@ -1206,7 +1210,7 @@ protected:
byte *getBackGround();
byte *getScaleBuf();
- byte *convertImage(VC10_state *state, bool compressed);
+ byte *convertAmigaImage(VC10_state *state, bool compressed);
bool decrunchFile(byte *src, byte *dst, uint32 size);
void loadVGABeardFile(uint16 id);
diff --git a/engines/agos/debug.cpp b/engines/agos/debug.cpp
index 2d7e3a0a3c..3bb13d16e8 100644
--- a/engines/agos/debug.cpp
+++ b/engines/agos/debug.cpp
@@ -499,7 +499,7 @@ void AGOSEngine::dumpBitmap(const char *filename, const byte *offs, uint16 w, ui
state.width = w / 16;
if (getFeatures() & GF_PLANAR) {
- state.srcPtr = convertImage(&state, (getGameType() == GType_PN || (flags & 0x80) != 0));
+ state.srcPtr = convertAmigaImage(&state, (getGameType() == GType_PN || (flags & 0x80) != 0));
flags &= ~0x80;
}
diff --git a/engines/agos/drivers/accolade/adlib.cpp b/engines/agos/drivers/accolade/adlib.cpp
index 2e3ce82605..9d013f9722 100644
--- a/engines/agos/drivers/accolade/adlib.cpp
+++ b/engines/agos/drivers/accolade/adlib.cpp
@@ -294,10 +294,10 @@ void MidiDriver_Accolade_AdLib::noteOn(byte FMvoiceChannel, byte note, byte velo
int16 channelVolumeAdjust = _channels[FMvoiceChannel].volumeAdjust;
channelVolumeAdjust += adjustedVelocity;
channelVolumeAdjust = CLIP<int16>(channelVolumeAdjust, 0, 0x7F);
-
+
// adjust velocity with the master volume
byte volumeAdjust = adjustedVelocity * ((float) (128 + _masterVolume) / 128);
-
+
adjustedVelocity = volumeAdjust;
if (!_musicDrvMode) {
diff --git a/engines/agos/gfx.cpp b/engines/agos/gfx.cpp
index 867842276a..8bdcfcfa6d 100644
--- a/engines/agos/gfx.cpp
+++ b/engines/agos/gfx.cpp
@@ -936,7 +936,7 @@ void AGOSEngine::drawImage(VC10_state *state) {
state->surf_addr += xoffs + yoffs * state->surf_pitch;
- if (getGameType() == GType_ELVIRA1 && (state->flags & kDFNonTrans) && yoffs > 133)
+ if (getGameType() == GType_ELVIRA1 && getPlatform() != Common::kPlatformPC98 && (state->flags & kDFNonTrans) && yoffs > 133)
state->paletteMod = 16;
if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW)
diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp
index 34c7634d94..6f9d73c48a 100644
--- a/engines/agos/midi.cpp
+++ b/engines/agos/midi.cpp
@@ -87,17 +87,23 @@ MidiPlayer::~MidiPlayer() {
clearConstructs();
}
-int MidiPlayer::open(int gameType, bool isDemo) {
+int MidiPlayer::open(int gameType, Common::Platform platform, bool isDemo) {
// Don't call open() twice!
assert(!_driver);
Common::String accoladeDriverFilename;
musicType = MT_INVALID;
+ int devFlags = MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32;
switch (gameType) {
case GType_ELVIRA1:
- _musicMode = kMusicModeAccolade;
- accoladeDriverFilename = "INSTR.DAT";
+ if (platform == Common::kPlatformPC98) {
+ _musicMode = kMusicModeDisabled;
+ devFlags = (devFlags & ~MDT_ADLIB) | MDT_PC98;
+ } else {
+ _musicMode = kMusicModeAccolade;
+ accoladeDriverFilename = "INSTR.DAT";
+ }
break;
case GType_ELVIRA2:
case GType_WW:
@@ -130,7 +136,7 @@ int MidiPlayer::open(int gameType, bool isDemo) {
int ret = 0;
if (_musicMode != kMusicModeDisabled) {
- dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
+ dev = MidiDriver::detectDevice(devFlags);
musicType = MidiDriver::getMusicType(dev);
switch (musicType) {
@@ -163,7 +169,7 @@ int MidiPlayer::open(int gameType, bool isDemo) {
switch (musicType) {
case MT_ADLIB:
_driver = MidiDriver_Accolade_AdLib_create(accoladeDriverFilename);
-
+
break;
case MT_MT32:
_driver = MidiDriver_Accolade_MT32_create(accoladeDriverFilename);
@@ -577,7 +583,7 @@ static const int simon1_gmf_size[] = {
17256, 5103, 8794, 4884, 16
};
-void MidiPlayer::loadSMF(Common::File *in, int song, bool sfx) {
+void MidiPlayer::loadSMF(Common::SeekableReadStream *in, int song, bool sfx) {
Common::StackLock lock(_mutex);
MusicInfo *p = sfx ? &_sfx : &_music;
@@ -666,7 +672,7 @@ void MidiPlayer::loadSMF(Common::File *in, int song, bool sfx) {
p->parser = parser; // That plugs the power cord into the wall
}
-void MidiPlayer::loadMultipleSMF(Common::File *in, bool sfx) {
+void MidiPlayer::loadMultipleSMF(Common::SeekableReadStream *in, bool sfx) {
// This is a special case for Simon 2 Windows.
// Instead of having multiple sequences as
// separate tracks in a Type 2 file, simon2win
@@ -722,7 +728,7 @@ void MidiPlayer::loadMultipleSMF(Common::File *in, bool sfx) {
}
}
-void MidiPlayer::loadXMIDI(Common::File *in, bool sfx) {
+void MidiPlayer::loadXMIDI(Common::SeekableReadStream *in, bool sfx) {
Common::StackLock lock(_mutex);
MusicInfo *p = sfx ? &_sfx : &_music;
clearConstructs(*p);
@@ -768,7 +774,7 @@ void MidiPlayer::loadXMIDI(Common::File *in, bool sfx) {
p->parser = parser; // That plugs the power cord into the wall
}
-void MidiPlayer::loadS1D(Common::File *in, bool sfx) {
+void MidiPlayer::loadS1D(Common::SeekableReadStream *in, bool sfx) {
Common::StackLock lock(_mutex);
MusicInfo *p = sfx ? &_sfx : &_music;
clearConstructs(*p);
diff --git a/engines/agos/midi.h b/engines/agos/midi.h
index a75f09df0b..7b5cd790ee 100644
--- a/engines/agos/midi.h
+++ b/engines/agos/midi.h
@@ -96,10 +96,10 @@ public:
MidiPlayer();
~MidiPlayer() override;
- void loadSMF(Common::File *in, int song, bool sfx = false);
- void loadMultipleSMF(Common::File *in, bool sfx = false);
- void loadXMIDI(Common::File *in, bool sfx = false);
- void loadS1D(Common::File *in, bool sfx = false);
+ void loadSMF(Common::SeekableReadStream *in, int song, bool sfx = false);
+ void loadMultipleSMF(Common::SeekableReadStream *in, bool sfx = false);
+ void loadXMIDI(Common::SeekableReadStream *in, bool sfx = false);
+ void loadS1D(Common::SeekableReadStream *in, bool sfx = false);
bool hasNativeMT32() const { return _nativeMT32; }
void setLoop(bool loop);
@@ -115,7 +115,7 @@ public:
void setVolume(int musicVol, int sfxVol);
public:
- int open(int gameType, bool isDemo);
+ int open(int gameType, Common::Platform platform, bool isDemo);
// MidiDriver_BASE interface implementation
void send(uint32 b) override;
@@ -124,7 +124,7 @@ public:
private:
kMusicMode _musicMode;
MusicType musicType;
-
+
private:
Common::SeekableReadStream *simon2SetupExtractFile(const Common::String &requestedFileName);
};
diff --git a/engines/agos/res.cpp b/engines/agos/res.cpp
index 76d23905b7..4b2373e2e4 100644
--- a/engines/agos/res.cpp
+++ b/engines/agos/res.cpp
@@ -806,6 +806,91 @@ void AGOSEngine::loadVGABeardFile(uint16 id) {
}
}
+void decodePak98(uint8 *dst, const uint8 *src, uint32 inSize) {
+ const uint8 *src2 = 0;
+ uint8 cmd = 0x80;
+
+ for (uint32 bytesLeft = inSize; bytesLeft; ) {
+ if (cmd == 0x80) {
+ src2 = src + 1;
+ --bytesLeft;
+ }
+
+ if (cmd & *src) {
+ *dst++ = *src2++;
+ --bytesLeft;
+ } else {
+ bytesLeft -= 2;
+ uint16 cmd2 = READ_LE_UINT16(src2);
+ src2 += 2;
+ uint8 cmd3 = cmd2 & 0x0F;
+ cmd2 >>= 4;
+
+ if (cmd2 == 0) {
+ uint16 count = cmd3 + 4;
+ --bytesLeft;
+ if (cmd3 == 0x0F) {
+ count = READ_LE_UINT16(src2);
+ src2 += 2;
+ bytesLeft -= 2;
+ } else if (cmd3 == 0x0E) {
+ count = 18 + (*src2++);
+ --bytesLeft;
+ }
+
+ uint8 destVal = *src2++;
+ while (count--)
+ *dst++ = destVal;
+
+ } else if (cmd2 == 1) {
+ uint16 count = cmd3 + 3;
+ if (cmd3 == 0x0F) {
+ count = READ_LE_UINT16(src2);
+ src2 += 2;
+ bytesLeft -= 2;
+ } else if (cmd3 == 0x0E) {
+ count = 17 + (*src2++);
+ --bytesLeft;
+ }
+
+ dst += count;
+
+ } else if (cmd2 == 2) {
+ uint16 count = cmd3 + 16;
+ if (cmd3 == 0x0F) {
+ count = READ_LE_UINT16(src2);
+ src2 += 2;
+ bytesLeft -= 2;
+ } else if (cmd3 == 0x0E) {
+ count = 30 + (*src2++);
+ --bytesLeft;
+ }
+
+ bytesLeft -= count;
+ while (count--)
+ *dst++ = *src2++;
+
+ } else {
+ uint16 count = cmd3 + 3;
+ if (cmd3 == 0x0F) {
+ count = 18 + (*src2++);
+ --bytesLeft;
+ }
+
+ uint8 *src3 = dst - cmd2;
+ while (count--)
+ *dst++ = *src3++;
+ }
+ }
+
+ cmd >>= 1;
+ if (cmd == 0) {
+ cmd = 0x80;
+ src = src2;
+ }
+ }
+}
+
void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type, bool useError) {
Common::File in;
char filename[15];
@@ -851,7 +936,9 @@ void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type, bool useError) {
sprintf(filename, "%.3d%d.pkd", id, type);
}
} else {
- if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
+ if (getGameType() == GType_ELVIRA1 && getPlatform() == Common::kPlatformPC98) {
+ sprintf(filename, "%.2d.GR2", id);
+ } else if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
sprintf(filename, "%.2d%d.VGA", id, type);
} else if (getGameType() == GType_PN) {
sprintf(filename, "%c%d.out", id + 48, type);
@@ -901,6 +988,40 @@ void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type, bool useError) {
dst = allocBlock (dstSize + extraBuffer);
decrunchFile(srcBuffer, dst, srcSize);
free(srcBuffer);
+ } else if (getPlatform() == Common::kPlatformPC98) {
+ bool compressed = (in.readUint16LE() == 1);
+ srcSize = in.readUint32LE();
+ if (type == 1) {
+ if (compressed)
+ srcSize = in.readUint32LE() + 2;
+ in.seek(srcSize, SEEK_CUR);
+ compressed = (in.readUint16LE() == 1);
+ srcSize = in.readUint32LE();
+ }
+
+ if (compressed) {
+ dstSize = srcSize;
+ srcSize = in.readUint32LE();
+ uint16 fill = in.readUint16LE();
+
+ uint8 *srcBuffer = new uint8[srcSize];
+ if (in.read(srcBuffer, srcSize) != srcSize)
+ error("loadVGAVideoFile: Read failed");
+ dst = allocBlock(dstSize);
+
+ Common::fill<uint16*, uint16>((uint16*)dst, (uint16*)(dst + (dstSize & ~1)), fill);
+ if (dstSize & 1)
+ *(dst + dstSize - 1) = fill & 0xff;
+
+ decodePak98(dst, srcBuffer, srcSize);
+
+ delete[] srcBuffer;
+ } else {
+ dstSize = srcSize;
+ dst = allocBlock(dstSize + extraBuffer);
+ if (in.read(dst, dstSize) != dstSize)
+ error("loadVGAVideoFile: Read failed");
+ }
} else {
dst = allocBlock(dstSize + extraBuffer);
if (in.read(dst, dstSize) != dstSize)
@@ -924,4 +1045,80 @@ void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type, bool useError) {
}
}
+Common::SeekableReadStream *AGOSEngine::createPak98FileStream(const char *filename) {
+ Common::File in;
+ if (!in.open(filename))
+ return 0;
+
+ /*uint16 cmpType = */in.readUint16LE();
+ uint32 outSize = in.readUint32LE();
+ uint32 inSize = in.readUint32LE();
+ uint16 fill = in.readUint16LE();
+
+ uint8 *tempBuffer = new uint8[inSize];
+ uint8 *decBuffer = new uint8[outSize];
+ Common::fill<uint16*, uint16>((uint16*)decBuffer, (uint16*)(decBuffer + (outSize & ~1)), fill);
+ if (outSize & 1)
+ *(decBuffer + outSize - 1) = fill & 0xff;
+
+ in.read(tempBuffer, inSize);
+ decodePak98(decBuffer, tempBuffer, inSize);
+ delete[] tempBuffer;
+
+ return new Common::MemoryReadStream(decBuffer, outSize, DisposeAfterUse::YES);
+}
+
+void AGOSEngine::convertPC98Image(VC10_state &state) {
+ if (state.flags & (kDFCompressedFlip | kDFCompressed)) {
+ const byte *src = state.srcPtr;
+ uint32 outSize = READ_LE_UINT32(src + 2);
+ assert(outSize == (state.width << 3) * state.height);
+ uint32 inSize = READ_LE_UINT32(src + 6);
+ uint16 fill = READ_LE_UINT16(src + 10);
+ delete[] _pak98Buf;
+ byte *decBuffer = new uint8[outSize];
+ Common::fill<uint16*, uint16>((uint16*)decBuffer, (uint16*)(decBuffer + (outSize & ~1)), fill);
+ if (outSize & 1)
+ *(decBuffer + outSize - 1) = fill & 0xff;
+
+ decodePak98(decBuffer, src + 12, inSize);
+ _pak98Buf = state.srcPtr = decBuffer;
+ }
+
+ // The PC-98 images are in a planar format, but slightly different from the Amiga format. It does
+ // not make much sense to set the GF_PLANAR flag, since the icons do not require the conversion.
+ delete[] _planarBuf;
+ uint16 planeLW = state.width << 1;
+ uint16 planePitch = planeLW * 3;
+ _planarBuf = new byte[(state.width << 3) * state.height];
+
+ const byte *src[4];
+ memset(src, 0, sizeof(src));
+ for (int i = 0; i < 4; ++i)
+ src[i] = state.srcPtr + i * planeLW;
+ byte *dst = _planarBuf;
+
+ for (int y = 0; y < state.height; ++y) {
+ for (int x = 0; x < planeLW; ++x) {
+ for (int i = 0; i <= 6; i += 2) {
+ byte col = 0;
+ for (int ii = 0; ii < 4; ++ii) {
+ col |= ((*src[ii] >> (7 - i)) & 1) << (ii + 4);
+ col |= ((*src[ii] >> (6 - i)) & 1) << ii;
+ }
+ *dst++ = col;
+ }
+ for (int ii = 0; ii < 4; ++ii)
+ ++src[ii];
+ }
+ for (int ii = 0; ii < 4; ++ii)
+ src[ii] += planePitch;
+ }
+
+ state.srcPtr = _planarBuf;
+ if (state.flags & kDFCompressedFlip)
+ state.flags |= kDFFlip;
+ state.flags &= ~(kDFCompressedFlip | kDFCompressed);
+}
+
} // End of namespace AGOS
diff --git a/engines/agos/res_ami.cpp b/engines/agos/res_ami.cpp
index 4b3d575142..1f05cf716d 100644
--- a/engines/agos/res_ami.cpp
+++ b/engines/agos/res_ami.cpp
@@ -140,7 +140,7 @@ static void convertCompressedImage(const byte *src, byte *dst, uint8 colorDepth,
}
}
-byte *AGOSEngine::convertImage(VC10_state *state, bool compressed) {
+byte *AGOSEngine::convertAmigaImage(VC10_state *state, bool compressed) {
int length, i, j;
uint8 colorDepth = 4;
diff --git a/engines/agos/res_snd.cpp b/engines/agos/res_snd.cpp
index 81d89cc4d3..059473695e 100644
--- a/engines/agos/res_snd.cpp
+++ b/engines/agos/res_snd.cpp
@@ -290,16 +290,22 @@ void AGOSEngine::playMusic(uint16 music, uint16 track) {
} else {
_midi->setLoop(true); // Must do this BEFORE loading music.
- char filename[15];
- Common::File f;
- sprintf(filename, "MOD%d.MUS", music);
- f.open(filename);
- if (f.isOpen() == false)
- error("playMusic: Can't load music from '%s'", filename);
+ Common::SeekableReadStream *str = 0;
+ if (getPlatform() == Common::kPlatformPC98) {
+ str = createPak98FileStream(Common::String::format("MOD%d.PAK", music).c_str());
+ if (!str)
+ error("playMusic: Can't load music from 'MOD%d.PAK'", music);
+ } else {
+ Common::File *file = new Common::File();
+ if (!file->open(Common::String::format("MOD%d.MUS", music)))
+ error("playMusic: Can't load music from 'MOD%d.MUS'", music);
+ str = file;
+ }
- _midi->loadS1D(&f);
+ _midi->loadS1D(str);
_midi->startTrack(0);
_midi->startTrack(track);
+ delete str;
}
}
diff --git a/engines/agos/subroutine.cpp b/engines/agos/subroutine.cpp
index eaa080f6bf..906fa856d5 100644
--- a/engines/agos/subroutine.cpp
+++ b/engines/agos/subroutine.cpp
@@ -23,6 +23,7 @@
#include "common/debug-channels.h"
#include "common/file.h"
#include "common/textconsole.h"
+#include "common/memstream.h"
#include "agos/agos.h"
#include "agos/intern.h"
@@ -259,7 +260,9 @@ void AGOSEngine::endCutscene() {
}
Common::SeekableReadStream *AGOSEngine::openTablesFile(const char *filename) {
- if (getFeatures() & GF_OLD_BUNDLE)
+ if (getPlatform() == Common::kPlatformPC98)
+ return openTablesFile_pak98(filename);
+ else if (getFeatures() & GF_OLD_BUNDLE)
return openTablesFile_simon1(filename);
else
return openTablesFile_gme(filename);
@@ -272,6 +275,13 @@ Common::SeekableReadStream *AGOSEngine::openTablesFile_simon1(const char *filena
return in;
}
+Common::SeekableReadStream *AGOSEngine::openTablesFile_pak98(const char *filename) {
+ Common::SeekableReadStream *in = createPak98FileStream(filename);
+ if (!in)
+ error("openTablesFile_pak98: Can't open '%s'", filename);
+ return in;
+}
+
Common::SeekableReadStream *AGOSEngine::openTablesFile_gme(const char *filename) {
uint res;
uint32 offs;
@@ -287,7 +297,6 @@ bool AGOSEngine::loadTablesIntoMem(uint16 subrId) {
byte *p;
uint16 min_num, max_num, file_num;
Common::SeekableReadStream *in;
- char filename[30];
if (_tblList == NULL)
return 0;
@@ -306,9 +315,8 @@ bool AGOSEngine::loadTablesIntoMem(uint16 subrId) {
_tablesHeapCurPos = _tablesHeapCurPosOrg;
_stringIdLocalMin = 1;
_stringIdLocalMax = 0;
-
- sprintf(filename, "TABLES%.2d", file_num);
- in = openTablesFile(filename);
+ Common::String filename = Common::String::format("TABLES%.2d%s", file_num, getPlatform() == Common::kPlatformPC98 ? ".PAK" : "");
+ in = openTablesFile(filename.c_str());
readSubroutineBlock(in);
closeTablesFile(in);
diff --git a/engines/agos/vga.cpp b/engines/agos/vga.cpp
index 7926f7f4fe..65f3efcd0d 100644
--- a/engines/agos/vga.cpp
+++ b/engines/agos/vga.cpp
@@ -268,9 +268,11 @@ int AGOSEngine::vcReadVarOrWord() {
}
}
-uint AGOSEngine::vcReadNextWord() {
+uint AGOSEngine::vcReadNextWord(bool forceLERead) {
uint a;
a = readUint16Wrapper(_vcPtr);
+ if (forceLERead)
+ a = FROM_BE_16(a);
_vcPtr += 2;
return a;
}
@@ -635,13 +637,13 @@ void AGOSEngine::drawImage_init(int16 image, uint16 palette, int16 x, int16 y, u
state.flags = flags;
src = _curVgaFile2 + state.image * 8;
- state.srcPtr = _curVgaFile2 + readUint32Wrapper(src);
+ state.srcPtr = _curVgaFile2 + (getPlatform() == Common::kPlatformPC98 ? READ_LE_UINT32(src) : readUint32Wrapper(src));
if (getGameType() == GType_FF || getGameType() == GType_PP) {
width = READ_LE_UINT16(src + 6);
height = READ_LE_UINT16(src + 4) & 0x7FFF;
flags = src[5];
} else {
- width = READ_BE_UINT16(src + 6) / 16;
+ width = (getPlatform() == Common::kPlatformPC98 ? READ_LE_UINT16(src + 6) : READ_BE_UINT16(src + 6)) / 16;
height = src[5];
flags = src[4];
}
@@ -662,10 +664,9 @@ void AGOSEngine::drawImage_init(int16 image, uint16 palette, int16 x, int16 y, u
if (getFeatures() & GF_PLANAR) {
if (getGameType() == GType_PN) {
- state.srcPtr = convertImage(&state, ((state.flags & (kDFCompressed | kDFCompressedFlip)) != 0));
- }
- else
- state.srcPtr = convertImage(&state, ((flags & 0x80) != 0));
+ state.srcPtr = convertAmigaImage(&state, ((state.flags & (kDFCompressed | kDFCompressedFlip)) != 0));
+ } else
+ state.srcPtr = convertAmigaImage(&state, ((flags & 0x80) != 0));
// converted planar clip is already uncompressed
if (state.flags & kDFCompressedFlip) {
@@ -690,6 +691,9 @@ void AGOSEngine::drawImage_init(int16 image, uint16 palette, int16 x, int16 y, u
}
}
+ if (getPlatform() == Common::kPlatformPC98)
+ convertPC98Image(state);
+
uint maxWidth = (getGameType() == GType_FF || getGameType() == GType_PP) ? 640 : 20;
if ((getGameType() == GType_SIMON2 || getGameType() == GType_FF) && width > maxWidth) {
horizontalScroll(&state);
Commit: 574133086d42e64d59cd4840f7f7cd524ec6afd7
https://github.com/scummvm/scummvm/commit/574133086d42e64d59cd4840f7f7cd524ec6afd7
Author: athrxx (athrxx at scummvm.org)
Date: 2021-01-22T00:47:58+01:00
Commit Message:
AGOS: (ELVIRA/PC98/Jp) - add Japanese font drawing
- Add dual layer graphics display to display the Japanese characters in their proper (double) resolution on top of the graphics. The original actually uses text mode.
- Adapt mouse handling to dual layer code.
- For consistency I got rid of all direct calls to _system->lockScreen() and _system->unlockScreen(), even in the sub engines that aren't affected. I find it easier to debug and to maintain this way.
Changed paths:
engines/agos/agos.cpp
engines/agos/agos.h
engines/agos/animation.cpp
engines/agos/charset-fontdata.cpp
engines/agos/charset.cpp
engines/agos/cursor.cpp
engines/agos/draw.cpp
engines/agos/event.cpp
engines/agos/gfx.cpp
engines/agos/icons.cpp
engines/agos/menus.cpp
engines/agos/script_e1.cpp
engines/agos/verb.cpp
engines/agos/vga.cpp
engines/agos/vga_e2.cpp
engines/agos/vga_pn.cpp
engines/agos/vga_ww.cpp
engines/agos/window.cpp
diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp
index 0bc3811b5c..eb8a5f6cd0 100644
--- a/engines/agos/agos.cpp
+++ b/engines/agos/agos.cpp
@@ -37,6 +37,7 @@
#include "backends/audiocd/audiocd.h"
#include "graphics/surface.h"
+#include "graphics/sjis.h"
#include "audio/mididrv.h"
@@ -140,7 +141,23 @@ AGOSEngine_Elvira2::AGOSEngine_Elvira2(OSystem *system, const AGOSGameDescriptio
}
AGOSEngine_Elvira1::AGOSEngine_Elvira1(OSystem *system, const AGOSGameDescription *gd)
- : AGOSEngine(system, gd) {
+ : AGOSEngine(system, gd), _sjisCurChar(0), _sjisFont(0) {
+}
+
+AGOSEngine_Elvira1::~AGOSEngine_Elvira1() {
+ delete _sjisFont;
+}
+
+Common::Error AGOSEngine_Elvira1::init() {
+ Common::Error ret = AGOSEngine::init();
+ if (ret.getCode() == Common::kNoError && getPlatform() == Common::kPlatformPC98) {
+ _sjisFont = Graphics::FontSJIS::createFont(Common::kPlatformPC98);
+ if (_sjisFont)
+ _sjisFont->toggleFatPrint(true);
+ else
+ error("AGOSEngine_Elvira1::init(): Failed to load SJIS font.");
+ }
+ return ret;
}
AGOSEngine::AGOSEngine(OSystem *system, const AGOSGameDescription *gd)
@@ -541,11 +558,12 @@ AGOSEngine::AGOSEngine(OSystem *system, const AGOSGameDescription *gd)
_moveXMax = 0;
_moveYMax = 0;
+ _forceAscii = false;
+
_vc10BasePtrOld = 0;
memcpy (_hebrewCharWidths,
"\x5\x5\x4\x6\x5\x3\x4\x5\x6\x3\x5\x5\x4\x6\x5\x3\x4\x6\x5\x6\x6\x6\x5\x5\x5\x6\x5\x6\x6\x6\x6\x6", 32);
-
const Common::FSNode gameDataDir(ConfMan.get("path"));
// Add default file directories for Acorn version of
@@ -576,7 +594,15 @@ Common::Error AGOSEngine::init() {
_screenHeight = 200;
}
- initGraphics(_screenWidth, _screenHeight);
+ _internalWidth = _screenWidth;
+ _internalHeight = _screenHeight;
+
+ if (getPlatform() == Common::kPlatformPC98) {
+ _internalWidth <<= 1;
+ _internalHeight <<= 1;
+ }
+
+ initGraphics(_internalWidth, _internalHeight);
_midi = new MidiPlayer();
@@ -601,11 +627,11 @@ Common::Error AGOSEngine::init() {
_backGroundBuf = new Graphics::Surface();
_backGroundBuf->create(_screenWidth, _screenHeight, Graphics::PixelFormat::createFormatCLUT8());
- if (getGameType() == GType_FF || getGameType() == GType_PP) {
+ if (getGameType() == GType_FF || getGameType() == GType_PP || (getGameType() == GType_ELVIRA1 && getPlatform() == Common::kPlatformPC98)) {
_backBuf = new Graphics::Surface();
_backBuf->create(_screenWidth, _screenHeight, Graphics::PixelFormat::createFormatCLUT8());
_scaleBuf = new Graphics::Surface();
- _scaleBuf->create(_screenWidth, _screenHeight, Graphics::PixelFormat::createFormatCLUT8());
+ _scaleBuf->create(_internalWidth, _internalHeight, Graphics::PixelFormat::createFormatCLUT8());
}
if (getGameType() == GType_SIMON2) {
diff --git a/engines/agos/agos.h b/engines/agos/agos.h
index 9da4b0cf91..553131127e 100644
--- a/engines/agos/agos.h
+++ b/engines/agos/agos.h
@@ -58,6 +58,7 @@ class SeekableReadStream;
namespace Graphics {
struct Surface;
+class FontSJIS;
}
namespace AGOS {
@@ -202,7 +203,7 @@ protected:
friend class Debugger;
// Engine APIs
- Common::Error init();
+ virtual Common::Error init();
virtual Common::Error go();
Common::Error run() override {
Common::Error err;
@@ -349,6 +350,7 @@ protected:
uint16 _scrollWidth, _scrollHeight;
const byte *_scrollImage;
byte _boxStarHeight;
+ bool _forceAscii;
SubroutineLine *_classLine;
int16 _classMask, _classMode1, _classMode2;
@@ -445,6 +447,7 @@ protected:
volatile uint16 _fastFadeInFlag;
uint16 _screenWidth, _screenHeight;
+ uint16 _internalWidth, _internalHeight;
uint16 _noOverWrite;
bool _rejectBlock;
@@ -1156,6 +1159,10 @@ protected:
void horizontalScroll(VC10_state *state);
void verticalScroll(VC10_state *state);
+ Graphics::Surface *getBackendSurface() const;
+ void updateBackendSurface(Common::Rect *area = 0) const;
+ virtual void clearHiResTextLayer() {}
+
int vcReadVarOrWord();
uint vcReadNextWord(bool forceLERead = false);
uint vcReadNextByte();
@@ -1552,7 +1559,8 @@ protected:
class AGOSEngine_Elvira1 : public AGOSEngine {
public:
AGOSEngine_Elvira1(OSystem *system, const AGOSGameDescription *gd);
- //~AGOSEngine_Elvira1();
+ ~AGOSEngine_Elvira1() override;
+ Common::Error init() override;
void setupGame() override;
void setupOpcodes() override;
@@ -1626,8 +1634,15 @@ protected:
const OpcodeEntryElvira1 *_opcodesElvira1;
void drawIcon(WindowBlock *window, uint icon, uint x, uint y) override;
+ void windowDrawChar(WindowBlock *window, uint x, uint y, byte chr) override;
+ void addHiResTextDirtyRect(Common::Rect rect);
+ void clearHiResTextLayer() override;
Common::String genSaveName(int slot) const override;
+
+ Graphics::FontSJIS *_sjisFont;
+ Common::Array<Common::Rect> _sjisTextFields;
+ uint16 _sjisCurChar;
};
class AGOSEngine_Elvira2 : public AGOSEngine_Elvira1 {
diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp
index a217536081..1d73a7dd2a 100644
--- a/engines/agos/animation.cpp
+++ b/engines/agos/animation.cpp
@@ -344,9 +344,9 @@ void MoviePlayerDXA::handleNextFrame() {
}
bool MoviePlayerDXA::processFrame() {
- Graphics::Surface *screen = _vm->_system->lockScreen();
+ Graphics::Surface *screen = _vm->getBackendSurface();
copyFrameToBuffer((byte *)screen->getPixels(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch);
- _vm->_system->unlockScreen();
+ _vm->updateBackendSurface();
uint32 soundTime = _mixer->getSoundElapsedTime(_bgSound);
uint32 nextFrameStartTime = ((Video::VideoDecoder::VideoTrack *)getTrack(0))->getNextFrameStartTime();
@@ -495,9 +495,9 @@ void MoviePlayerSMK::nextFrame() {
}
bool MoviePlayerSMK::processFrame() {
- Graphics::Surface *screen = _vm->_system->lockScreen();
+ Graphics::Surface *screen = _vm->getBackendSurface();
copyFrameToBuffer((byte *)screen->getPixels(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch);
- _vm->_system->unlockScreen();
+ _vm->updateBackendSurface();
uint32 waitTime = getTimeToNextFrame();
diff --git a/engines/agos/charset-fontdata.cpp b/engines/agos/charset-fontdata.cpp
index ae31814e43..a10172647c 100644
--- a/engines/agos/charset-fontdata.cpp
+++ b/engines/agos/charset-fontdata.cpp
@@ -28,6 +28,7 @@
#include "agos/intern.h"
#include "graphics/surface.h"
+#include "graphics/sjis.h"
namespace AGOS {
@@ -2921,7 +2922,7 @@ void AGOSEngine::windowDrawChar(WindowBlock *window, uint x, uint y, byte chr) {
_videoLockOut |= 0x8000;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
dst = (byte *)screen->getPixels();
@@ -3019,9 +3020,85 @@ void AGOSEngine::windowDrawChar(WindowBlock *window, uint x, uint y, byte chr) {
dst += dstPitch;
} while (--h);
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x8000;
}
+void AGOSEngine_Elvira1::windowDrawChar(WindowBlock *window, uint x, uint y, byte chr) {
+ if (_language != Common::JA_JPN || _forceAscii) {
+ AGOSEngine::windowDrawChar(window, x, y, chr);
+ return;
+ }
+
+ if (_sjisCurChar) {
+ _sjisCurChar |= (chr << 8);
+ } else {
+ _sjisCurChar = chr;
+ if ((chr >= 0x80 && chr < 0xA0) || chr >= 0xE0)
+ return;
+ }
+
+ _videoLockOut |= 0x8000;
+
+ x = x & ~7;
+ y = (y + 4) & ~3;
+ // PC-98 uses text mode black (hardware colors, not related to the graphics mode palette
+ // colors) for font drawing, so I just set one of the unused black colors (color 33) here.
+ _sjisFont->drawChar(*_scaleBuf, _sjisCurChar, x << 1, y << 1, 33, 0);
+ Common::Rect dirtyRect(x, y, x + (_sjisFont->getCharWidth(_sjisCurChar) >> 1), y + (_sjisFont->getFontHeight() >> 1));
+ addHiResTextDirtyRect(dirtyRect);
+ updateBackendSurface(&dirtyRect);
+ _sjisCurChar = 0;
+
+ _videoLockOut &= ~0x8000;
+}
+
+void AGOSEngine_Elvira1::addHiResTextDirtyRect(Common::Rect rect) {
+ rect.left >>= 1;
+ rect.top <<= 1;
+ rect.right >>= 1;
+ rect.bottom <<= 1;
+
+ for (Common::Array<Common::Rect>::iterator i = _sjisTextFields.begin(); i != _sjisTextFields.end(); ++i) {
+ // Merge rects if it makes sense, but only once.
+ if (rect.left <= i->right && rect.right >= i->left && rect.top <= i->bottom && rect.bottom >= i->top) {
+ i->left = MIN<int16>(i->left, rect.left);
+ i->top = MIN<int16>(i->top, rect.top);
+ i->right = MAX<int16>(i->right, rect.right);
+ i->bottom = MAX<int16>(i->bottom, rect.bottom);
+ return;
+ }
+ }
+
+ _sjisTextFields.push_back(rect);
+}
+
+void AGOSEngine_Elvira1::clearHiResTextLayer() {
+ if (getPlatform() != Common::kPlatformPC98)
+ return;
+
+ void *p = _scaleBuf->getPixels();
+ assert(p);
+
+ if (_sjisTextFields.size() < 10) {
+ for (Common::Array<Common::Rect>::iterator i = _sjisTextFields.begin(); i != _sjisTextFields.end(); ++i) {
+ uint16 w = i->width();
+ uint16 ptch = _scaleBuf->pitch >> 2;
+ uint32 *dst = (uint32*)p + i->top * ptch + i->left;
+ for (uint32 *end = dst + i->height() * ptch; dst < end; dst += ptch)
+ Common::fill<uint32*, uint32>(dst, &dst[w], 0);
+ i->left <<= 1;
+ i->top >>= 1;
+ i->right <<= 1;
+ i->bottom >>= 1;
+ updateBackendSurface(i);
+ }
+ } else {
+ memset(p, 0, _scaleBuf->w * _scaleBuf->h);
+ updateBackendSurface();
+ }
+ _sjisTextFields.clear();
+}
+
} // End of namespace AGOS
diff --git a/engines/agos/charset.cpp b/engines/agos/charset.cpp
index 4d83a4ef1c..c3e8e49d63 100644
--- a/engines/agos/charset.cpp
+++ b/engines/agos/charset.cpp
@@ -148,6 +148,11 @@ void AGOSEngine::justifyOutPut(byte chr) {
_printCharPixelCount = 0;
doOutput(&chr, 1);
clsCheck(_textWindow);
+ } else if (getLanguage() == Common::JA_JPN) {
+ // Japanese has no word wrapping
+ _lettersToPrintBuf[0] = chr;
+ _lettersToPrintBuf[1] = '\0';
+ doOutput(_lettersToPrintBuf, 1);
} else if (chr == 0 || chr == ' ' || chr == 10) {
bool fit;
@@ -215,6 +220,7 @@ void AGOSEngine_PN::windowPutChar(WindowBlock *window, byte c, byte b) {
void AGOSEngine::windowPutChar(WindowBlock *window, byte c, byte b) {
byte width = 6;
+ byte textColumnWidth = 8;
if (c == 12) {
clearWindow(window);
@@ -256,8 +262,9 @@ void AGOSEngine::windowPutChar(WindowBlock *window, byte c, byte b) {
return;
}
- // Ignore invalid characters
- if (c - 32 > 98)
+ if (_language == Common::JA_JPN)
+ textColumnWidth = width = 4;
+ else if (c - 32 > 98) // Ignore invalid characters
return;
if (window->textLength == window->textMaxLength) {
@@ -278,16 +285,15 @@ void AGOSEngine::windowPutChar(WindowBlock *window, byte c, byte b) {
windowDrawChar(window, (window->width + window->x - window->textColumn) * 8, window->textRow * 8 + window->y, c);
window->textLength++;
} else {
- windowDrawChar(window, (window->textColumn + window->x) * 8, window->textRow * 8 + window->y, c);
-
+ windowDrawChar(window, window->x * 8 + window->textColumn * textColumnWidth, window->textRow * 8 + window->y, c);
window->textLength++;
- window->textColumnOffset += 6;
+ window->textColumnOffset += width;
if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
if (c == 'i' || c == 'l')
window->textColumnOffset -= 2;
}
- if (window->textColumnOffset >= 8) {
- window->textColumnOffset -= 8;
+ if (window->textColumnOffset >= textColumnWidth) {
+ window->textColumnOffset -= textColumnWidth;
window->textColumn++;
}
}
@@ -354,24 +360,37 @@ void AGOSEngine::windowScroll(WindowBlock *window) {
_videoLockOut |= 0x8000;
if (window->height != 1) {
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *src, *dst;
- uint16 w, h;
+ uint16 w1, h1, w2, h2;
- w = window->width * 8;
- h = (window->height -1) * 8;
+ w1 = w2 = window->width * 8;
+ h1 = h2 = (window->height -1) * 8;
dst = (byte *)screen->getBasePtr(window->x * 8, window->y);
src = dst + 8 * screen->pitch;
do {
- memcpy(dst, src, w);
+ memcpy(dst, src, w1);
src += screen->pitch;
dst += screen->pitch;
- } while (--h);
+ } while (--h1);
+
+ if (getGameId() == GID_ELVIRA1 && getPlatform() == Common::kPlatformPC98) {
+ w1 = w2 << 1;
+ h1 = h2 << 1;
+ dst = (byte *)_scaleBuf->getBasePtr(window->x * 16, window->y * 2);
+ src = dst + 16 * screen->pitch;
+ do {
+ memcpy(dst, src, w1);
+ src += screen->pitch;
+ dst += screen->pitch;
+ } while (--h1);
+ }
- _system->unlockScreen();
+ Common::Rect dirtyRect(window->x * 8, window->y, window->x * 8 + w2, window->y + h2);
+ updateBackendSurface(&dirtyRect);
}
colorBlock(window, window->x * 8, (window->height - 1) * 8 + window->y, window->width * 8, 8);
diff --git a/engines/agos/cursor.cpp b/engines/agos/cursor.cpp
index 19a38116cf..6a16b764b4 100644
--- a/engines/agos/cursor.cpp
+++ b/engines/agos/cursor.cpp
@@ -559,6 +559,10 @@ void AGOSEngine::handleMouseMoved() {
_needHitAreaRecalc++;
}
} else if (getGameType() == GType_ELVIRA1) {
+ if (getPlatform() == Common::kPlatformPC98) {
+ _mouse.x >>= 1;
+ _mouse.y >>= 1;
+ }
if (_mouseCursor != _variableArray[438]) {
_mouseCursor = _variableArray[438];
_needHitAreaRecalc++;
@@ -784,6 +788,12 @@ static const byte mouseCursorPalette[] = {
void AGOSEngine::initMouse() {
_maxCursorWidth = 16;
_maxCursorHeight = 16;
+
+ if (getGameId() == GID_ELVIRA1 && getPlatform() == Common::kPlatformPC98) {
+ _maxCursorWidth <<= 1;
+ _maxCursorHeight <<= 1;
+ }
+
_mouseData = (byte *)calloc(_maxCursorWidth * _maxCursorHeight, 1);
memset(_mouseData, 0xFF, _maxCursorWidth * _maxCursorHeight);
@@ -864,7 +874,21 @@ void AGOSEngine::drawMousePointer() {
src += 2;
}
- CursorMan.replaceCursor(_mouseData, 16, 16, 0, 0, 0xFF);
+ if (getGameId() == GID_ELVIRA1 && getPlatform() == Common::kPlatformPC98) {
+ // Simple 2x upscaling for the cursor in dual layer hi-res mode.
+ uint8 ptch = 16;
+ uint16 *dst1 = &((uint16*)_mouseData)[16 * 16 * 2 - 1];
+ uint16 *dst2 = dst1 - ptch;
+ for (const byte *src = &_mouseData[16 * 16 - 1]; src >= _mouseData; --src) {
+ *dst1-- = *dst2-- = (*src << 8) | *src;
+ if (!(ptch = (ptch - 1) % 16)) {
+ dst1 -= 16;
+ dst2 -= 16;
+ }
+ }
+ }
+
+ CursorMan.replaceCursor(_mouseData, _maxCursorWidth, _maxCursorHeight, 0, 0, 0xFF);
}
}
diff --git a/engines/agos/draw.cpp b/engines/agos/draw.cpp
index f7ceeb70c1..a627bb54d8 100644
--- a/engines/agos/draw.cpp
+++ b/engines/agos/draw.cpp
@@ -536,7 +536,7 @@ void AGOSEngine::displayBoxStars() {
ha = _hitAreas;
count = ARRAYSIZE(_hitAreas);
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
do {
if (ha->id != 0 && ha->flags & kBFBoxInUse && !(ha->flags & kBFBoxDead)) {
@@ -604,7 +604,7 @@ void AGOSEngine::displayBoxStars() {
}
} while (ha++, --count);
- _system->unlockScreen();
+ updateBackendSurface();
delay(100);
@@ -724,7 +724,7 @@ void AGOSEngine::fillBackFromBackGround(uint16 height, uint16 width) {
}
void AGOSEngine::fillBackFromFront() {
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *src = (byte *)screen->getPixels();
byte *dst = getBackBuf();
@@ -733,7 +733,7 @@ void AGOSEngine::fillBackFromFront() {
src += screen->pitch;
dst += _backBuf->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
}
void AGOSEngine::fillBackGroundFromBack() {
@@ -747,7 +747,7 @@ void AGOSEngine::fillBackGroundFromBack() {
}
void AGOSEngine::fillBackGroundFromFront() {
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *src = (byte *)screen->getPixels();
byte *dst = getBackGround();
@@ -756,7 +756,7 @@ void AGOSEngine::fillBackGroundFromFront() {
src += screen->pitch;
dst += _backGroundBuf->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
}
void AGOSEngine::setMoveRect(uint16 x, uint16 y, uint16 width, uint16 height) {
@@ -782,7 +782,7 @@ void AGOSEngine::displayScreen() {
}
}
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
if (getGameType() == GType_PP || getGameType() == GType_FF) {
byte *src = getBackBuf();
byte *dst = (byte *)screen->getPixels();
@@ -841,7 +841,7 @@ void AGOSEngine::displayScreen() {
}
}
- _system->unlockScreen();
+ updateBackendSurface();
if (getGameType() == GType_FF && _scrollFlag) {
scrollScreen();
diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp
index 3c16590aff..2708d0a681 100644
--- a/engines/agos/event.cpp
+++ b/engines/agos/event.cpp
@@ -365,7 +365,7 @@ static const byte _image4[32] = {
void AGOSEngine::drawStuff(const byte *src, uint xoffs) {
const uint8 y = (getPlatform() == Common::kPlatformAtariST) ? 132 : 135;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dst = (byte *)screen->getBasePtr(xoffs, y);
for (uint h = 0; h < 6; h++) {
@@ -374,7 +374,7 @@ void AGOSEngine::drawStuff(const byte *src, uint xoffs) {
dst += screen->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
}
void AGOSEngine::playerDamageEvent(VgaTimerEntry * vte, uint dx) {
diff --git a/engines/agos/gfx.cpp b/engines/agos/gfx.cpp
index 8bdcfcfa6d..07342277a5 100644
--- a/engines/agos/gfx.cpp
+++ b/engines/agos/gfx.cpp
@@ -640,7 +640,7 @@ void AGOSEngine_Simon1::drawImage(VC10_state *state) {
if (!drawImage_clip(state))
return;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
if (getFeatures() & GF_32COLOR)
state->palette = 0xC0;
@@ -738,7 +738,7 @@ void AGOSEngine_Simon1::drawImage(VC10_state *state) {
drawVertImage(state);
}
- _system->unlockScreen();
+ updateBackendSurface();
}
void AGOSEngine::drawBackGroundImage(VC10_state *state) {
@@ -857,7 +857,7 @@ void AGOSEngine::drawImage(VC10_state *state) {
if (!drawImage_clip(state))
return;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
uint16 xoffs = 0, yoffs = 0;
if (getGameType() == GType_WW) {
@@ -951,7 +951,7 @@ void AGOSEngine::drawImage(VC10_state *state) {
drawVertImage(state);
}
- _system->unlockScreen();
+ updateBackendSurface();
}
void AGOSEngine::horizontalScroll(VC10_state *state) {
@@ -1023,6 +1023,61 @@ void AGOSEngine::verticalScroll(VC10_state *state) {
}
}
+Graphics::Surface *AGOSEngine::getBackendSurface() const {
+ return (getGameId() == GID_ELVIRA1 && getPlatform() == Common::kPlatformPC98) ? _backBuf : _system->lockScreen();
+}
+
+void AGOSEngine::updateBackendSurface(Common::Rect *area) const {
+ if (getGameId() == GID_ELVIRA1 && getPlatform() == Common::kPlatformPC98) {
+ int x = 0;
+ int y = 0;
+ int w = _screenWidth;
+ int h = _screenHeight;
+
+ if (area) {
+ x = area->left;
+ y = area->top;
+ w = area->width();
+ h = area->height();
+ }
+
+ Graphics::Surface *screen = _system->lockScreen();
+
+ int src0Pitch = _backBuf->pitch;
+ int src1Pitch = _scaleBuf->pitch;
+ int dst1Pitch = screen->pitch;
+ const byte *src00 = (byte*)_backBuf->getBasePtr(x, y);
+ const byte *src10 = (byte*)_scaleBuf->getBasePtr(x << 1, y << 1);
+ const byte *src11 = src10 + src1Pitch;
+ byte *dst10 = (byte*)screen->getBasePtr(x << 1, y << 1);
+ byte *dst11 = dst10 + dst1Pitch;
+ src0Pitch -= w;
+ src1Pitch += (src1Pitch - (w << 1));
+ dst1Pitch += (dst1Pitch - (w << 1));
+
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ uint8 v0 = *src00++;
+ uint8 v1 = *src10++;
+ *dst10++ = v1 ? v1 : v0;
+ v1 = *src10++;
+ *dst10++ = v1 ? v1 : v0;
+ v1 = *src11++;
+ *dst11++ = v1 ? v1 : v0;
+ v1 = *src11++;
+ *dst11++ = v1 ? v1 : v0;
+ }
+ src00 += src0Pitch;
+ src10 += src1Pitch;
+ src11 += src1Pitch;
+ dst10 += dst1Pitch;
+ dst11 += dst1Pitch;
+ }
+ }
+
+ _system->unlockScreen();
+}
+
void AGOSEngine::paletteFadeOut(byte *palPtr, uint num, uint size) {
byte *p = palPtr;
@@ -1372,7 +1427,7 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId, bool specialCas
uint width = _videoWindows[updateWindow * 4 + 2] * 16;
uint height = _videoWindows[updateWindow * 4 + 3];
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dst = (byte *)_backGroundBuf->getBasePtr(xoffs, yoffs);
byte *src = 0;
uint srcWidth = 0;
@@ -1389,7 +1444,7 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId, bool specialCas
src = (byte *)screen->getBasePtr(xoffs, yoffs);
srcWidth = screen->pitch;
} else {
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x20;
return;
}
@@ -1404,7 +1459,7 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId, bool specialCas
src = (byte *)screen->getBasePtr(xoffs, yoffs);
srcWidth = screen->pitch;
} else {
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x20;
return;
}
@@ -1416,7 +1471,7 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId, bool specialCas
src = (byte *)screen->getBasePtr(xoffs, yoffs);
srcWidth = screen->pitch;
} else {
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x20;
return;
}
@@ -1428,7 +1483,7 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId, bool specialCas
src = (byte *)screen->getBasePtr(xoffs, yoffs);
srcWidth = screen->pitch;
} else {
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x20;
return;
}
@@ -1474,7 +1529,7 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId, bool specialCas
}
}
- _system->unlockScreen();
+ updateBackendSurface();
}
_videoLockOut &= ~0x20;
@@ -1485,7 +1540,7 @@ void AGOSEngine::drawEdging() {
byte *dst;
uint8 color = (getPlatform() == Common::kPlatformDOS) ? 7 : 15;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
dst = (byte *)screen->getBasePtr(0, 136);
uint8 len = 52;
@@ -1499,7 +1554,7 @@ void AGOSEngine::drawEdging() {
dst = (byte *)screen->getBasePtr(0, 187);
memset(dst, color, _screenWidth);
- _system->unlockScreen();
+ updateBackendSurface();
}
} // End of namespace AGOS
diff --git a/engines/agos/icons.cpp b/engines/agos/icons.cpp
index 364fbf5f15..3bbec55f3e 100644
--- a/engines/agos/icons.cpp
+++ b/engines/agos/icons.cpp
@@ -202,7 +202,7 @@ void AGOSEngine_Simon2::drawIcon(WindowBlock *window, uint icon, uint x, uint y)
_videoLockOut |= 0x8000;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
dst = (byte *)screen->getPixels();
dst += 110;
@@ -217,7 +217,7 @@ void AGOSEngine_Simon2::drawIcon(WindowBlock *window, uint icon, uint x, uint y)
src += READ_LE_UINT16(src + icon * 4 + 2);
decompressIcon(dst, src, 20, 10, 208, screen->pitch);
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x8000;
}
@@ -228,7 +228,7 @@ void AGOSEngine_Simon1::drawIcon(WindowBlock *window, uint icon, uint x, uint y)
_videoLockOut |= 0x8000;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
dst = (byte *)screen->getPixels();
dst += (x + window->x) * 8;
@@ -245,7 +245,7 @@ void AGOSEngine_Simon1::drawIcon(WindowBlock *window, uint icon, uint x, uint y)
decompressIcon(dst, src, 24, 12, 224, screen->pitch);
}
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x8000;
}
@@ -256,7 +256,7 @@ void AGOSEngine_Waxworks::drawIcon(WindowBlock *window, uint icon, uint x, uint
_videoLockOut |= 0x8000;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
dst = (byte *)screen->getPixels();
dst += (x + window->x) * 8;
@@ -273,7 +273,7 @@ void AGOSEngine_Waxworks::drawIcon(WindowBlock *window, uint icon, uint x, uint
decompressIcon(dst, src, 24, 10, color, screen->pitch);
}
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x8000;
}
@@ -284,7 +284,7 @@ void AGOSEngine_Elvira2::drawIcon(WindowBlock *window, uint icon, uint x, uint y
_videoLockOut |= 0x8000;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
dst = (byte *)screen->getPixels();
dst += (x + window->x) * 8;
@@ -301,7 +301,7 @@ void AGOSEngine_Elvira2::drawIcon(WindowBlock *window, uint icon, uint x, uint y
decompressIcon(dst, src, 24, 12, color, screen->pitch);
}
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x8000;
}
@@ -312,7 +312,7 @@ void AGOSEngine_Elvira1::drawIcon(WindowBlock *window, uint icon, uint x, uint y
_videoLockOut |= 0x8000;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
dst = (byte *)screen->getPixels();
dst += (x + window->x) * 8;
@@ -328,7 +328,7 @@ void AGOSEngine_Elvira1::drawIcon(WindowBlock *window, uint icon, uint x, uint y
decompressIconPlanar(dst, src, 24, 12, 16, screen->pitch, false);
}
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x8000;
}
@@ -339,7 +339,7 @@ void AGOSEngine::drawIcon(WindowBlock *window, uint icon, uint x, uint y) {
_videoLockOut |= 0x8000;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
dst = (byte *)screen->getBasePtr(x * 8, y);
src = _iconFilePtr + icon * 146;
@@ -365,7 +365,7 @@ void AGOSEngine::drawIcon(WindowBlock *window, uint icon, uint x, uint y) {
}
}
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x8000;
}
@@ -951,7 +951,7 @@ void AGOSEngine::drawArrow(uint16 x, uint16 y, int8 dir) {
src = _arrowImage;
}
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dst = (byte *)screen->getBasePtr(x * 8, y);
for (h = 0; h < 19; h++) {
@@ -964,7 +964,7 @@ void AGOSEngine::drawArrow(uint16 x, uint16 y, int8 dir) {
dst+= screen->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
}
void AGOSEngine_Simon1::removeArrows(WindowBlock *window, uint num) {
@@ -1042,7 +1042,7 @@ static const byte hitBarData[12 * 7] = {
// Personal Nightmare specific
void AGOSEngine_PN::drawIconHitBar() {
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dst = (byte *)screen->getBasePtr(6 * 8, 3);
const byte *src = hitBarData;
uint8 color = (getPlatform() == Common::kPlatformDOS) ? 7 : 15;
@@ -1061,7 +1061,7 @@ void AGOSEngine_PN::drawIconHitBar() {
dst += screen->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
}
void AGOSEngine_PN::iconPage() {
diff --git a/engines/agos/menus.cpp b/engines/agos/menus.cpp
index 77a37cb601..7de21be146 100644
--- a/engines/agos/menus.cpp
+++ b/engines/agos/menus.cpp
@@ -163,7 +163,7 @@ void AGOSEngine::unlightMenuStrip() {
mouseOff();
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
src = (byte *)screen->getBasePtr(272, 8);
w = 48;
h = 82;
@@ -179,7 +179,7 @@ void AGOSEngine::unlightMenuStrip() {
for (i = 120; i != 130; i++)
disableBox(i);
- _system->unlockScreen();
+ updateBackendSurface();
mouseOn();
}
@@ -191,7 +191,7 @@ void AGOSEngine::lightMenuBox(uint hitarea) {
mouseOff();
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
src = (byte *)screen->getBasePtr(ha->x, ha->y);
w = ha->width;
h = ha->height;
@@ -204,7 +204,7 @@ void AGOSEngine::lightMenuBox(uint hitarea) {
src += screen->pitch;
} while (--h);
- _system->unlockScreen();
+ updateBackendSurface();
mouseOn();
}
diff --git a/engines/agos/script_e1.cpp b/engines/agos/script_e1.cpp
index 38b29d475e..85f92bcf7a 100644
--- a/engines/agos/script_e1.cpp
+++ b/engines/agos/script_e1.cpp
@@ -1166,6 +1166,7 @@ void AGOSEngine::printStats() {
window->flags = 1;
mouseOff();
+ _forceAscii = true;
// Strength
val = _variableArray[0];
@@ -1215,6 +1216,7 @@ void AGOSEngine::printStats() {
val = 99;
writeChar(window, 36, 133, 4, val);
+ _forceAscii = false;
mouseOn();
}
diff --git a/engines/agos/verb.cpp b/engines/agos/verb.cpp
index fb3878381f..7f1ff7592c 100644
--- a/engines/agos/verb.cpp
+++ b/engines/agos/verb.cpp
@@ -985,7 +985,7 @@ void AGOSEngine::invertBox(HitArea *ha, byte a, byte b, byte c, byte d) {
_videoLockOut |= 0x8000;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
src = (byte *)screen->getBasePtr(ha->x, ha->y);
// WORKAROUND: Hitareas for saved game names aren't adjusted for scrolling locations
@@ -1041,7 +1041,7 @@ void AGOSEngine::invertBox(HitArea *ha, byte a, byte b, byte c, byte d) {
src += screen->pitch;
} while (--h);
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x8000;
}
diff --git a/engines/agos/vga.cpp b/engines/agos/vga.cpp
index 65f3efcd0d..c3790de402 100644
--- a/engines/agos/vga.cpp
+++ b/engines/agos/vga.cpp
@@ -1182,7 +1182,7 @@ void AGOSEngine::vc31_setWindow() {
void AGOSEngine::vc32_saveScreen() {
if (getGameType() == GType_PN) {
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dst = getBackGround();
byte *src = (byte *)screen->getPixels();
for (int i = 0; i < _screenHeight; i++) {
@@ -1190,7 +1190,7 @@ void AGOSEngine::vc32_saveScreen() {
dst += _backGroundBuf->pitch;
src += screen->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
} else {
uint16 xoffs = _videoWindows[4 * 4 + 0] * 16;
uint16 yoffs = _videoWindows[4 * 4 + 1];
@@ -1251,13 +1251,14 @@ void AGOSEngine::clearVideoWindow(uint16 num, uint16 color) {
}
if (getGameType() == GType_ELVIRA1 && num == 3) {
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dst = (byte *)screen->getPixels();
for (int i = 0; i < _screenHeight; i++) {
memset(dst, color, _screenWidth);
dst += screen->pitch;
}
- _system->unlockScreen();
+ clearHiResTextLayer();
+ updateBackendSurface();
} else {
const uint16 *vlut = &_videoWindows[num * 4];
uint16 xoffs = (vlut[0] - _videoWindows[16]) * 16;
diff --git a/engines/agos/vga_e2.cpp b/engines/agos/vga_e2.cpp
index a26f189c43..f4ecd11a05 100644
--- a/engines/agos/vga_e2.cpp
+++ b/engines/agos/vga_e2.cpp
@@ -89,7 +89,7 @@ void AGOSEngine::vc45_setWindowPalette() {
dst += width * 2;
}
} else {
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dst = (byte *)screen->getBasePtr(vlut[0] * 16, vlut[1]);
if (getGameType() == GType_ELVIRA2 && num == 7) {
@@ -107,7 +107,7 @@ void AGOSEngine::vc45_setWindowPalette() {
dst += screen->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
}
}
@@ -223,7 +223,7 @@ void AGOSEngine::vc53_dissolveIn() {
uint16 count = dissolveCheck * 2;
while (count--) {
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dstPtr = (byte *)screen->getBasePtr(x, y);
yoffs = _rnd.getRandomNumber(dissolveY);
@@ -264,7 +264,7 @@ void AGOSEngine::vc53_dissolveIn() {
*dst &= color;
*dst |= *src & 0xF;
- _system->unlockScreen();
+ updateBackendSurface();
dissolveCount--;
if (!dissolveCount) {
@@ -296,7 +296,7 @@ void AGOSEngine::vc54_dissolveOut() {
uint16 count = dissolveCheck * 2;
while (count--) {
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dstPtr = (byte *)screen->getBasePtr(x, y);
color |= dstPtr[0] & 0xF0;
@@ -318,7 +318,7 @@ void AGOSEngine::vc54_dissolveOut() {
dst += xoffs;
*dst = color;
- _system->unlockScreen();
+ updateBackendSurface();
dissolveCount--;
if (!dissolveCount) {
@@ -378,7 +378,7 @@ void AGOSEngine::fullFade() {
}
void AGOSEngine::vc56_fullScreen() {
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dst = (byte *)screen->getPixels();
byte *src = _curVgaFile2 + 800;
@@ -387,7 +387,7 @@ void AGOSEngine::vc56_fullScreen() {
src += 320;
dst += screen->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
fullFade();
}
diff --git a/engines/agos/vga_pn.cpp b/engines/agos/vga_pn.cpp
index 306c41c71c..629fd2882b 100644
--- a/engines/agos/vga_pn.cpp
+++ b/engines/agos/vga_pn.cpp
@@ -154,7 +154,7 @@ void AGOSEngine::vc48_specialEffect() {
if (getPlatform() == Common::kPlatformDOS) {
if (num == 1) {
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dst = (byte *)screen->getPixels();
for (uint h = 0; h < _screenHeight; h++) {
@@ -164,7 +164,7 @@ void AGOSEngine::vc48_specialEffect() {
}
dst += screen->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
} else if (num == 2) {
const char *str = "There are gurgling noises from the sink.";
for (; *str; str++)
@@ -204,13 +204,13 @@ void AGOSEngine_PN::clearVideoWindow(uint16 num, uint16 color) {
uint16 xoffs = vlut[0] * 16;
uint16 yoffs = vlut[1];
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dst = (byte *)screen->getBasePtr(xoffs, yoffs);
for (uint h = 0; h < vlut[3]; h++) {
memset(dst, color, vlut[2] * 16);
dst += screen->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
}
} // End of namespace AGOS
diff --git a/engines/agos/vga_ww.cpp b/engines/agos/vga_ww.cpp
index 5bf8f84551..ceffe77101 100644
--- a/engines/agos/vga_ww.cpp
+++ b/engines/agos/vga_ww.cpp
@@ -142,7 +142,7 @@ void AGOSEngine::vc61() {
byte *src, *dst, *dstPtr;
uint h, tmp;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
dstPtr = (byte *)screen->getPixels();
if (a == 6) {
@@ -175,7 +175,7 @@ void AGOSEngine::vc61() {
}
if (a != 6) {
- _system->unlockScreen();
+ updateBackendSurface();
return;
}
@@ -189,7 +189,7 @@ void AGOSEngine::vc61() {
dst += screen->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
if (a == 6)
fullFade();
diff --git a/engines/agos/window.cpp b/engines/agos/window.cpp
index de0b768d02..8be23ef249 100644
--- a/engines/agos/window.cpp
+++ b/engines/agos/window.cpp
@@ -65,11 +65,13 @@ WindowBlock *AGOSEngine::openWindow(uint x, uint y, uint w, uint h, uint flags,
window->textRow = 0;
window->scrollY = 0;
- // Characters are 6 pixels
+ // Characters are 6 pixels (except Japanese: when downscaled, 1-byte characters are 4 pixels, 2-byte characters are 8 pixels)
if (getGameType() == GType_ELVIRA2)
window->textMaxLength = (window->width * 8 - 4) / 6;
else if (getGameType() == GType_PN)
window->textMaxLength = window->width * 8 / 6 + 1;
+ else if (getGameType() == GType_ELVIRA1 && getPlatform() == Common::kPlatformPC98)
+ window->textMaxLength = window->width << 1;
else
window->textMaxLength = window->width * 8 / 6;
@@ -107,6 +109,7 @@ void AGOSEngine::closeWindow(uint a) {
}
void AGOSEngine::clearWindow(WindowBlock *window) {
+ clearHiResTextLayer();
if (window->flags & 0x10)
restoreWindow(window);
else
@@ -169,7 +172,7 @@ void AGOSEngine::colorWindow(WindowBlock *window) {
void AGOSEngine::colorBlock(WindowBlock *window, uint16 x, uint16 y, uint16 w, uint16 h) {
_videoLockOut |= 0x8000;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
byte *dst = (byte *)screen->getBasePtr(x, y);
uint8 color = window->fillColor;
@@ -181,7 +184,7 @@ void AGOSEngine::colorBlock(WindowBlock *window, uint16 x, uint16 y, uint16 w, u
dst += screen->pitch;
} while (--h);
- _system->unlockScreen();
+ updateBackendSurface();
_videoLockOut &= ~0x8000;
}
@@ -231,7 +234,7 @@ void AGOSEngine::restoreBlock(uint16 x, uint16 y, uint16 w, uint16 h) {
byte *dst, *src;
uint i;
- Graphics::Surface *screen = _system->lockScreen();
+ Graphics::Surface *screen = getBackendSurface();
dst = (byte *)screen->getPixels();
src = getBackGround();
@@ -250,7 +253,7 @@ void AGOSEngine::restoreBlock(uint16 x, uint16 y, uint16 w, uint16 h) {
src += _backGroundBuf->pitch;
}
- _system->unlockScreen();
+ updateBackendSurface();
}
void AGOSEngine::setTextColor(uint color) {
Commit: 8d17584247e9c0820ac84e3bf290722b697a3aac
https://github.com/scummvm/scummvm/commit/8d17584247e9c0820ac84e3bf290722b697a3aac
Author: athrxx (athrxx at scummvm.org)
Date: 2021-01-22T00:47:58+01:00
Commit Message:
AGOS: (ELVIRA/PC98/Jp) - more dirty rect handling
Make more use of dirty rect handling (applies to the PC-98 version only, the other versions don't need that).
Changed paths:
engines/agos/charset-fontdata.cpp
engines/agos/event.cpp
engines/agos/gfx.cpp
engines/agos/icons.cpp
engines/agos/menus.cpp
engines/agos/verb.cpp
engines/agos/window.cpp
diff --git a/engines/agos/charset-fontdata.cpp b/engines/agos/charset-fontdata.cpp
index a10172647c..664b6bb964 100644
--- a/engines/agos/charset-fontdata.cpp
+++ b/engines/agos/charset-fontdata.cpp
@@ -3020,7 +3020,8 @@ void AGOSEngine::windowDrawChar(WindowBlock *window, uint x, uint y, byte chr) {
dst += dstPitch;
} while (--h);
- updateBackendSurface();
+ Common::Rect dirtyRect(x, y, x + 6, y + 8);
+ updateBackendSurface(&dirtyRect);
_videoLockOut &= ~0x8000;
}
diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp
index 2708d0a681..4a8f52a44d 100644
--- a/engines/agos/event.cpp
+++ b/engines/agos/event.cpp
@@ -374,7 +374,8 @@ void AGOSEngine::drawStuff(const byte *src, uint xoffs) {
dst += screen->pitch;
}
- updateBackendSurface();
+ Common::Rect dirtyRect(xoffs, y, xoffs + 4, y + 6);
+ updateBackendSurface(&dirtyRect);
}
void AGOSEngine::playerDamageEvent(VgaTimerEntry * vte, uint dx) {
diff --git a/engines/agos/gfx.cpp b/engines/agos/gfx.cpp
index 07342277a5..a7e8b0c15e 100644
--- a/engines/agos/gfx.cpp
+++ b/engines/agos/gfx.cpp
@@ -738,7 +738,7 @@ void AGOSEngine_Simon1::drawImage(VC10_state *state) {
drawVertImage(state);
}
- updateBackendSurface();
+ updateBackendSurface();
}
void AGOSEngine::drawBackGroundImage(VC10_state *state) {
@@ -859,7 +859,7 @@ void AGOSEngine::drawImage(VC10_state *state) {
Graphics::Surface *screen = getBackendSurface();
- uint16 xoffs = 0, yoffs = 0;
+ uint16 xoffs = 0, yoffs = 0, xmax = 0, ymax = 0;
if (getGameType() == GType_WW) {
if (_windowNum == 4 || (_windowNum >= 10 && _windowNum <= 27)) {
state->surf_addr = (byte *)_window4BackScn->getPixels();
@@ -868,8 +868,8 @@ void AGOSEngine::drawImage(VC10_state *state) {
xoffs = ((vlut[0] - _videoWindows[16]) * 2 + state->x) * 8;
yoffs = (vlut[1] - _videoWindows[17] + state->y);
- uint xmax = (xoffs + state->draw_width * 2);
- uint ymax = (yoffs + state->draw_height);
+ xmax = (xoffs + state->draw_width * 2);
+ ymax = (yoffs + state->draw_height);
setMoveRect(xoffs, yoffs, xmax, ymax);
_window4Flag = 1;
@@ -879,6 +879,8 @@ void AGOSEngine::drawImage(VC10_state *state) {
xoffs = (vlut[0] * 2 + state->x) * 8;
yoffs = vlut[1] + state->y;
+ xmax = (xoffs + state->draw_width * 2);
+ ymax = (yoffs + state->draw_height);
}
} else if (getGameType() == GType_ELVIRA2) {
if (_windowNum == 4 || _windowNum >= 10) {
@@ -888,8 +890,8 @@ void AGOSEngine::drawImage(VC10_state *state) {
xoffs = ((vlut[0] - _videoWindows[16]) * 2 + state->x) * 8;
yoffs = (vlut[1] - _videoWindows[17] + state->y);
- uint xmax = (xoffs + state->draw_width * 2);
- uint ymax = (yoffs + state->draw_height);
+ xmax = (xoffs + state->draw_width * 2);
+ ymax = (yoffs + state->draw_height);
setMoveRect(xoffs, yoffs, xmax, ymax);
_window4Flag = 1;
@@ -899,6 +901,8 @@ void AGOSEngine::drawImage(VC10_state *state) {
xoffs = (vlut[0] * 2 + state->x) * 8;
yoffs = vlut[1] + state->y;
+ xmax = (xoffs + state->draw_width * 2);
+ ymax = (yoffs + state->draw_height);
}
} else if (getGameType() == GType_ELVIRA1) {
if (_windowNum == 6) {
@@ -907,12 +911,16 @@ void AGOSEngine::drawImage(VC10_state *state) {
xoffs = state->x * 8;
yoffs = state->y;
+ xmax = xoffs + vlut[2] * 16;
+ ymax = yoffs + vlut[3];
} else if (_windowNum == 2 || _windowNum == 3) {
state->surf_addr = (byte *)screen->getPixels();
state->surf_pitch = screen->pitch;
xoffs = (vlut[0] * 2 + state->x) * 8;
yoffs = vlut[1] + state->y;
+ xmax = xoffs + state->draw_width * 2;
+ ymax = yoffs + state->draw_height;
} else {
state->surf_addr = (byte *)_window4BackScn->getPixels();
state->surf_pitch = _videoWindows[18] * 16;
@@ -920,8 +928,8 @@ void AGOSEngine::drawImage(VC10_state *state) {
xoffs = ((vlut[0] - _videoWindows[16]) * 2 + state->x) * 8;
yoffs = (vlut[1] - _videoWindows[17] + state->y);
- uint xmax = (xoffs + state->draw_width * 2);
- uint ymax = (yoffs + state->draw_height);
+ xmax = (xoffs + state->draw_width * 2);
+ ymax = (yoffs + state->draw_height);
setMoveRect(xoffs, yoffs, xmax, ymax);
_window4Flag = 1;
@@ -951,7 +959,9 @@ void AGOSEngine::drawImage(VC10_state *state) {
drawVertImage(state);
}
- updateBackendSurface();
+
+ Common::Rect dirtyRect(xoffs, yoffs, xmax, ymax);
+ updateBackendSurface(&dirtyRect);
}
void AGOSEngine::horizontalScroll(VC10_state *state) {
@@ -1527,9 +1537,15 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId, bool specialCas
dst[w] += 0x10;
dst += screen->pitch;
}
+
+ if (getPlatform() == Common::kPlatformPC98) {
+ Common::Rect dirtyRect(0, 133, _screenWidth, _screenHeight);
+ updateBackendSurface(&dirtyRect);
+ }
}
- updateBackendSurface();
+ Common::Rect dirtyRect(xoffs, yoffs, xoffs + width, yoffs + _boxStarHeight);
+ updateBackendSurface(&dirtyRect);
}
_videoLockOut &= ~0x20;
diff --git a/engines/agos/icons.cpp b/engines/agos/icons.cpp
index 3bbec55f3e..3aa25d4249 100644
--- a/engines/agos/icons.cpp
+++ b/engines/agos/icons.cpp
@@ -315,8 +315,9 @@ void AGOSEngine_Elvira1::drawIcon(WindowBlock *window, uint icon, uint x, uint y
Graphics::Surface *screen = getBackendSurface();
dst = (byte *)screen->getPixels();
- dst += (x + window->x) * 8;
- dst += (y * 8 + window->y) * screen->pitch;
+ x = (x + window->x) * 8;
+ y = (y * 8 + window->y);
+ dst += y * screen->pitch + x;
if (getFeatures() & GF_PLANAR) {
src = _iconFilePtr;
@@ -328,7 +329,8 @@ void AGOSEngine_Elvira1::drawIcon(WindowBlock *window, uint icon, uint x, uint y
decompressIconPlanar(dst, src, 24, 12, 16, screen->pitch, false);
}
- updateBackendSurface();
+ Common::Rect dirtyRect(x, y, x + 24, y + 12);
+ updateBackendSurface(&dirtyRect);
_videoLockOut &= ~0x8000;
}
@@ -964,7 +966,8 @@ void AGOSEngine::drawArrow(uint16 x, uint16 y, int8 dir) {
dst+= screen->pitch;
}
- updateBackendSurface();
+ Common::Rect dirtyRect(x * 8, y, x * 8 + 16, y + 19);
+ updateBackendSurface(&dirtyRect);
}
void AGOSEngine_Simon1::removeArrows(WindowBlock *window, uint num) {
diff --git a/engines/agos/menus.cpp b/engines/agos/menus.cpp
index 7de21be146..7617c11681 100644
--- a/engines/agos/menus.cpp
+++ b/engines/agos/menus.cpp
@@ -179,7 +179,8 @@ void AGOSEngine::unlightMenuStrip() {
for (i = 120; i != 130; i++)
disableBox(i);
- updateBackendSurface();
+ Common::Rect dirtyRect(272, 8, 320, 90);
+ updateBackendSurface(&dirtyRect);
mouseOn();
}
@@ -204,7 +205,8 @@ void AGOSEngine::lightMenuBox(uint hitarea) {
src += screen->pitch;
} while (--h);
- updateBackendSurface();
+ Common::Rect dirtyRect(ha->x, ha->y, ha->x + w, ha->y + ha->height);
+ updateBackendSurface(&dirtyRect);
mouseOn();
}
diff --git a/engines/agos/verb.cpp b/engines/agos/verb.cpp
index 7f1ff7592c..75c40d9899 100644
--- a/engines/agos/verb.cpp
+++ b/engines/agos/verb.cpp
@@ -1041,7 +1041,8 @@ void AGOSEngine::invertBox(HitArea *ha, byte a, byte b, byte c, byte d) {
src += screen->pitch;
} while (--h);
- updateBackendSurface();
+ Common::Rect dirtyRect(ha->x, ha->y, ha->x + w, ha->y + ha->height);
+ updateBackendSurface(&dirtyRect);
_videoLockOut &= ~0x8000;
}
diff --git a/engines/agos/window.cpp b/engines/agos/window.cpp
index 8be23ef249..70775e9942 100644
--- a/engines/agos/window.cpp
+++ b/engines/agos/window.cpp
@@ -178,13 +178,15 @@ void AGOSEngine::colorBlock(WindowBlock *window, uint16 x, uint16 y, uint16 w, u
uint8 color = window->fillColor;
if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW)
color += dst[0] & 0xF0;
+ uint16 h2 = h;
do {
memset(dst, color, w);
dst += screen->pitch;
} while (--h);
- updateBackendSurface();
+ Common::Rect dirtyRect(x, y, x + w, y + h2);
+ updateBackendSurface(&dirtyRect);
_videoLockOut &= ~0x8000;
}
@@ -242,6 +244,7 @@ void AGOSEngine::restoreBlock(uint16 x, uint16 y, uint16 w, uint16 h) {
src += y * _backGroundBuf->pitch;
uint8 paletteMod = 0;
+ uint16 h2 = 2;
if (getGameType() == GType_ELVIRA1 && !(getFeatures() & GF_DEMO) && y >= 133)
paletteMod = 16;
@@ -253,7 +256,8 @@ void AGOSEngine::restoreBlock(uint16 x, uint16 y, uint16 w, uint16 h) {
src += _backGroundBuf->pitch;
}
- updateBackendSurface();
+ Common::Rect dirtyRect(x, y, x + w, y + h2);
+ updateBackendSurface(&dirtyRect);
}
void AGOSEngine::setTextColor(uint color) {
Commit: 512ae9cfe3c7ffd11dfa8619da6d93006f400e8a
https://github.com/scummvm/scummvm/commit/512ae9cfe3c7ffd11dfa8619da6d93006f400e8a
Author: athrxx (athrxx at scummvm.org)
Date: 2021-01-22T00:47:58+01:00
Commit Message:
AGOS: (ELVIRA/PC98/Jp) - add static msg strings + some fixes
(this concerns mostly the save/load/pause texts)
Changed paths:
engines/agos/charset-fontdata.cpp
engines/agos/charset.cpp
engines/agos/saveload.cpp
engines/agos/script_e1.cpp
engines/agos/window.cpp
diff --git a/engines/agos/charset-fontdata.cpp b/engines/agos/charset-fontdata.cpp
index 664b6bb964..74988fd1fd 100644
--- a/engines/agos/charset-fontdata.cpp
+++ b/engines/agos/charset-fontdata.cpp
@@ -3020,7 +3020,7 @@ void AGOSEngine::windowDrawChar(WindowBlock *window, uint x, uint y, byte chr) {
dst += dstPitch;
} while (--h);
- Common::Rect dirtyRect(x, y, x + 6, y + 8);
+ Common::Rect dirtyRect(x + window->textColumnOffset, y, x + window->textColumnOffset + 6, y + 8);
updateBackendSurface(&dirtyRect);
_videoLockOut &= ~0x8000;
diff --git a/engines/agos/charset.cpp b/engines/agos/charset.cpp
index c3e8e49d63..94efc8dd75 100644
--- a/engines/agos/charset.cpp
+++ b/engines/agos/charset.cpp
@@ -148,7 +148,7 @@ void AGOSEngine::justifyOutPut(byte chr) {
_printCharPixelCount = 0;
doOutput(&chr, 1);
clsCheck(_textWindow);
- } else if (getLanguage() == Common::JA_JPN) {
+ } else if (getLanguage() == Common::JA_JPN && !_forceAscii) {
// Japanese has no word wrapping
_lettersToPrintBuf[0] = chr;
_lettersToPrintBuf[1] = '\0';
@@ -262,7 +262,7 @@ void AGOSEngine::windowPutChar(WindowBlock *window, byte c, byte b) {
return;
}
- if (_language == Common::JA_JPN)
+ if (_language == Common::JA_JPN && !_forceAscii)
textColumnWidth = width = 4;
else if (c - 32 > 98) // Ignore invalid characters
return;
diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp
index 8f3320b48b..9faa473cfe 100644
--- a/engines/agos/saveload.cpp
+++ b/engines/agos/saveload.cpp
@@ -238,6 +238,11 @@ bool AGOSEngine::confirmOverWrite(WindowBlock *window) {
message2 = " Ueberschreiben ?\r\r";
message3 = " Ja Nein";
break;
+ case Common::JA_JPN:
+ message1 = "\r ""\x82\xbb\x82\xcc\x83""t""\x83""@""\x83""C""\x83\x8b\x82\xcd\x82\xb7\x82\xc5\x82\xc9\x91\xb6\x8d\xdd\x82\xb5\x82\xdc\x82\xb7""\r\r";
+ message2 = " ""\x8f\xe3\x8f\x91\x82\xab\x82\xb5\x82\xc4\x82\xe6\x82\xeb\x82\xb5\x82\xa2\x82\xc5\x82\xb7\x82\xa9\x81""H\r\r";
+ message3 = " ""\x82\xcd\x82\xa2"" ""\x82\xa2\x82\xa2\x82\xa6";
+ break;
default:
message1 = "\r File already exists.\r\r";
message2 = " Overwrite it ?\r\r";
@@ -310,17 +315,22 @@ restart:
case Common::DE_DEU:
message1 = "\rLege Spielstandsdiskette ein. Dateinamen eingeben:\r\r ";
break;
+ case Common::JA_JPN:
+ message1 = "\r ""\x83""t""\x83""@""\x83""C""\x83\x8b\x96\xbc\x82\xf0\x93\xfc\x97\xcd\x82\xb5\x82\xc4\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81""F\r\r\r ";
+ break;
default:
message1 = "\r Insert savegame data disk & enter filename:\r\r ";
break;
}
+ clearHiResTextLayer();
for (; *message1; message1++)
windowPutChar(window, *message1);
memset(_saveBuf, 0, 10);
name = _saveBuf;
_saveGameNameLen = 0;
+ _forceAscii = true;
while (!shouldQuit()) {
windowPutChar(window, 128);
@@ -350,6 +360,8 @@ restart:
}
}
+ _forceAscii = false;
+
if (_saveGameNameLen != 0) {
int16 slot = matchSaveGame(name, numSaveGames);
if (!load) {
@@ -373,6 +385,7 @@ restart:
printStats();
}
+ clearHiResTextLayer();
restartAnimation();
_gameStoppedClock = getTime() - saveTime + _gameStoppedClock;
}
@@ -942,6 +955,10 @@ void AGOSEngine::fileError(WindowBlock *window, bool saveError) {
message1 = "\r Sicherung erfolglos.";
message2 = "\rVersuche eine andere Diskette.";
break;
+ case Common::JA_JPN:
+ message1 = "\r ""\x83""Z""\x81""[""\x83""u""\x82\xc9\x8e\xb8\x94""s""\x82\xb5\x82\xdc\x82\xb5\x82\xbd";
+ message2 = "\r ""\x95\xca\x82\xcc\x83""f""\x83""B""\x83""X""\x83""N""\x82\xf0\x8e""g""\x97""p""\x82\xb5\x82\xc4\x82\xad\x82\xbe\x82\xb3\x82\xa2";
+ break;
default:
message1 = "\r Save failed.";
message2 = "\r Disk error.";
@@ -978,6 +995,10 @@ void AGOSEngine::fileError(WindowBlock *window, bool saveError) {
message1 = "\r Laden erfolglos.";
message2 = "\r Datei nicht gefunden.";
break;
+ case Common::JA_JPN:
+ message1 = "\r ""\x83\x8d\x81""[""\x83""h""\x82\xc9\x8e\xb8\x94""s""\x82\xb5\x82\xdc\x82\xb5\x82\xbd";
+ message2 = "\r ""\x83""t""\x83""@""\x83""C""\x83\x8b\x82\xaa\x8c\xa9\x82\xc2\x82\xa9\x82\xe8\x82\xdc\x82\xb9\x82\xf1";
+ break;
default:
message1 = "\r Load failed.";
message2 = "\r File not found.";
diff --git a/engines/agos/script_e1.cpp b/engines/agos/script_e1.cpp
index 85f92bcf7a..d86bfb1d3d 100644
--- a/engines/agos/script_e1.cpp
+++ b/engines/agos/script_e1.cpp
@@ -927,6 +927,10 @@ restart:
message1 = " Juego en Pausa\r\r\r";
message2 = "Continuar Salir";
break;
+ case Common::JA_JPN:
+ message1 = " ""\x83""Q""\x81""[""\x83\x80\x92\x86\x92""f\r\r\r";
+ message2 = " ""\x91\xb1\x82\xaf\x82\xe9"" ""\x82\xe2\x82\xdf\x82\xe9";
+ break;
default:
message1 = " Game Paused\r\r\r";
message2 = " Continue Quit";
@@ -958,6 +962,10 @@ restart:
message1 = " Estas seguro ?\r\r\r";
message2 = " Si No";
break;
+ case Common::JA_JPN:
+ message1 = " ""\x82\xe6\x82\xeb\x82\xb5\x82\xa2\x82\xc5\x82\xb7\x82\xa9\x81""H\r\r\r";
+ message2 = " ""\x82\xcd\x82\xa2"" ""\x82\xa2\x82\xa2\x82\xa6";
+ break;
default:
message1 = " Are you sure ?\r\r\r";
message2 = " Yes No";
@@ -978,6 +986,7 @@ restart:
}
}
+ clearHiResTextLayer();
restartAnimation();
_gameStoppedClock = getTime() - pauseTime + _gameStoppedClock;
}
@@ -1154,6 +1163,7 @@ void AGOSEngine::printScroll() {
_windowNum = 3;
_curVgaFile2 = vpe->vgaFile2;
+ clearHiResTextLayer();
drawImage_init(9, 0, 10, 32, 0);
_curVgaFile2 = curVgaFile2Orig;
@@ -1166,7 +1176,6 @@ void AGOSEngine::printStats() {
window->flags = 1;
mouseOff();
- _forceAscii = true;
// Strength
val = _variableArray[0];
@@ -1216,7 +1225,6 @@ void AGOSEngine::printStats() {
val = 99;
writeChar(window, 36, 133, 4, val);
- _forceAscii = false;
mouseOn();
}
diff --git a/engines/agos/window.cpp b/engines/agos/window.cpp
index 70775e9942..73d1523f20 100644
--- a/engines/agos/window.cpp
+++ b/engines/agos/window.cpp
@@ -293,9 +293,11 @@ void AGOSEngine::waitWindow(WindowBlock *window) {
window->textRow = window->height - 1;
window->textLength = 0;
+ _forceAscii = true;
message = "[ OK ]";
for (; *message; message++)
windowPutChar(window, *message);
+ _forceAscii = false;
ha = findEmptyHitArea();
ha->x = (window->width / 2 + window->x - 3) * 8;
@@ -328,6 +330,7 @@ void AGOSEngine::waitWindow(WindowBlock *window) {
void AGOSEngine::writeChar(WindowBlock *window, int x, int y, int offs, int val) {
int chr;
+ _forceAscii = true;
// Clear background of first digit
window->textColumnOffset = offs;
@@ -358,6 +361,8 @@ void AGOSEngine::writeChar(WindowBlock *window, int x, int y, int offs, int val)
window->textColor = 15;
windowDrawChar(window, x * 8, y, chr);
}
+
+ _forceAscii = false;
}
} // End of namespace AGOS
Commit: 56d897a100aeded316e16dc93aee66548806c8cd
https://github.com/scummvm/scummvm/commit/56d897a100aeded316e16dc93aee66548806c8cd
Author: athrxx (athrxx at scummvm.org)
Date: 2021-01-22T00:47:59+01:00
Commit Message:
AGOS: (ELVIRA/PC98/Jp) - more fixes
Just walking around the castle fixing bugs as I find them, e. g.:
- make decompression routine safer (this could write a couple of invalid byte in at least one case)
- also support compressed data header without actual data (required in the kitchen scene when killing the evil cook)
- fix some graphics/palette related glitches
Changed paths:
engines/agos/agos.cpp
engines/agos/agos.h
engines/agos/charset-fontdata.cpp
engines/agos/cursor.cpp
engines/agos/draw.cpp
engines/agos/gfx.cpp
engines/agos/icons.cpp
engines/agos/res.cpp
engines/agos/saveload.cpp
engines/agos/window.cpp
diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp
index eb8a5f6cd0..794db26fb3 100644
--- a/engines/agos/agos.cpp
+++ b/engines/agos/agos.cpp
@@ -515,6 +515,7 @@ AGOSEngine::AGOSEngine(OSystem *system, const AGOSGameDescription *gd)
_planarBuf = 0;
_pak98Buf = 0;
+ _paletteModNext = 16;
_midiEnabled = false;
diff --git a/engines/agos/agos.h b/engines/agos/agos.h
index 553131127e..e70b65762e 100644
--- a/engines/agos/agos.h
+++ b/engines/agos/agos.h
@@ -555,6 +555,7 @@ protected:
byte _videoBuf1[32000];
uint16 _videoWindows[128];
const byte *_pak98Buf;
+ byte _paletteModNext;
uint8 _window3Flag;
uint8 _window4Flag;
diff --git a/engines/agos/charset-fontdata.cpp b/engines/agos/charset-fontdata.cpp
index 74988fd1fd..5b30a76d4f 100644
--- a/engines/agos/charset-fontdata.cpp
+++ b/engines/agos/charset-fontdata.cpp
@@ -3037,7 +3037,7 @@ void AGOSEngine_Elvira1::windowDrawChar(WindowBlock *window, uint x, uint y, byt
} else {
_sjisCurChar = chr;
if ((chr >= 0x80 && chr < 0xA0) || chr >= 0xE0)
- return;
+ return;
}
_videoLockOut |= 0x8000;
diff --git a/engines/agos/cursor.cpp b/engines/agos/cursor.cpp
index 6a16b764b4..5e05f14075 100644
--- a/engines/agos/cursor.cpp
+++ b/engines/agos/cursor.cpp
@@ -879,8 +879,8 @@ void AGOSEngine::drawMousePointer() {
uint8 ptch = 16;
uint16 *dst1 = &((uint16*)_mouseData)[16 * 16 * 2 - 1];
uint16 *dst2 = dst1 - ptch;
- for (const byte *src = &_mouseData[16 * 16 - 1]; src >= _mouseData; --src) {
- *dst1-- = *dst2-- = (*src << 8) | *src;
+ for (const byte *pos = &_mouseData[16 * 16 - 1]; pos >= _mouseData; --pos) {
+ *dst1-- = *dst2-- = (*pos << 8) | *pos;
if (!(ptch = (ptch - 1) % 16)) {
dst1 -= 16;
dst2 -= 16;
diff --git a/engines/agos/draw.cpp b/engines/agos/draw.cpp
index a627bb54d8..53d5b9c0af 100644
--- a/engines/agos/draw.cpp
+++ b/engines/agos/draw.cpp
@@ -501,7 +501,7 @@ void AGOSEngine::saveBackGround(VgaSprite *vsp) {
animTable->x = x;
animTable->y = y;
- animTable->width = READ_BE_UINT16(ptr + 6) / 16;
+ animTable->width = (getPlatform() == Common::kPlatformPC98 ? READ_LE_UINT16(ptr + 6) : READ_BE_UINT16(ptr + 6)) / 16;
if (vsp->flags & 0x40) {
animTable->width++;
}
diff --git a/engines/agos/gfx.cpp b/engines/agos/gfx.cpp
index a7e8b0c15e..5cdbe718f6 100644
--- a/engines/agos/gfx.cpp
+++ b/engines/agos/gfx.cpp
@@ -944,8 +944,9 @@ void AGOSEngine::drawImage(VC10_state *state) {
state->surf_addr += xoffs + yoffs * state->surf_pitch;
- if (getGameType() == GType_ELVIRA1 && getPlatform() != Common::kPlatformPC98 && (state->flags & kDFNonTrans) && yoffs > 133)
- state->paletteMod = 16;
+ if (getGameType() == GType_ELVIRA1 && (state->flags & kDFNonTrans) && yoffs > 133)
+ state->paletteMod = _paletteModNext;
+ _paletteModNext = 16;
if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW)
state->palette = state->surf_addr[0] & 0xF0;
@@ -959,7 +960,6 @@ void AGOSEngine::drawImage(VC10_state *state) {
drawVertImage(state);
}
-
Common::Rect dirtyRect(xoffs, yoffs, xmax, ymax);
updateBackendSurface(&dirtyRect);
}
diff --git a/engines/agos/icons.cpp b/engines/agos/icons.cpp
index 3aa25d4249..7664a4182b 100644
--- a/engines/agos/icons.cpp
+++ b/engines/agos/icons.cpp
@@ -329,7 +329,7 @@ void AGOSEngine_Elvira1::drawIcon(WindowBlock *window, uint icon, uint x, uint y
decompressIconPlanar(dst, src, 24, 12, 16, screen->pitch, false);
}
- Common::Rect dirtyRect(x, y, x + 24, y + 12);
+ Common::Rect dirtyRect(x, y, x + 24, y + 24);
updateBackendSurface(&dirtyRect);
_videoLockOut &= ~0x8000;
diff --git a/engines/agos/res.cpp b/engines/agos/res.cpp
index 4b2373e2e4..048cb963e3 100644
--- a/engines/agos/res.cpp
+++ b/engines/agos/res.cpp
@@ -806,51 +806,61 @@ void AGOSEngine::loadVGABeardFile(uint16 id) {
}
}
-void decodePak98(uint8 *dst, const uint8 *src, uint32 inSize) {
- const uint8 *src2 = 0;
- uint8 cmd = 0x80;
-
- for (uint32 bytesLeft = inSize; bytesLeft; ) {
- if (cmd == 0x80) {
- src2 = src + 1;
- --bytesLeft;
- }
+uint8 safeReadByte(const uint8 *&src, const uint8 *end) {
+ if (src < end)
+ return (*src++);
+ error("decodePak98(): invalid data");
+ return 0;
+}
+
+uint16 safeReadWord(const uint8 *&src, const uint8 *end) {
+ uint8 lo = safeReadByte(src, end);
+ return (safeReadByte(src, end) << 8) | lo;
+}
+
+#define S_NEXTBYTE safeReadByte(src, endSrc)
+#define S_NEXTWORD safeReadWord(src, endSrc)
- if (cmd & *src) {
- *dst++ = *src2++;
- --bytesLeft;
+void decodePak98(uint8 *dst, uint32 outSize, const uint8 *src, uint32 inSize) {
+ const uint8 *end = dst + outSize;
+ const uint8 *endSrc = src + inSize;
+ uint8 state = 0x80;
+ uint8 flg = S_NEXTBYTE;
+
+ for (uint32 srcBytesLeft = inSize - 1; srcBytesLeft; ) {
+ if (state & flg) {
+ if (dst < end)
+ *dst++ = S_NEXTBYTE;
+ --srcBytesLeft;
} else {
- bytesLeft -= 2;
- uint16 cmd2 = READ_LE_UINT16(src2);
- src2 += 2;
+ srcBytesLeft -= 2;
+ uint16 cmd2 = S_NEXTWORD;
uint8 cmd3 = cmd2 & 0x0F;
cmd2 >>= 4;
if (cmd2 == 0) {
uint16 count = cmd3 + 4;
- --bytesLeft;
+ --srcBytesLeft;
if (cmd3 == 0x0F) {
- count = READ_LE_UINT16(src2);
- src2 += 2;
- bytesLeft -= 2;
+ count = S_NEXTWORD;
+ srcBytesLeft -= 2;
} else if (cmd3 == 0x0E) {
- count = 18 + (*src2++);
- --bytesLeft;
+ count = 18 + (S_NEXTBYTE);
+ --srcBytesLeft;
}
- uint8 destVal = *src2++;
- while (count--)
+ uint8 destVal = S_NEXTBYTE;
+ while (count-- && dst < end)
*dst++ = destVal;
} else if (cmd2 == 1) {
uint16 count = cmd3 + 3;
if (cmd3 == 0x0F) {
- count = READ_LE_UINT16(src2);
- src2 += 2;
- bytesLeft -= 2;
+ count = S_NEXTWORD;
+ srcBytesLeft -= 2;
} else if (cmd3 == 0x0E) {
- count = 17 + (*src2++);
- --bytesLeft;
+ count = 17 + (S_NEXTBYTE);
+ --srcBytesLeft;
}
dst += count;
@@ -858,39 +868,43 @@ void decodePak98(uint8 *dst, const uint8 *src, uint32 inSize) {
} else if (cmd2 == 2) {
uint16 count = cmd3 + 16;
if (cmd3 == 0x0F) {
- count = READ_LE_UINT16(src2);
- src2 += 2;
- bytesLeft -= 2;
+ count = S_NEXTWORD;
+ srcBytesLeft -= 2;
} else if (cmd3 == 0x0E) {
- count = 30 + (*src2++);
- --bytesLeft;
+ count = 30 + (S_NEXTBYTE);
+ --srcBytesLeft;
}
- bytesLeft -= count;
- while (count--)
- *dst++ = *src2++;
+ srcBytesLeft -= count;
+ while (count-- && dst < end)
+ *dst++ = S_NEXTBYTE;
} else {
uint16 count = cmd3 + 3;
if (cmd3 == 0x0F) {
- count = 18 + (*src2++);
- --bytesLeft;
+ count = 18 + (S_NEXTBYTE);
+ --srcBytesLeft;
}
- uint8 *src3 = dst - cmd2;
- while (count--)
- *dst++ = *src3++;
+ const uint8 *src2 = dst - cmd2;
+ while (count-- && dst < end)
+ *dst++ = *src2++;
}
}
- cmd >>= 1;
- if (cmd == 0) {
- cmd = 0x80;
- src = src2;
+ if (!(state >>= 1)) {
+ state = 0x80;
+ if (srcBytesLeft) {
+ flg = S_NEXTBYTE;
+ srcBytesLeft--;
+ }
}
}
}
+#undef S_NEXTBYTE
+#undef S_NEXTWORD
+
void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type, bool useError) {
Common::File in;
char filename[15];
@@ -1003,19 +1017,19 @@ void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type, bool useError) {
dstSize = srcSize;
srcSize = in.readUint32LE();
uint16 fill = in.readUint16LE();
-
- uint8 *srcBuffer = new uint8[srcSize];
- if (in.read(srcBuffer, srcSize) != srcSize)
- error("loadVGAVideoFile: Read failed");
dst = allocBlock(dstSize);
- Common::fill<uint16*, uint16>((uint16*)dst, (uint16*)(dst + (dstSize & ~1)), fill);
+ Common::fill<uint16*, uint16>((uint16*)dst, (uint16*)(dst + (dstSize & ~1)), TO_LE_16(fill));
if (dstSize & 1)
*(dst + dstSize - 1) = fill & 0xff;
- decodePak98(dst, srcBuffer, srcSize);
-
- delete[] srcBuffer;
+ if (srcSize) {
+ uint8 *srcBuffer = new uint8[srcSize];
+ if (in.read(srcBuffer, srcSize) != srcSize)
+ error("loadVGAVideoFile: Read failed");
+ decodePak98(dst, dstSize, srcBuffer, srcSize);
+ delete[] srcBuffer;
+ }
} else {
dstSize = srcSize;
dst = allocBlock(dstSize + extraBuffer);
@@ -1055,15 +1069,17 @@ Common::SeekableReadStream *AGOSEngine::createPak98FileStream(const char *filena
uint32 inSize = in.readUint32LE();
uint16 fill = in.readUint16LE();
- uint8 *tempBuffer = new uint8[inSize];
- uint8 *decBuffer = new uint8[outSize];
- Common::fill<uint16*, uint16>((uint16*)decBuffer, (uint16*)(decBuffer + (outSize & ~1)), fill);
+ uint8 *decBuffer = (uint8*)malloc(outSize);
+ Common::fill<uint16*, uint16>((uint16*)decBuffer, (uint16*)(decBuffer + (outSize & ~1)), TO_LE_16(fill));
if (outSize & 1)
*(decBuffer + outSize - 1) = fill & 0xff;
- in.read(tempBuffer, inSize);
- decodePak98(decBuffer, tempBuffer, inSize);
- delete[] tempBuffer;
+ if (inSize) {
+ uint8 *tempBuffer = new uint8[inSize];
+ in.read(tempBuffer, inSize);
+ decodePak98(decBuffer, outSize, tempBuffer, inSize);
+ delete[] tempBuffer;
+ }
return new Common::MemoryReadStream(decBuffer, outSize, DisposeAfterUse::YES);
}
@@ -1072,25 +1088,27 @@ void AGOSEngine::convertPC98Image(VC10_state &state) {
if (state.flags & (kDFCompressedFlip | kDFCompressed)) {
const byte *src = state.srcPtr;
uint32 outSize = READ_LE_UINT32(src + 2);
- assert(outSize == (state.width << 3) * state.height);
+ assert(outSize >= (uint32)((state.width << 3) * state.height));
uint32 inSize = READ_LE_UINT32(src + 6);
uint16 fill = READ_LE_UINT16(src + 10);
delete[] _pak98Buf;
byte *decBuffer = new uint8[outSize];
- Common::fill<uint16*, uint16>((uint16*)decBuffer, (uint16*)(decBuffer + (outSize & ~1)), fill);
+ Common::fill<uint16*, uint16>((uint16*)decBuffer, (uint16*)(decBuffer + (outSize & ~1)), TO_LE_16(fill));
if (outSize & 1)
*(decBuffer + outSize - 1) = fill & 0xff;
-
- decodePak98(decBuffer, src + 12, inSize);
+ if (inSize)
+ decodePak98(decBuffer, outSize, src + 12, inSize);
_pak98Buf = state.srcPtr = decBuffer;
+ _paletteModNext = 0;
}
// The PC-98 images are in a planar format, but slightly different from the Amiga format. It does
- // not make much sense to set the GF_PLANAR flag, since the icons do not require the conversion.
- delete[] _planarBuf;
+ // not make much sense to set the GF_PLANAR flag, since the Amiga code can't be used anyway.
+ free(_planarBuf);
uint16 planeLW = state.width << 1;
uint16 planePitch = planeLW * 3;
- _planarBuf = new byte[(state.width << 3) * state.height];
+
+ _planarBuf = (byte*)malloc((state.width << 3) * state.height);
const byte *src[4];
memset(src, 0, sizeof(src));
diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp
index 9faa473cfe..356babdea3 100644
--- a/engines/agos/saveload.cpp
+++ b/engines/agos/saveload.cpp
@@ -239,7 +239,7 @@ bool AGOSEngine::confirmOverWrite(WindowBlock *window) {
message3 = " Ja Nein";
break;
case Common::JA_JPN:
- message1 = "\r ""\x82\xbb\x82\xcc\x83""t""\x83""@""\x83""C""\x83\x8b\x82\xcd\x82\xb7\x82\xc5\x82\xc9\x91\xb6\x8d\xdd\x82\xb5\x82\xdc\x82\xb7""\r\r";
+ message1 = "\r ""\x82\xbb\x82\xcc\x83""t""\x83""@""\x83""C""\x83\x8b\x82\xcd\x82\xb7\x82\xc5\x82\xc9\x91\xb6\x8d\xdd\x82\xb5\x82\xdc\x82\xb7""\r\r";
message2 = " ""\x8f\xe3\x8f\x91\x82\xab\x82\xb5\x82\xc4\x82\xe6\x82\xeb\x82\xb5\x82\xa2\x82\xc5\x82\xb7\x82\xa9\x81""H\r\r";
message3 = " ""\x82\xcd\x82\xa2"" ""\x82\xa2\x82\xa2\x82\xa6";
break;
diff --git a/engines/agos/window.cpp b/engines/agos/window.cpp
index 73d1523f20..91a5ef99f8 100644
--- a/engines/agos/window.cpp
+++ b/engines/agos/window.cpp
@@ -244,7 +244,7 @@ void AGOSEngine::restoreBlock(uint16 x, uint16 y, uint16 w, uint16 h) {
src += y * _backGroundBuf->pitch;
uint8 paletteMod = 0;
- uint16 h2 = 2;
+ Common::Rect dirtyRect(x, y, x + w, h);
if (getGameType() == GType_ELVIRA1 && !(getFeatures() & GF_DEMO) && y >= 133)
paletteMod = 16;
@@ -256,7 +256,6 @@ void AGOSEngine::restoreBlock(uint16 x, uint16 y, uint16 w, uint16 h) {
src += _backGroundBuf->pitch;
}
- Common::Rect dirtyRect(x, y, x + w, y + h2);
updateBackendSurface(&dirtyRect);
}
Commit: a3fd19265fdc745400d47d5f956fb1a26b7f88ea
https://github.com/scummvm/scummvm/commit/a3fd19265fdc745400d47d5f956fb1a26b7f88ea
Author: athrxx (athrxx at scummvm.org)
Date: 2021-01-22T00:47:59+01:00
Commit Message:
AGOS: (ELVIRA/PC98/Jp) - add sound driver
Added support for PC9801-26, Roland MT-32 and General Midi. I have left out the PC speaker part for now.
Changed paths:
A engines/agos/drivers/accolade/pc98.cpp
engines/agos/drivers/accolade/mididriver.h
engines/agos/midi.cpp
engines/agos/midi.h
engines/agos/module.mk
diff --git a/engines/agos/drivers/accolade/mididriver.h b/engines/agos/drivers/accolade/mididriver.h
index 253fb6b736..8f02b0034c 100644
--- a/engines/agos/drivers/accolade/mididriver.h
+++ b/engines/agos/drivers/accolade/mididriver.h
@@ -38,6 +38,7 @@ extern void MidiDriver_Accolade_readDriver(Common::String filename, MusicType re
extern MidiDriver *MidiDriver_Accolade_AdLib_create(Common::String driverFilename);
extern MidiDriver *MidiDriver_Accolade_MT32_create(Common::String driverFilename);
+extern MidiDriver *MidiDriverPC98_create(MidiDriver::DeviceHandle dev);
} // End of namespace AGOS
diff --git a/engines/agos/drivers/accolade/pc98.cpp b/engines/agos/drivers/accolade/pc98.cpp
new file mode 100644
index 0000000000..d2f3a15d95
--- /dev/null
+++ b/engines/agos/drivers/accolade/pc98.cpp
@@ -0,0 +1,718 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "audio/softsynth/fmtowns_pc98/pc98_audio.h"
+#include "audio/mididrv.h"
+#include "audio/mixer.h"
+#include "engines/engine.h"
+#include "common/func.h"
+
+namespace AGOS {
+
+class PC98CommonDriver : public MidiDriver {
+public:
+ enum PC98DriverProperties {
+ kPropMusicVolume = 0x10,
+ kPropSfxVolume = 0x20,
+ kPropPause = 0x30
+ };
+public:
+ PC98CommonDriver();
+ virtual ~PC98CommonDriver() override {};
+
+ bool isOpen() const override { return _isOpen; }
+ void send(uint32 b) override;
+ void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override;
+ uint32 property(int prop, uint32 param) override;
+ uint32 getBaseTempo() override { return _baseTempo; }
+ MidiChannel *allocateChannel() override { return 0; }
+ MidiChannel *getPercussionChannel() override { return 0; }
+
+protected:
+ void updateSounds();
+ void updateParser();
+ void reset();
+
+ uint32 _baseTempo;
+ bool _isOpen;
+
+ Audio::Mixer *_mixer;
+
+ const uint8 *_instrumentsRemap;
+ const int8 *_instrumentLevelAdjust;
+ const uint8 *_partsRemap;
+
+ uint8 _chanUse[16];
+ uint8 _ngDelay;
+ bool _allNotes;
+ bool _programLock;
+ bool _noFadeRemap;
+ bool _delayedProgramChange;
+
+private:
+ virtual void noteOn(uint8 part, uint8 note, uint8 velo) = 0;
+ virtual void noteOff(uint8 part, uint8 note) = 0;
+ virtual void programChange(uint8 part, uint8 prog) = 0;
+ virtual void processSounds() = 0;
+ virtual void setVolume(int musicVolume, int sfxVolume) = 0;
+ virtual void pause(bool paused) = 0;
+
+ class TimerCb {
+ public:
+ typedef void(*FuncType)(void*);
+ TimerCb(const FuncType func, void *arg) : _func(func), _arg(arg) {}
+ bool isValid() const { return _func && _arg; }
+ void operator()() const { (*_func)(_arg); }
+ private:
+ const FuncType _func;
+ void *_arg;
+ } *_timerCb;
+
+ uint32 _internalUpdateTimer;
+
+ uint16 _musicVolume;
+ uint16 _sfxVolume;
+ int8 _fadeVolumeAdjust;
+ uint8 _partPrograms[16];
+};
+
+class PC98FMDriver : public PC98CommonDriver, private PC98AudioPluginDriver {
+public:
+ PC98FMDriver();
+ ~PC98FMDriver() override;
+
+ int open() override;
+ void close() override;
+
+private:
+ void noteOn(uint8 part, uint8 note, uint8 velo) override;
+ void noteOff(uint8 part, uint8 note) override;
+ void programChange(uint8 part, uint8 prog) override;
+ void processSounds() override;
+ void setVolume(int musicVolume, int sfxVolume) override;
+ void pause(bool paused) override {}
+
+ void loadInstrument(uint8 chan, uint8 prg);
+ void startNote(uint8 chan, uint8 note, uint8 velo);
+ void stopNote(uint8 chan, uint8 note);
+
+ void timerCallbackA() override {}
+ void timerCallbackB() override;
+
+ PC98AudioCore *_pc98a;
+
+ uint8 _chanAssign[3];
+ uint8 _chanNotes[3];
+ uint8 _partProgramsInternal[16];
+ uint8 _partNotes[16];
+
+ static const uint8 _instrumentsRemapFM[128];
+ static const uint8 _instrumentLevelAdjustFM[128];
+ static const uint8 _partsRemapFM[16];
+ static const uint8 _instrumentPatches[16][25];
+ static const uint8 _ngMapping[76];
+ static const uint8 _carrier[8];
+ static const uint16 _frequency[12];
+};
+
+class PC98MidiDriver : public PC98CommonDriver {
+public:
+ PC98MidiDriver(DeviceHandle dev);
+ ~PC98MidiDriver() override;
+
+ int open() override;
+ void close() override;
+
+ static void timerCallback(void *obj);
+
+private:
+ void noteOn(uint8 part, uint8 note, uint8 velo) override;
+ void noteOff(uint8 part, uint8 note) override;
+ void programChange(uint8 part, uint8 prog) override;
+ void processSounds() override {}
+ void setVolume(int musicVolume, int sfxVolume) override;
+ void pause(bool paused) override;
+ void sendSysexWithCheckSum(uint8 *data);
+
+ MidiDriver *_drv;
+ DeviceHandle _dev;
+
+ uint8 _volSysex[9];
+ uint8 _partAssignSysexGS[9];
+ uint8 _partAssignSysexMT32[9];
+
+ static const uint8 _instrumentsRemapMT32[128];
+ static const uint8 _instrumentsRemapGM[128];
+ static const uint8 _partsRemapMidi[16];
+ static const uint8 _sysexMsg[3][9];
+};
+
+PC98CommonDriver::PC98CommonDriver() : _mixer(g_engine->_mixer), _baseTempo(0), _fadeVolumeAdjust(0), _allNotes(false), _programLock(false), _isOpen(false), _noFadeRemap(false), _delayedProgramChange(false), _ngDelay(0), _timerCb(0), _musicVolume(0xff), _sfxVolume(0xff), _internalUpdateTimer(0) {
+ memset(_partPrograms, 0, sizeof(_partPrograms));
+ memset(_chanUse, 0, sizeof(_chanUse));
+}
+
+void PC98CommonDriver::send(uint32 b) {
+ if (!_isOpen)
+ return;
+
+ byte para2 = (b >> 16) & 0xFF;
+ byte para1 = (b >> 8) & 0xFF;
+ byte ch = b & 0x0F;
+
+ switch (b & 0xF0) {
+ case 0x80:
+ noteOff(ch, para1);
+ break;
+ case 0x90:
+ if (para2) {
+ int16 velo = para2;
+ if (ch != 9)
+ velo = CLIP<int16>(velo + _instrumentLevelAdjust[_partPrograms[ch]], 0, 127);
+ velo = CLIP<int16>(velo + _fadeVolumeAdjust, 0, 127);
+ noteOn(ch, para1, velo);
+ } else {
+ noteOff(ch, para1);
+ }
+ break;
+ case 0xC0:
+ _partPrograms[ch] = para1;
+ programChange(ch, ch == 9 ? 0 : _instrumentsRemap[para1 & 0x07F]);
+ break;
+ default:
+ // 0xA0 and 0xB0 are parsing related and will be filtered and handled in MidiParser_S1D.
+ if (!((b & 0xF0) == 0xB0 && (para1 == 0x7b || para1 == 0x07)))
+ warning("PC98CommonDriver::send(): Unsupported Midi Message: 0x%02x 0x%02x 0x%02x", b & 0xFF, para1, para2);
+ break;
+ }
+}
+
+void PC98CommonDriver::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+ delete _timerCb;
+ _timerCb = (_isOpen && timerParam && timerProc) ? new TimerCb(timerProc, timerParam) : 0;
+}
+
+uint32 PC98CommonDriver::property(int prop, uint32 param) {
+ uint32 res = 0;
+ switch (prop) {
+ case kPropMusicVolume:
+ case kPropSfxVolume: {
+ uint16 &v = (prop == kPropMusicVolume) ? _musicVolume : _sfxVolume;
+ res = v;
+ if ((int32)param != -1)
+ v = param & 0x1ff;
+ setVolume(_musicVolume, _sfxVolume);
+ break;
+ }
+ case kPropPause:
+ pause(param);
+ break;
+ default:
+ break;
+ }
+ return res;
+}
+
+void PC98CommonDriver::updateSounds() {
+ if (!_isOpen)
+ return;
+
+ _internalUpdateTimer += _baseTempo;
+ if (_internalUpdateTimer >= 16667) {
+ _internalUpdateTimer -= 16667;
+
+ // I haven't implemented music fading in and out, since Elvira 1 (the
+ // only game for this sound driver) does not use the feature at all.
+ // The fade volume would have to be updated here...
+
+ for (int i = 0; i < 16; ++i)
+ _chanUse[i] = 0;
+
+ processSounds();
+ }
+}
+
+void PC98CommonDriver::updateParser() {
+ if (_isOpen && _timerCb && _timerCb->isValid())
+ (*_timerCb)();
+}
+
+void PC98CommonDriver::reset() {
+ memset(_partPrograms, 0, sizeof(_partPrograms));
+ memset(_chanUse, 0, sizeof(_chanUse));
+ _allNotes = false;
+ _programLock = false;
+ _noFadeRemap = false;
+ _delayedProgramChange = false;
+ _ngDelay = 0;
+}
+
+PC98FMDriver::PC98FMDriver() : PC98CommonDriver(), _pc98a(0) {
+ _baseTempo = 10080;
+ _instrumentsRemap = _instrumentsRemapFM;
+ _instrumentLevelAdjust = (const int8*)_instrumentLevelAdjustFM;
+ _partsRemap = _partsRemapFM;
+ memset(_partProgramsInternal, 0, sizeof(_partProgramsInternal));
+ memset(_partNotes, 0, sizeof(_partNotes));
+ memset(_chanAssign, 0, sizeof(_chanAssign));
+ memset(_chanNotes, 0, sizeof(_chanNotes));
+}
+
+PC98FMDriver::~PC98FMDriver() {
+ _mixer->stopAll();
+ close();
+}
+
+int PC98FMDriver::open() {
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ delete _pc98a;
+
+ _pc98a = new PC98AudioCore(g_engine->_mixer, this, kType26);
+ if (_pc98a && _pc98a->init()) {
+ _pc98a->writeReg(0, 0x06, 0x0a);
+ _pc98a->writeReg(0, 0x07, 0x9c);
+ for (int i = 8; i < 11; ++i)
+ _pc98a->writeReg(0, i, 0);
+ _pc98a->writeReg(0, 0x27, 0x3a);
+ } else {
+ return MERR_DEVICE_NOT_AVAILABLE;
+ }
+
+ memset(_partProgramsInternal, 0, sizeof(_partProgramsInternal));
+ memset(_partNotes, 0, sizeof(_partNotes));
+ memset(_chanAssign, 0, sizeof(_chanAssign));
+ memset(_chanNotes, 0, sizeof(_chanNotes));
+
+ reset();
+
+ _isOpen = true;
+
+ return 0;
+}
+
+void PC98FMDriver::close() {
+ setTimerCallback(0, 0);
+ _isOpen = false;
+ delete _pc98a;
+ _pc98a = 0;
+}
+
+void PC98FMDriver::noteOn(uint8 part, uint8 note, uint8 velo) {
+ if (_delayedProgramChange && part != 9) {
+ int ch = 0x80;
+ uint8 high = 0x80;
+ for (int i = 2; i >= 0; --i) {
+ if (_chanAssign[i] == 0x80) {
+ ch = i;
+ break;
+ }
+ if (part < _chanAssign[i] && high > _chanAssign[i]) {
+ ch = i;
+ high = _chanAssign[i];
+ }
+ }
+ if (ch == 0x80)
+ return;
+
+ loadInstrument(ch, _partProgramsInternal[part]);
+
+ _partNotes[ch] = note;
+ _chanAssign[ch] = part;
+ part = ch;
+ }
+ startNote(part, note, velo);
+}
+
+void PC98FMDriver::noteOff(uint8 part, uint8 note) {
+ if (_delayedProgramChange) {
+ if (part == 9) {
+ _pc98a->writeReg(0, 6, 0);
+ stopNote(part, note);
+ } else {
+ for (int i = 2; i >= 0; --i) {
+ if (_chanAssign[i] != part || (note != _partNotes[i] && !_allNotes))
+ continue;
+ _chanAssign[i] = 0x80;
+ _partNotes[i] = 0;
+ stopNote(i, note);
+ }
+ }
+ } else {
+ stopNote(part, note);
+ }
+}
+
+void PC98FMDriver::programChange(uint8 part, uint8 prog) {
+ if (!_delayedProgramChange)
+ loadInstrument(part, prog);
+ _partProgramsInternal[part] = prog;
+}
+
+void PC98FMDriver::processSounds() {
+ if (_ngDelay)
+ --_ngDelay;
+ if (!_ngDelay)
+ _pc98a->writeReg(0, 0x0a, 0);
+}
+
+void PC98FMDriver::setVolume(int musicVolume, int sfxVolume) {
+ _pc98a->setMusicVolume(musicVolume);
+ _pc98a->setSoundEffectVolume(sfxVolume);
+}
+
+void PC98FMDriver::loadInstrument(uint8 chan, uint8 prg) {
+ if (chan > 2)
+ return;
+
+ assert(prg < ARRAYSIZE(_instrumentPatches));
+ const uint8 *src = _instrumentPatches[prg];
+ _pc98a->writeReg(0, 0xB0 | chan, *src++);
+
+ for (uint8 reg = 0x30 | chan; reg < 0x40; reg += 4) {
+ _pc98a->writeReg(0, reg, *src++);
+ _pc98a->writeReg(0, reg + 0x10, *src++);
+ _pc98a->writeReg(0, reg + 0x20, *src++);
+ _pc98a->writeReg(0, reg + 0x30, (*src++) & 0x1F);
+ _pc98a->writeReg(0, reg + 0x40, (*src++) & 0x1F);
+ _pc98a->writeReg(0, reg + 0x50, *src++);
+ }
+}
+
+void PC98FMDriver::startNote(uint8 chan, uint8 note, uint8 velo) {
+ if (chan == 9) {
+ if (note >= sizeof(_ngMapping) || _ngMapping[note] == 0xff)
+ return;
+ _pc98a->writeReg(0, 0x06, _ngMapping[note]);
+ _pc98a->writeReg(0, 0x0a, 0x0a);
+ _ngDelay = 3;
+ }
+
+ if (chan > 2)
+ return;
+
+ if (_chanUse[chan] && note < _chanNotes[chan])
+ return;
+
+ _allNotes = true;
+ stopNote(chan, 0);
+ _allNotes = false;
+ _chanNotes[chan] = note;
+ _chanUse[chan]++;
+
+ const uint8 *instr = _instrumentPatches[_partProgramsInternal[chan]];
+ uint8 c = _carrier[*instr & 7];
+
+ instr += 2;
+ const uint8 *pos = instr;
+ uint8 instvl = 0x7F;
+ for (int i = 0; i < 4; ++i) {
+ if (((c >> i) & 1) && *pos < instvl)
+ instvl = *pos;
+ pos += 6;
+ }
+
+ pos = instr;
+ velo = 0x7f - (0x57 + (velo >> 2)) - instvl;
+ for (uint8 i = 0x40 | chan; i < 0x50; i += 4) {
+ if (c & 1)
+ _pc98a->writeReg(0, i, MIN<uint8>(*pos + velo, 0x7f));
+ pos += 6;
+ c >>= 1;
+ }
+
+ if (note > 18)
+ note -= 12;
+ uint16 frq = _frequency[note % 12];
+ uint8 bl = (note / 12) << 3;
+ _pc98a->writeReg(0, 0xa4 | chan, (frq >> 8) | bl);
+ _pc98a->writeReg(0, 0xa0 | chan, frq & 0xff);
+
+ _pc98a->writeReg(0, 0x28, 0xF0 | chan);
+}
+
+void PC98FMDriver::stopNote(uint8 chan, uint8 note) {
+ if (chan > 2)
+ return;
+
+ if (_allNotes || note == _chanNotes[chan])
+ _pc98a->writeReg(0, 0x28, chan);
+}
+
+void PC98FMDriver::timerCallbackB() {
+ updateSounds();
+ PC98AudioCore::MutexLock tempUnlock = _pc98a->stackUnlockMutex();
+ updateParser();
+}
+
+const uint8 PC98FMDriver::_instrumentsRemapFM[128] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x09, 0x0a, 0x0b, 0x0c, 0x01, 0x02, 0x0f,
+ 0x0f, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x19, 0x1b, 0x1c, 0x1d, 0x1e, 0x03,
+ 0x04, 0x21, 0x22, 0x23, 0x05, 0x25, 0x06, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2e,
+ 0x30, 0x31, 0x35, 0x07, 0x35, 0x35, 0x36, 0x37, 0x38, 0x08, 0x3a, 0x3b, 0x3c, 0x3e, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x44, 0x44, 0x45, 0x09, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x0a, 0x51, 0x51,
+ 0x51, 0x54, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5a, 0x5a, 0x5e, 0x5f,
+ 0x60, 0x61, 0x67, 0x63, 0x0c, 0x65, 0x66, 0x67, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
+};
+
+const uint8 PC98FMDriver::_instrumentLevelAdjustFM[128] = {
+ 0x28, 0x0f, 0x28, 0x1d, 0x14, 0x14, 0x23, 0x19, 0x28, 0x13, 0x23, 0x23, 0x30, 0x23, 0x17, 0x16,
+ 0x23, 0x14, 0x15, 0x13, 0x23, 0x23, 0x19, 0x19, 0x32, 0x19, 0x16, 0x23, 0x05, 0x0a, 0x05, 0x0a,
+ 0x35, 0x28, 0x2d, 0x23, 0x19, 0x1c, 0x22, 0x23, 0x23, 0x1a, 0x2d, 0x23, 0x23, 0x23, 0x23, 0x1e,
+ 0x32, 0x1e, 0x37, 0x23, 0x18, 0x2e, 0x2b, 0x32, 0x11, 0x14, 0x0f, 0x0f, 0x14, 0x14, 0x14, 0x14,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x0a, 0x28, 0x0d, 0x14, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x0a, 0x19, 0x19, 0x14, 0x14, 0x12, 0x14, 0x2b, 0x2c, 0x34, 0x2e, 0x30, 0x31, 0x15, 0x29,
+ 0x32, 0x23, 0x23, 0x23, 0x23, 0x0b, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x14, 0x14, 0x1e, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x2c, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x00, 0x23, 0x23, 0x23
+};
+
+const uint8 PC98FMDriver::_partsRemapFM[16] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+};
+
+const uint8 PC98FMDriver::_instrumentPatches[16][25] = {
+ { 0x3f, 0x06, 0x6b, 0x1f, 0x0e, 0x00, 0xff, 0x03, 0x0a, 0x1f, 0x02, 0x01, 0x0f, 0x03, 0x0a, 0x1f, 0x02, 0x01, 0x0f, 0x02, 0x00, 0x1f, 0x02, 0x01, 0x0f },
+ { 0x3f, 0x38, 0x75, 0x1f, 0x81, 0x01, 0x0a, 0x01, 0x00, 0x14, 0x82, 0x01, 0x0a, 0x73, 0x00, 0x14, 0x82, 0x01, 0x0a, 0x62, 0x00, 0x14, 0x82, 0x01, 0x0a },
+ { 0x07, 0x08, 0x3e, 0xdf, 0x15, 0x00, 0x0f, 0x03, 0x11, 0x5f, 0x1f, 0x00, 0x0a, 0x02, 0x25, 0x5d, 0x1f, 0x00, 0x0a, 0x02, 0x00, 0x92, 0x1f, 0x00, 0x0a },
+ { 0x3d, 0x4a, 0x23, 0x1b, 0x11, 0x00, 0xfa, 0x41, 0x00, 0x14, 0x00, 0x00, 0x0a, 0x42, 0x00, 0x14, 0x00, 0x00, 0x0a, 0x40, 0x00, 0x14, 0x00, 0x00, 0x0a },
+ { 0x3c, 0x0b, 0x2b, 0x5a, 0x02, 0x01, 0x35, 0x63, 0x2a, 0x55, 0x01, 0x00, 0x24, 0x03, 0x19, 0x5c, 0x09, 0x05, 0x44, 0x21, 0x00, 0x4d, 0x06, 0x00, 0x44 },
+ { 0x3c, 0x01, 0x20, 0x52, 0x0c, 0x01, 0x5a, 0x22, 0x17, 0x4f, 0x0a, 0x01, 0x2a, 0x26, 0x05, 0x45, 0x0a, 0x00, 0x0f, 0x31, 0x00, 0x47, 0x02, 0x00, 0x0f },
+ { 0x2c, 0x3a, 0x2d, 0x58, 0x0e, 0x00, 0xf7, 0x05, 0x39, 0x5a, 0x0e, 0x00, 0xf4, 0x02, 0x00, 0x58, 0x08, 0x00, 0xf4, 0x01, 0x05, 0x9a, 0x08, 0x00, 0xf4 },
+ { 0x3c, 0x01, 0x20, 0x52, 0x0c, 0x01, 0x2a, 0x21, 0x17, 0x4f, 0x0a, 0x01, 0x5a, 0x11, 0x00, 0x12, 0x8a, 0x01, 0x3a, 0x61, 0x07, 0x14, 0x82, 0x01, 0x3a },
+ { 0x00, 0x01, 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x23, 0x5f, 0x05, 0x00, 0xf3, 0x71, 0x1b, 0x5f, 0x0c, 0x01, 0xf5, 0x01, 0x07, 0x5f, 0x0c, 0x00, 0xf5 },
+ { 0x18, 0x37, 0x2c, 0x9e, 0x0d, 0x08, 0xb6, 0x31, 0x18, 0x1c, 0x04, 0x03, 0x36, 0x30, 0x22, 0xdc, 0x06, 0x0a, 0xb6, 0x32, 0x00, 0x9c, 0x01, 0x05, 0x26 },
+ { 0x3b, 0x0a, 0x00, 0x1f, 0x00, 0x00, 0x0a, 0x02, 0x22, 0x18, 0x0e, 0x00, 0x0a, 0x60, 0x3a, 0x18, 0x0c, 0x00, 0xfa, 0x31, 0x00, 0x51, 0x0b, 0x00, 0x38 },
+ { 0x2c, 0x34, 0x21, 0x58, 0x0e, 0x00, 0xf7, 0x76, 0x2f, 0x58, 0x14, 0x00, 0xfa, 0x30, 0x00, 0xd8, 0x84, 0x00, 0xf2, 0x72, 0x0b, 0x98, 0x8c, 0x00, 0xf6 },
+ { 0x3b, 0x08, 0x06, 0x5f, 0x00, 0x00, 0x1a, 0x01, 0x19, 0x4e, 0x0e, 0x00, 0x0a, 0x61, 0x30, 0x58, 0x18, 0x00, 0x5a, 0x30, 0x00, 0x50, 0x0b, 0x00, 0x38 },
+ { 0x3b, 0x0a, 0x0d, 0x16, 0x00, 0x00, 0x0a, 0x00, 0x0b, 0x1a, 0x16, 0x40, 0xfb, 0x0d, 0x13, 0x1a, 0x1a, 0xc0, 0xfa, 0x01, 0x00, 0x5e, 0x8e, 0x00, 0xf7 },
+ { 0x3b, 0x0e, 0x0c, 0x1f, 0x00, 0x00, 0x05, 0x0a, 0x25, 0x1b, 0x1b, 0x80, 0xfa, 0x00, 0x31, 0x1f, 0x0a, 0xc0, 0xf5, 0x00, 0x00, 0x5c, 0x8e, 0x40, 0xf7 },
+ { 0x32, 0x02, 0x15, 0x58, 0x14, 0x00, 0xfa, 0x31, 0x25, 0x5f, 0x0a, 0x40, 0xf4, 0x01, 0x17, 0x5a, 0x0c, 0x80, 0xf6, 0x01, 0x00, 0x9a, 0x8b, 0x00, 0xf5 }
+};
+
+const uint8 PC98FMDriver::_ngMapping[76] = {
+ 0x18, 0x1c, 0x3c, 0x20, 0x3e, 0x24, 0x40, 0x28, 0x2c, 0x38, 0x30, 0x3c, 0x00, 0x00, 0x36, 0x00, 0x38, 0x00, 0x00,
+ 0x00, 0x16, 0x11, 0x16, 0x16, 0x11, 0x16, 0x11, 0x16, 0x11, 0x0f, 0x32, 0x00, 0x00, 0x0d, 0x00, 0x10, 0x1f, 0x1f,
+ 0x0a, 0x08, 0x0a, 0x19, 0x04, 0x19, 0x04, 0x14, 0x04, 0x14, 0x0f, 0x0c, 0x0f, 0x0c, 0xff, 0xff, 0x0d, 0xff, 0x12,
+ 0xff, 0xff, 0xff, 0x0f, 0x19, 0x0f, 0x0f, 0x19, 0x0f, 0x19, 0x0f, 0x19, 0x14, 0x06, 0xff, 0xff, 0x0f, 0xff, 0x00
+};
+
+const uint8 PC98FMDriver::_carrier[8] = {
+ 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0E, 0x0E, 0x0F
+};
+
+const uint16 PC98FMDriver::_frequency[12] = {
+ 0x0267, 0x028d, 0x02b3, 0x02dd, 0x0308, 0x0337, 0x0368, 0x039c, 0x03d3, 0x040e, 0x044b, 0x048b
+};
+
+#define MIDIMSG32(s, p1, p2) (p2 << 16 | p1 << 8 | s)
+
+PC98MidiDriver::PC98MidiDriver(MidiDriver::DeviceHandle dev) : _dev(dev), _drv(0) {
+ _instrumentsRemap = (getMusicType(dev) == MT_MT32) ? _instrumentsRemapMT32 : (getMusicType(dev) == MT_GM ? _instrumentsRemapGM : 0);
+ int8 *tbl2 = new int8[128];
+ memset(tbl2, 0, 128);
+ _instrumentLevelAdjust = tbl2;
+ _partsRemap = _partsRemapMidi;
+ memcpy(_volSysex, _sysexMsg[0], 9);
+ memcpy(_partAssignSysexGS, _sysexMsg[1], 9);
+ memcpy(_partAssignSysexMT32, _sysexMsg[2], 9);
+}
+
+PC98MidiDriver::~PC98MidiDriver() {
+ close();
+ delete[] _instrumentLevelAdjust;
+}
+
+int PC98MidiDriver::open() {
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ delete _drv;
+
+ _drv = MidiDriver::createMidi(_dev);
+ if (!_drv || !_instrumentsRemap)
+ return MERR_DEVICE_NOT_AVAILABLE;
+
+ _baseTempo = _drv->getBaseTempo();
+ int res = _drv->open();
+
+ if (!res) {
+ _drv->setTimerCallback(this, &timerCallback);
+
+ for (byte i = 0xB1; i < 0xBA; ++i)
+ _drv->send(MIDIMSG32(i, 0x79, 0));
+
+ property(kPropMusicVolume, Audio::Mixer::kMaxChannelVolume);
+
+ if (getMusicType(_dev) == MT_MT32) {
+ _partAssignSysexGS[7] = 0x10;
+ for (uint8 i = 0x10; i < 0x20; ++i) {
+ _partAssignSysexGS[5] = i;
+ sendSysexWithCheckSum(_partAssignSysexGS);
+ }
+
+ for (uint8 i = 0x01; i < 0x0A; ++i) {
+ _partAssignSysexMT32[6] = 0x0C + i;
+ _partAssignSysexMT32[7] = i;
+ sendSysexWithCheckSum(_partAssignSysexMT32);
+ }
+
+ } else if (getMusicType(_dev) == MT_GM) {
+ _partAssignSysexGS[5] = 0x10;
+ _partAssignSysexGS[7] = 9;
+ sendSysexWithCheckSum(_partAssignSysexGS);
+ uint8 p = 0;
+ for (uint8 i = 0x11; i < 0x20; ++i) {
+ _partAssignSysexGS[5] = i;
+ _partAssignSysexGS[7] = p++;
+ if (p == 9)
+ p++;
+ sendSysexWithCheckSum(_partAssignSysexGS);
+ }
+
+ _partAssignSysexMT32[7] = 0x10;
+ for (uint8 i = 0x0D; i < 0x16; ++i) {
+ _partAssignSysexMT32[6] = i;
+ sendSysexWithCheckSum(_partAssignSysexMT32);
+ }
+
+ _drv->send(MIDIMSG32(0xB9, 0x07, 0x46));
+ }
+
+ reset();
+ _isOpen = true;
+ }
+
+ return res;
+}
+
+void PC98MidiDriver::close() {
+ setTimerCallback(0, 0);
+ _isOpen = false;
+ if (!_drv)
+ return;
+
+ _drv->setTimerCallback(0, 0);
+ _mixer->stopAll();
+ _drv->close();
+ delete _drv;
+ _drv = 0;
+}
+
+void PC98MidiDriver::timerCallback(void *obj) {
+ PC98MidiDriver *drv = static_cast<PC98MidiDriver*>(obj);
+ drv->updateSounds();
+ drv->updateParser();
+}
+
+void PC98MidiDriver::noteOn(uint8 part, uint8 note, uint8 velo) {
+ _drv->send(MIDIMSG32(0x90 | _partsRemap[part & 0x0F], note, velo));
+}
+
+void PC98MidiDriver::noteOff(uint8 part, uint8 note) {
+ if (_allNotes)
+ _drv->send(MIDIMSG32(0xB0 | _partsRemap[part & 0x0F], 0x7B, 0));
+ else
+ _drv->send(MIDIMSG32(0x80 | _partsRemap[part & 0x0F], note, 0));
+}
+
+void PC98MidiDriver::programChange(uint8 part, uint8 prog) {
+ if (!_programLock)
+ _drv->send(MIDIMSG32(0xC0 | _partsRemap[part & 0x0F], prog, 0));
+}
+
+void PC98MidiDriver::setVolume(int musicVolume, int sfxVolume) {
+ if (!_isOpen)
+ return;
+
+ if (getMusicType(_dev) == MT_MT32) {
+ _volSysex[7] = musicVolume * 100 / Audio::Mixer::kMaxChannelVolume;
+ sendSysexWithCheckSum(_volSysex);
+ } else {
+ uint8 vol = musicVolume * 127 / Audio::Mixer::kMaxChannelVolume;
+ for (int i = 0; i < 16; ++i)
+ _drv->send(MIDIMSG32(0xB0 | _partsRemap[i], 0x07, vol));
+ }
+}
+
+void PC98MidiDriver::pause(bool paused) {
+ if (paused) {
+ _allNotes = true;
+ for (int i = 0; i < 16; ++i)
+ noteOff(i, 0);
+ _allNotes = false;
+ }
+}
+
+void PC98MidiDriver::sendSysexWithCheckSum(uint8 *data) {
+ uint8 chk = 0;
+ for (int i = 4; i < 8; ++i)
+ chk += data[i];
+ data[8] = 0x80 - (chk & 0x7f);
+ _drv->sysEx(data, 9);
+}
+
+const uint8 PC98MidiDriver::_instrumentsRemapMT32[128] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x6e, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
+};
+
+const uint8 PC98MidiDriver::_instrumentsRemapGM[128] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x09, 0x0a, 0x0b, 0x0c, 0x10, 0x10, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x26,
+ 0x58, 0x21, 0x22, 0x23, 0x61, 0x25, 0x0b, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x2d, 0x34, 0x35, 0x36, 0x37, 0x38, 0x2e, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x23, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4c, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x4c, 0x65, 0x66, 0x67, 0x0c, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x75, 0x76, 0x74, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
+};
+
+const uint8 PC98MidiDriver::_partsRemapMidi[16] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0f, 0x09, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f
+};
+
+const uint8 PC98MidiDriver::_sysexMsg[3][9] = {
+ { 0x41, 0x10, 0x16, 0x12, 0x10, 0x00, 0x16, 0x64, 0x00 },
+ { 0x41, 0x10, 0x42, 0x12, 0x40, 0x10, 0x02, 0x10, 0x00 },
+ { 0x41, 0x10, 0x16, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00 }
+};
+
+MidiDriver *MidiDriverPC98_create(MidiDriver::DeviceHandle dev) {
+ MusicType type = MidiDriver::getMusicType(dev);
+ if (type == MT_PC98)
+ return new PC98FMDriver();
+ else if (type == MT_GM || type == MT_MT32)
+ return new PC98MidiDriver(dev);
+ return 0;
+}
+
+#undef MIDIMSG32
+
+} // End of namespace AGOS
diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp
index 6f9d73c48a..2f9d2bb971 100644
--- a/engines/agos/midi.cpp
+++ b/engines/agos/midi.cpp
@@ -98,7 +98,7 @@ int MidiPlayer::open(int gameType, Common::Platform platform, bool isDemo) {
switch (gameType) {
case GType_ELVIRA1:
if (platform == Common::kPlatformPC98) {
- _musicMode = kMusicModeDisabled;
+ _musicMode = kMusicModePC98;
devFlags = (devFlags & ~MDT_ADLIB) | MDT_PC98;
} else {
_musicMode = kMusicModeAccolade;
@@ -135,7 +135,14 @@ int MidiPlayer::open(int gameType, Common::Platform platform, bool isDemo) {
MidiDriver::DeviceHandle dev;
int ret = 0;
- if (_musicMode != kMusicModeDisabled) {
+ if (_musicMode == kMusicModePC98) {
+ dev = MidiDriver::detectDevice(devFlags);
+ _driver = MidiDriverPC98_create(dev);
+ if (_driver && !_driver->open()) {
+ _driver->setTimerCallback(this, &onTimer);
+ return 0;
+ }
+ } else if (_musicMode != kMusicModeDisabled) {
dev = MidiDriver::detectDevice(devFlags);
musicType = MidiDriver::getMusicType(dev);
@@ -169,7 +176,6 @@ int MidiPlayer::open(int gameType, Common::Platform platform, bool isDemo) {
switch (musicType) {
case MT_ADLIB:
_driver = MidiDriver_Accolade_AdLib_create(accoladeDriverFilename);
-
break;
case MT_MT32:
_driver = MidiDriver_Accolade_MT32_create(accoladeDriverFilename);
@@ -487,7 +493,10 @@ void MidiPlayer::pause(bool b) {
// if using the driver Accolade_AdLib call setVolume() to turn off\on the volume on all channels
if (musicType == MT_ADLIB && _musicMode == kMusicModeAccolade) {
static_cast <MidiDriver_Accolade_AdLib*> (_driver)->setVolume(_paused ? 0 : 128);
+ } else if (_musicMode == kMusicModePC98) {
+ _driver->property(0x30, _paused ? 1 : 0);
}
+
for (int i = 0; i < 16; ++i) {
if (_music.channel[i])
_music.channel[i]->volume(_paused ? 0 : (_music.volume[i] * _musicVolume / 255));
@@ -506,6 +515,11 @@ void MidiPlayer::setVolume(int musicVol, int sfxVol) {
_musicVolume = musicVol;
_sfxVolume = sfxVol;
+ if (_musicMode == kMusicModePC98) {
+ _driver->property(0x10, _musicVolume);
+ _driver->property(0x20, _sfxVolume);
+ }
+
// Now tell all the channels this.
Common::StackLock lock(_mutex);
if (_driver && !_paused) {
diff --git a/engines/agos/midi.h b/engines/agos/midi.h
index 7b5cd790ee..68404cb6bd 100644
--- a/engines/agos/midi.h
+++ b/engines/agos/midi.h
@@ -37,7 +37,8 @@ enum kMusicMode {
kMusicModeDisabled = 0,
kMusicModeAccolade = 1,
kMusicModeMilesAudio = 2,
- kMusicModeSimon1 = 3
+ kMusicModeSimon1 = 3,
+ kMusicModePC98 = 4
};
struct MusicInfo {
diff --git a/engines/agos/module.mk b/engines/agos/module.mk
index 919f819c1c..f128e7917b 100644
--- a/engines/agos/module.mk
+++ b/engines/agos/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/agos
MODULE_OBJS := \
drivers/accolade/adlib.o \
drivers/accolade/driverfile.o \
+ drivers/accolade/pc98.o \
drivers/accolade/mt32.o \
drivers/simon1/adlib.o \
agos.o \
Commit: 77b316ef58b782166c441222ea04492e453b22a7
https://github.com/scummvm/scummvm/commit/77b316ef58b782166c441222ea04492e453b22a7
Author: athrxx (athrxx at scummvm.org)
Date: 2021-01-22T00:47:59+01:00
Commit Message:
AGOS: (ELVIRA) - block mouse wheel during save/load/pause dialog
I assume that this happens accidently, since the mouse wheel was never supported by the original. Currently, you can still scroll the inventory using the mouse wheel when a save/load/pause dialog is open. It causes a text glitch in the PC-98 version. Instead of just fixing that glitch I've decided to fix the underlying bug (limited to Elvira 1 though; no idea what the behavior is in the other games).
Changed paths:
engines/agos/input.cpp
diff --git a/engines/agos/input.cpp b/engines/agos/input.cpp
index 3b57369f1e..4a10f151f9 100644
--- a/engines/agos/input.cpp
+++ b/engines/agos/input.cpp
@@ -514,14 +514,14 @@ void AGOSEngine_Elvira2::handleMouseWheelDown() {
void AGOSEngine::handleMouseWheelUp() {
HitArea *ha = findBox(0x7FFB);
- if (ha != NULL && (ha->flags & kBFBoxInUse)) {
+ if (ha != NULL && (ha->flags & kBFBoxInUse) && !(getGameId() == GID_ELVIRA1 && _windowNum == 3)) {
inventoryUp(ha->window);
}
}
void AGOSEngine::handleMouseWheelDown() {
HitArea *ha = findBox(0x7FFC);
- if (ha != NULL && (ha->flags & kBFBoxInUse)) {
+ if (ha != NULL && (ha->flags & kBFBoxInUse) && !(getGameId() == GID_ELVIRA1 && _windowNum == 3)) {
inventoryDown(ha->window);
}
}
Commit: 8969fa9c090081b3a9795a11b802e9a7905cfcb3
https://github.com/scummvm/scummvm/commit/8969fa9c090081b3a9795a11b802e9a7905cfcb3
Author: athrxx (athrxx at scummvm.org)
Date: 2021-01-22T00:47:59+01:00
Commit Message:
AGOS: (ELVIRA/PC98/Jp) - fix game restart
Like most of the other data in the PC-98 version the restart save file also requires decompression.
Changed paths:
engines/agos/saveload.cpp
diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp
index 356babdea3..e797b14ebb 100644
--- a/engines/agos/saveload.cpp
+++ b/engines/agos/saveload.cpp
@@ -1047,12 +1047,16 @@ bool AGOSEngine::loadGame(const Common::String &filename, bool restartMode) {
if (restartMode) {
// Load restart state
- Common::File *file = new Common::File();
- if (!file->open(filename)) {
- delete file;
- file = nullptr;
+ if (getPlatform() == Common::kPlatformPC98 && !filename.compareToIgnoreCase("start")) {
+ f = createPak98FileStream("START.PAK");
+ } else {
+ Common::File *file = new Common::File();
+ if (!file->open(filename)) {
+ delete file;
+ file = nullptr;
+ }
+ f = file;
}
- f = file;
} else {
f = _saveFileMan->openForLoading(filename);
}
Commit: 656a35ee87c6e06fc1a61dff9eb6b1119706466c
https://github.com/scummvm/scummvm/commit/656a35ee87c6e06fc1a61dff9eb6b1119706466c
Author: athrxx (athrxx at scummvm.org)
Date: 2021-01-22T00:51:05+01:00
Commit Message:
NEWS: mention Elvira PC-98 support
Changed paths:
NEWS.md
diff --git a/NEWS.md b/NEWS.md
index 58e3ad1c52..5cf40b893d 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -21,6 +21,9 @@ For a more comprehensive changelog of the latest experimental code, see:
- Switched ScummVM GUI output to UTF-32.
- Updated the Roland MT-32 emulation code to the Munt project's mt32emu 2.4.2.
+ AGOS:
+ - Added support for the Japanese PC-98 version of Elvira 1.
+
Dreamweb:
- Rendering fixes for Russian fan translation.
More information about the Scummvm-git-logs
mailing list