Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,553 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "common/str.h"
#include "common/scummsys.h"
#include "glk/scott/scott.h"
#include "glk/scott/globals.h"
#include "glk/scott/c64_checksums.h"
#include "glk/scott/definitions.h"
#include "glk/scott/disk_image.h"
#include "glk/scott/game_info.h"
#include "glk/scott/resource.h"
#include "glk/scott/saga_draw.h"
#include "glk/scott/unp64/unp64_interface.h"
namespace Glk {
namespace Scott {
#define MAX_LENGTH 300000
#define MIN_LENGTH 24
enum FileType {
UNKNOWN_FILE_TYPE,
TYPE_D64,
TYPE_T64
};
struct C64Rec {
GameIDType _id;
size_t _length;
uint16_t _chk;
FileType _type;
int _decompressIterations;
const char *_switches;
const char *_appendFile;
int _parameter;
size_t _copySource;
size_t _copyDest;
size_t _copySize;
int _imgOffset;
};
static C64Rec g_C64Registry[] = {
{ BATON_C64, 0x2ab00, 0xc3fc, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Mysterious Adventures C64 dsk 1
{ TIME_MACHINE_C64, 0x2ab00, 0xc3fc, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 },
{ ARROW1_C64, 0x2ab00, 0xc3fc, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 },
{ ARROW2_C64, 0x2ab00, 0xc3fc, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 },
{ PULSAR7_C64, 0x2ab00, 0xc3fc, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 },
{ CIRCUS_C64, 0x2ab00, 0xc3fc, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 },
{ FEASIBILITY_C64, 0x2ab00, 0x9eaa, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Mysterious Adventures C64 dsk 2
{ AKYRZ_C64, 0x2ab00, 0x9eaa, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 },
{ PERSEUS_C64, 0x2ab00, 0x9eaa, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 },
{ INDIANS_C64, 0x2ab00, 0x9eaa, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 },
{ WAXWORKS_C64, 0x2ab00, 0x9eaa, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 },
{ BATON_C64, 0x2ab00, 0x9dca, TYPE_D64, 2, nullptr, nullptr, 0, 0, 0, 0, 0 },
{ ROBIN_OF_SHERWOOD_C64, 0x2ab00, 0xcf9e, TYPE_D64, 1, nullptr, nullptr, 0, 0x1802, 0xbd27, 0x2000, 0 }, // Robin Of Sherwood D64 * unknown packer
{ ROBIN_OF_SHERWOOD_C64, 0xb2ef, 0x7c44, TYPE_T64, 1, nullptr, nullptr, 0, 0x9702, 0x9627, 0x2000, 0 }, // Robin Of Sherwood C64 (T64) * TCS Cruncher v2.0
{ ROBIN_OF_SHERWOOD_C64, 0xb690, 0x7b61, TYPE_T64, 1, nullptr, nullptr, 0, 0x9702, 0x9627, 0x2000, 0 }, // Robin Of Sherwood C64 (T64) alt * TCS Cruncher v2.0
{ ROBIN_OF_SHERWOOD_C64, 0x8db6, 0x7853, TYPE_T64, 1, nullptr, nullptr, 0, 0xd7fb, 0xbd20, 0x2000, 0 }, // Robin Of Sherwood T64 alt 2 * PUCrunch
{ GREMLINS_C64, 0xdd94, 0x25a8, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Gremlins C64 (T64) version * Action Replay v4.x
{ GREMLINS_C64, 0x2ab00, 0xc402, TYPE_D64, 0, nullptr, "G1", -0x8D, 0, 0, 0, 0 }, // Gremlins C64 (D64) version
{ GREMLINS_C64, 0x2ab00, 0x3ccf, TYPE_D64, 0, nullptr, "G1", -0x8D, 0, 0, 0, 0 }, // Gremlins C64 (D64) version 2
{ GREMLINS_C64, 0x2ab00, 0xabf8, TYPE_D64, 2, "-e0x1255",nullptr, 2, 0, 0, 0, 0 }, // Gremlins C64 (D64) version alt * ByteBoiler, Exomizer
{ GREMLINS_C64, 0x2ab00, 0xa265, TYPE_D64, 2, "-e0x1255",nullptr, 2, 0, 0, 0, 0 }, // Gremlins C64 (D64) version alt 2 * ByteBoiler, Exomizer
{ GREMLINS_GERMAN_C64, 0xc003, 0x558c, TYPE_T64, 1, nullptr, nullptr, 0, 0xd801, 0xc6c0, 0x1f00, 0 }, // German Gremlins C64 (T64) version * TBC Multicompactor v2.x
{ GREMLINS_GERMAN_C64, 0x2ab00, 0x6729, TYPE_D64, 2, nullptr, nullptr, 0, 0xdc02, 0xcac1, 0x1f00, 0 }, // German Gremlins C64 (D64) version * Exomizer
{ SEAS_OF_BLOOD_C64, 0xa209, 0xf115, TYPE_T64, 6, "-e0x1000", nullptr, 3, 0xd802, 0xb07c, 0x2000, 0 }, // Seas of Blood C64 (T64) MasterCompressor / Relax -> ECA
// Compacker -> Unknown -> MasterCompressor / Relax -> ECA
// Compacker -> CCS Packer
{ SEAS_OF_BLOOD_C64, 0x2ab00, 0x5c1d, TYPE_D64, 1, nullptr, nullptr, 0, 0xd802, 0xb07c, 0x2000, 0 }, // Seas of Blood C64 (D64) CCS Packer
{ SEAS_OF_BLOOD_C64, 0x2ab00, 0xe308, TYPE_D64, 1, nullptr, nullptr, 0, 0xd802, 0xb07c, 0x2000, 0 }, // Seas of Blood C64 (D64) alt CCS Packer
{ CLAYMORGUE_C64, 0x6ff7, 0xe4ed, TYPE_T64, 3, nullptr, nullptr, 0, 0x855, 0x7352, 0x20, 0 }, // Sorcerer Of Claymorgue Castle C64 (T64), MasterCompressor / Relax
// -> ECA Compacker -> MegaByte Cruncher v1.x Missing 17 pictures
{ CLAYMORGUE_C64, 0x912f, 0xa69f, TYPE_T64, 1, nullptr, nullptr, 0, 0x855, 0x7352, 0x20, 0 }, // Sorcerer Of Claymorgue Castle C64 (T64) alt, MegaByte Cruncher
// v1.x Missing 17 pictures
{ CLAYMORGUE_C64, 0xc0dd, 0x3701, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, -0x7fe },// Sorcerer Of Claymorgue Castle C64 (T64) alt 2, Trilogic Expert
// v2.7
{ CLAYMORGUE_C64, 0xbc5f, 0x492c, TYPE_T64, 1, nullptr, nullptr, 0, 0x855, 0x7352, 0x20, 0 }, // Sorcerer Of Claymorgue Castle C64 (T64) alt 3, , Section8 Packer
{ CLAYMORGUE_C64, 0x2ab00, 0xfd67, TYPE_D64, 1, nullptr, nullptr, 0, 0x855, 0x7352, 0x20, 0 }, // Sorcerer Of Claymorgue Castle C64 (D64), Section8 Packer
{ ADVENTURELAND_C64, 0x6a10, 0x1910, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Adventureland C64 (T64) CruelCrunch v2.2
{ ADVENTURELAND_C64, 0x6a10, 0x1b10, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Adventureland C64 (T64) alt CruelCrunch v2.2
{ ADVENTURELAND_C64, 0x2ab00, 0x6638, TYPE_D64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Adventureland C64 (D64) CruelCrunch v2.2
{ ADVENTURELAND_C64, 0x2adab, 0x751f, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Adventureland C64 (D64) alt
{ ADVENTURELAND_C64, 0x2adab, 0x64a4, TYPE_D64, 0, nullptr, "SAG1PIC", -0xa53, 0, 0, 0, 0x65af }, // Adventureland C64 (D64) alt 2
{ SAVAGE_ISLAND_C64, 0x2ab00, 0x8801, TYPE_D64, 1, "-f86 -d0x1793", "SAVAGEISLAND1+", 1, 0, 0, 0, 0 }, // Savage Island part 1 C64 (D64)
{ SAVAGE_ISLAND2_C64, 0x2ab00, 0x8801, TYPE_D64, 1, "-f86 -d0x178b", "SAVAGEISLAND2+", 1, 0, 0, 0, 0 }, // Savage Island part 2 C64 (D64)
{ SAVAGE_ISLAND_C64, 0x2ab00, 0xc361, TYPE_D64, 1, "-f86 -d0x1793", "SAVAGE ISLAND P1", 1, 0, 0, 0, 0 }, // Savage Island part 1 C64 (D64) alt
{ SAVAGE_ISLAND2_C64, 0x2ab00, 0xc361, TYPE_D64, 1, nullptr, "SAVAGE ISLAND P2", 0, 0, 0, 0, 0 }, // Savage Island part 2 C64 (D64) alt
{ HULK_C64, 0x2ab00, 0xcdd8, TYPE_D64, 0, nullptr, nullptr, 0, 0x1806, 0xb801, 0x307, 0 }, // Questprobe 1 - The Hulk C64 (D64)
{ SPIDERMAN_C64, 0x2ab00, 0xde56, TYPE_D64, 0, nullptr, nullptr, 0, 0x1801, 0xa801, 0x2000, 0 }, // Spiderman C64 (D64)
{ SPIDERMAN_C64, 0x08e72, 0xb2f4, TYPE_T64, 3, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Spiderman C64 (T64) MasterCompressor / Relax -> ECA Compacker -> Section8 Packer
{ BATON_C64, 0x5170, 0xb240, TYPE_T64, 2, nullptr, nullptr, 0, 0, 0, 0, 0 }, // The Golden Baton C64, T64
{ BATON_C64, 0x2ab00, 0xbfbf, TYPE_D64, 2, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Mysterious Adventures C64 dsk 1 alt
{ FEASIBILITY_C64, 0x2ab00, 0x9c18, TYPE_D64, 2, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Mysterious Adventures C64 dsk 2 alt
{ TIME_MACHINE_C64, 0x5032, 0x5635, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // The Time Machine C64
{ ARROW1_C64, 0x5b46, 0x92db, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Arrow of Death part 1 C64
{ ARROW2_C64, 0x5fe2, 0xe14f, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Arrow of Death part 2 C64
{ PULSAR7_C64, 0x46bf, 0x1679, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Escape from Pulsar 7 C64
{ CIRCUS_C64, 0x4269, 0xa449, TYPE_T64, 2, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Circus C64
{ FEASIBILITY_C64, 0x5a7b, 0x0f48, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Feasibility Experiment C64
{ AKYRZ_C64, 0x2ab00, 0x6cca, TYPE_D64, 0, nullptr, nullptr, 0, 0, 0, 0, 0 }, // The Wizard of Akyrz C64
{ AKYRZ_C64, 0x4be1, 0x5a00, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // The Wizard of Akyrz C64, T64
{ PERSEUS_C64, 0x502b, 0x913b, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Perseus and Andromeda C64
{ INDIANS_C64, 0x4f9f, 0xe6c8, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Ten Little Indians C64
{ WAXWORKS_C64, 0x4a11, 0xa37a, TYPE_T64, 1, nullptr, nullptr, 0, 0, 0, 0, 0 }, // Waxworks C64
{ SUPERGRAN_C64, 0x726f, 0x0901, TYPE_T64, 1, nullptr, nullptr, 0, 0xd802, 0xc623, 0x1f00, 0 }, // Super Gran C64 (T64) PUCrunch Generic Hack
{ UNKNOWN_GAME, 0, 0, UNKNOWN_FILE_TYPE, 0, nullptr, nullptr, 0, 0, 0, 0, 0 }
};
int decrunchC64(uint8_t **sf, size_t *extent, C64Rec entry);
uint8_t *getLargestFile(uint8_t *data, int length, int *newlength) {
uint8_t *file = nullptr;
*newlength = 0;
DiskImage *d64 = diCreateFromData(data, length);
if (d64) {
RawDirEntry *largest = findLargestFileEntry(d64);
if (largest) {
ImageFile *c64file = diOpen(d64, largest->_rawname, largest->_type, "rb");
if (c64file) {
uint8_t *largeFile = new uint8_t[0xffff];
*newlength = diRead(c64file, largeFile, 0xffff);
file = new uint8_t[*newlength];
memcpy(file, largeFile, *newlength);
}
}
//di_free_image(d64);
}
return file;
}
uint8_t *getFileNamed(uint8_t* data, int length, int* newLength, const char* name) {
uint8_t *file = nullptr;
*newLength = 0;
DiskImage *d64 = diCreateFromData(data, length);
byte rawname[100];
diRawnameFromName(rawname, name);
if (d64) {
ImageFile *c64file = diOpen(d64, rawname, 0xC2, "rb");
if (c64file) {
uint8_t *buf = new uint8_t[0xffff];
*newLength = diRead(c64file, buf, 0xffff);
file = new uint8_t[*newLength];
memcpy(file, buf, *newLength);
delete[] buf;
}
}
return file;
}
int savageIslandMenu(uint8_t **sf, size_t *extent, int recIndex) {
g_scott->output("This disk image contains two games. Select one.\n\n"
"1. Savage Island part I\n"
"2. Savage Island part II");
g_scott->glk_request_char_event(_G(_bottomWindow));
event_t ev;
int result = 0;
do {
g_scott->glk_select(&ev);
if (ev.type == evtype_CharInput) {
if (ev.val1 == '1' || ev.val1 == '2') {
result = ev.val1 - '0';
} else {
g_scott->glk_request_char_event(_G(_bottomWindow));
}
}
if (g_vm->shouldQuit())
return 0;
} while (result == 0);
g_scott->glk_window_clear(_G(_bottomWindow));
recIndex += result - 1;
C64Rec rec = g_C64Registry[recIndex];
int length;
uint8_t *file = getFileNamed(*sf, *extent, &length, rec._appendFile);
if (file != nullptr) {
if (rec._chk == 0xc361) {
if (rec._switches != nullptr) {
_G(_saveIslandAppendix1) = getFileNamed(*sf, *extent, &_G(_saveIslandAppendix1Length), "SI1PC1");
_G(_saveIslandAppendix2) = getFileNamed(*sf, *extent, &_G(_saveIslandAppendix2Length), "SI1PC2");
} else {
_G(_saveIslandAppendix1) = getFileNamed(*sf, *extent, &_G(_saveIslandAppendix1Length), "SI2PIC");
}
}
delete[] *sf;
*sf = file;
*extent = length;
if (_G(_saveIslandAppendix1Length) > 2)
_G(_saveIslandAppendix1Length) -= 2;
if (_G(_saveIslandAppendix2Length) > 2)
_G(_saveIslandAppendix2Length) -= 2;
return decrunchC64(sf, extent, rec);
} else {
error("savageIslandMenu: Failed loading file %s\n", rec._appendFile);
}
}
void appendSIfiles(uint8_t **sf, size_t *extent) {
//int totalLength = *extent + _G(_saveIslandAppendix1Length) + _G(_saveIslandAppendix2Length);
uint8_t *megabuf = new uint8_t[0xFFFF];
memcpy(megabuf, *sf, *extent);
delete[] *sf;
int offset = 0x6202;
if (_G(_saveIslandAppendix1)) {
memcpy(megabuf + offset, _G(_saveIslandAppendix1) + 2, _G(_saveIslandAppendix1Length));
delete[] _G(_saveIslandAppendix1);
}
if (_G(_saveIslandAppendix2)) {
memcpy(megabuf + offset + _G(_saveIslandAppendix1Length), _G(_saveIslandAppendix2) + 2, _G(_saveIslandAppendix2Length));
delete[] _G(_saveIslandAppendix2);
}
*extent = offset + _G(_saveIslandAppendix1Length) + _G(_saveIslandAppendix2Length);
*sf = new uint8_t[*extent];
memcpy(*sf, megabuf, *extent);
delete[] megabuf;
}
int mysteriousMenu(uint8_t **sf, size_t *extent, int recindex) {
recindex = 0;
g_scott->output("This disk image contains six games. Select one.\n\n"
"1. The Golden Baton\n"
"2. The Time Machine\n"
"3. Arrow of Death part 1\n"
"4. Arrow of Death part 2\n"
"5. Escape from Pulsar 7\n"
"6. Circus");
g_scott->glk_request_char_event(_G(_bottomWindow));
event_t ev;
int result = 0;
do {
g_scott->glk_select(&ev);
if (ev.type == evtype_CharInput) {
if (ev.val1 >= '1' && ev.val1 <= '6') {
result = ev.val1 - '0';
} else {
g_scott->glk_request_char_event(_G(_bottomWindow));
}
}
if (g_vm->shouldQuit())
return 0;
} while (result == 0);
g_scott->glk_window_clear(_G(_bottomWindow));
const char *filename = nullptr;
switch (result) {
case 1:
filename = "BATON";
break;
case 2:
filename = "TIME MACHINE";
break;
case 3:
filename = "ARROW I";
break;
case 4:
filename = "ARROW II";
break;
case 5:
filename = "PULSAR 7";
break;
case 6:
filename = "CIRCUS";
break;
default:
error("mysteriousMenu: Unknown Game");
}
int length;
uint8_t *file = getFileNamed(*sf, *extent, &length, filename);
if (file != nullptr) {
delete[] *sf;
*sf = file;
*extent = length;
C64Rec rec = g_C64Registry[recindex - 1 + result];
return decrunchC64(sf, extent, rec);
} else {
error("mysteriousMenu: Failed loading file %s", filename);
return 0;
}
}
int mysteriousMenu2(uint8_t **sf, size_t *extent, int recindex) {
recindex = 6;
g_scott->output("This disk image contains five games. Select one.\n\n"
"1. Feasibility Experiment\n"
"2. The Wizard of Akyrz\n"
"3. Perseus and Andromeda\n"
"4. Ten Little Indians\n"
"5. Waxworks");
g_scott->glk_request_char_event(_G(_bottomWindow));
event_t ev;
int result = 0;
do {
g_scott->glk_select(&ev);
if (ev.type == evtype_CharInput) {
if (ev.val1 >= '1' && ev.val1 <= '5') {
result = ev.val1 - '0';
} else {
g_scott->glk_request_char_event(_G(_bottomWindow));
}
}
if (g_vm->shouldQuit())
return 0;
} while (result == 0);
g_scott->glk_window_clear(_G(_bottomWindow));
const char *filename = nullptr;
switch (result) {
case 1:
filename = "EXPERIMENT";
break;
case 2:
filename = "WIZARD OF AKYRZ";
break;
case 3:
filename = "PERSEUS";
break;
case 4:
filename = "INDIANS";
break;
case 5:
filename = "WAXWORKS";
break;
default:
error("mysteriousMenu2: Unknown Game");
}
int length;
uint8_t *file = getFileNamed(*sf, *extent, &length, filename);
if (file != nullptr) {
delete[] *sf;
*sf = file;
*extent = length;
C64Rec rec = g_C64Registry[recindex - 1 + result];
return decrunchC64(sf, extent, rec);
} else {
error("mysteriousMenu2: Failed loading file %s", filename);
}
}
int detectC64(uint8_t **sf, size_t *extent) {
if (*extent > MAX_LENGTH || *extent < MIN_LENGTH)
return 0;
Common::String md5 = g_vm->getGameMD5();
int index = _G(_md5Index)[md5];
if (g_C64Registry[index]._id == SAVAGE_ISLAND_C64) {
return savageIslandMenu(sf, extent, index);
} else if (g_C64Registry[index]._id == BATON_C64 && index == 0) {
return mysteriousMenu(sf, extent, index);
} else if (g_C64Registry[index]._id == FEASIBILITY_C64 && index == 6) {
return mysteriousMenu2(sf, extent, index);
}
if (g_C64Registry[index]._type == TYPE_D64) {
int newlength;
uint8_t *largest_file = getLargestFile(*sf, *extent, &newlength);
uint8_t *appendix = nullptr;
int appendixlen = 0;
if (g_C64Registry[index]._appendFile != nullptr) {
appendix = getFileNamed(*sf, *extent, &appendixlen, g_C64Registry[index]._appendFile);
if (appendix == nullptr)
error("detectC64(): Appending file failed");
appendixlen -= 2;
}
uint8_t *megabuf = new uint8_t[newlength + appendixlen];
memcpy(megabuf, largest_file, newlength);
if (appendix != nullptr) {
memcpy(megabuf + newlength + g_C64Registry[index]._parameter, appendix + 2, appendixlen);
newlength += appendixlen;
}
delete[] appendix;
if (largest_file) {
*sf = megabuf;
*extent = newlength;
}
delete[] largest_file;
} else if (g_C64Registry[index]._type == TYPE_T64) {
uint8_t *file_records = *sf + 64;
int number_of_records = READ_LE_UINT16(&(*sf)[36]);
int offset = READ_LE_UINT16(&file_records[8]);
int start_addr = READ_LE_UINT16(&file_records[2]);
int end_addr = READ_LE_UINT16(&file_records[4]);
int size;
if (number_of_records == 1)
size = *extent - offset;
else
size = end_addr - start_addr;
uint8_t *first_file = new uint8_t[size + 2];
memcpy(first_file + 2, *sf + offset, size);
memcpy(first_file, file_records + 2, 2);
*sf = first_file;
*extent = size + 2;
}
return decrunchC64(sf, extent, g_C64Registry[index]);
}
size_t copyData(size_t dest, size_t source, uint8_t** data, size_t dataSize, size_t bytesToMove) {
if (source > dataSize || *data == nullptr)
return 0;
size_t newSize = MAX(dest + bytesToMove, dataSize);
uint8_t *megaBuf = new uint8_t[newSize];
memcpy(megaBuf, *data, dataSize);
memcpy(megaBuf + dest, *data + source, bytesToMove);
delete[] *data;
*data = megaBuf;
return newSize;
}
int decrunchC64(uint8_t **sf, size_t *extent, C64Rec record) {
uint8_t *uncompressed = nullptr;
_G(_fileLength) = *extent;
size_t decompressedLength = *extent;
uncompressed = new uint8_t[0xffff];
size_t result = 0;
for (int i = 1; i <= record._decompressIterations; i++) {
/* We only send switches on the iteration specified by parameter */
if (i == record._parameter && record._switches != nullptr) {
result = unp64(_G(_entireFile), _G(_fileLength), uncompressed, &decompressedLength, record._switches);
} else
result = unp64(_G(_entireFile), _G(_fileLength), uncompressed, &decompressedLength, nullptr);
if (result) {
if (_G(_entireFile) != nullptr)
delete[] _G(_entireFile);
_G(_entireFile) = new uint8_t[decompressedLength];
memcpy(_G(_entireFile), uncompressed, decompressedLength);
_G(_fileLength) = decompressedLength;
} else {
delete[] uncompressed;
uncompressed = nullptr;
break;
}
}
if (uncompressed != nullptr)
delete[] uncompressed;
for (int i = 0; i < NUMGAMES; i++) {
if (_G(_games)[i]._gameID == record._id) {
_G(_game) = &_G(_games)[i];
break;
}
}
if (_G(_game)->_title == nullptr) {
error("decrunchC64: Game not found");
}
int offset;
DictionaryType dictype = getId(&offset);
if (dictype != _G(_game)->_dictionary) {
error("decrunchC64: Wrong game?");
}
if (!tryLoading(*_G(_game), offset, 0)) {
error("decrunchC64: Game could not be read");
}
if (_G(_saveIslandAppendix1) != nullptr) {
appendSIfiles(sf, extent);
}
if (record._copySource != 0) {
result = copyData(record._copyDest, record._copySource, sf, *extent, record._copySize);
if (result) {
*extent = result;
}
}
if (CURRENT_GAME == CLAYMORGUE_C64 && record._copySource == 0x855) {
result = copyData(0x1531a, 0x2002, sf, *extent, 0x2000);
if (result) {
*extent = result;
}
}
if (!(_G(_game)->_subType & MYSTERIOUS))
sagaSetup(record._imgOffset);
return CURRENT_GAME;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,46 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_C64CHECKSUMS_H
#define GLK_SCOTT_C64CHECKSUMS_H
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
int detectC64(uint8_t **sf, size_t *extent);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,872 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "common/str.h"
#include "glk/scott/scott.h"
#include "glk/scott/globals.h"
#include "glk/scott/command_parser.h"
namespace Glk {
namespace Scott {
#define MAX_WORDLENGTH 128
#define MAX_WORDS 128
#define MAX_BUFFER 128
void freeStrings() {
if (_G(_firstErrorMessage) != nullptr) {
delete[] _G(_firstErrorMessage);
_G(_firstErrorMessage) = nullptr;
}
if (_G(_wordsInInput) == 0) {
if (_G(_unicodeWords) != nullptr || _G(_charWords) != nullptr) {
g_scott->fatal("ERROR! Wordcount 0 but word arrays not empty!\n");
}
return;
}
for (int i = 0; i < _G(_wordsInInput); i++) {
if (_G(_unicodeWords)[i] != nullptr)
delete _G(_unicodeWords)[i];
if (_G(_charWords)[i] != nullptr)
delete _G(_charWords)[i];
}
delete _G(_unicodeWords);
_G(_unicodeWords) = nullptr;
delete _G(_charWords);
_G(_charWords) = nullptr;
_G(_wordsInInput) = 0;
}
void createErrorMessage(const char *fchar, glui32 *second, const char *tchar) {
if (_G(_firstErrorMessage) != nullptr)
return;
glui32 *first = toUnicode(fchar);
glui32 *third = toUnicode(tchar);
glui32 buffer[MAX_BUFFER];
int i, j = 0, k = 0;
for (i = 0; first[i] != 0 && i < MAX_BUFFER; i++)
buffer[i] = first[i];
if (second != nullptr) {
for (j = 0; second[j] != 0 && i + j < MAX_BUFFER; j++)
buffer[i + j] = second[j];
}
if (third != nullptr) {
for (k = 0; third[k] != 0 && i + j + k < MAX_BUFFER; k++)
buffer[i + j + k] = third[k];
delete[] third;
}
int length = i + j + k;
_G(_firstErrorMessage) = new glui32[(length + 1) * 4];
memcpy(_G(_firstErrorMessage), buffer, length * 4);
_G(_firstErrorMessage)[length] = 0;
delete[] first;
}
void printPendingError(void) {
if (_G(_firstErrorMessage)) {
g_scott->glk_put_string_stream_uni(g_scott->glk_window_get_stream(_G(_bottomWindow)), _G(_firstErrorMessage));
delete[] _G(_firstErrorMessage);
_G(_firstErrorMessage) = nullptr;
_G(_stopTime) = 1;
}
}
char **LineInput(void) {
event_t ev;
glui32 unibuf[512];
do {
g_scott->display(_G(_bottomWindow), "\n%s", _G(_sys)[WHAT_NOW].c_str());
g_scott->glk_request_line_event_uni(_G(_bottomWindow), unibuf, (glui32)511, 0);
while (ev.type != evtype_Quit) {
g_scott->glk_select(&ev);
if (ev.type == evtype_Quit)
return nullptr;
if (ev.type == evtype_LineInput)
break;
else
g_scott->updates(ev);
}
unibuf[ev.val1] = 0;
if (_G(_transcript)) {
g_scott->glk_put_string_stream_uni(_G(_transcript), unibuf);
g_scott->glk_put_char_stream_uni(_G(_transcript), 10);
}
_G(_charWords) = splitIntoWords(unibuf, ev.val1);
if (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr)
g_scott->output(_G(_sys)[HUH]);
else {
return _G(_charWords);
}
} while (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr);
return nullptr;
}
int matchYMCA(glui32 *string, int length, int index) {
const char *ymca = "y.m.c.a.";
int i;
for (i = 0; i < 8; i++) {
if (i + index > length || string[index + i] != static_cast<unsigned int>(ymca[i]))
return i;
}
return i;
}
char **splitIntoWords(glui32 *string, int length) {
if (length < 1) {
return nullptr;
}
g_scott->glk_buffer_to_lower_case_uni(string, 256, length);
g_scott->glk_buffer_canon_normalize_uni(string, 256, length);
int startpos[MAX_WORDS];
int wordlength[MAX_WORDS];
int words_found = 0;
int word_index = 0;
int foundspace = 0;
int foundcomma = 0;
startpos[0] = 0;
wordlength[0] = 0;
int lastwasspace = 1;
for (int i = 0; string[i] != 0 && i < length && word_index < MAX_WORDS; i++) {
foundspace = 0;
switch (string[i]) {
case 'y': {
int ymca = matchYMCA(string, length, i);
if (ymca > 3) {
/* Start a new word */
startpos[words_found] = i;
wordlength[words_found] = ymca;
words_found++;
wordlength[words_found] = 0;
i += ymca;
if (i < length)
foundspace = 1;
lastwasspace = 0;
}
} break;
/* Unicode space and tab variants */
case ' ':
case '\t':
case '!':
case '?':
case '\"':
case 0x83: // ¿
case 0x80: // ¡
case 0xa0: // non-breaking space
case 0x2000: // en quad
case 0x2001: // em quad
case 0x2003: // em
case 0x2004: // three-per-em
case 0x2005: // four-per-em
case 0x2006: // six-per-em
case 0x2007: // figure space
case 0x2009: // thin space
case 0x200A: // hair space
case 0x202f: // narrow no-break space
case 0x205f: // medium mathematical space
case 0x3000: // ideographic space
foundspace = 1;
break;
case '.':
case ',':
foundcomma = 1;
break;
default:
break;
}
if (!foundspace) {
if (lastwasspace || foundcomma) {
/* Start a new word */
startpos[words_found] = i;
words_found++;
wordlength[words_found] = 0;
}
wordlength[words_found - 1]++;
lastwasspace = 0;
} else {
/* Check if the last character of previous word was a period or comma */
lastwasspace = 1;
foundcomma = 0;
}
}
if (words_found == 0) {
return nullptr;
}
wordlength[words_found]--; /* Don't count final newline character */
/* Now we've created two arrays, one for starting positions
and one for word length. Now we convert these into an array of strings */
glui32 **words = new glui32 *[words_found];
char **words8 = new char *[words_found];
for (int i = 0; i < words_found; i++) {
words[i] = new glui32[(wordlength[i] + 1) * 4];
memcpy(words[i], string + startpos[i], wordlength[i] * 4);
words[i][wordlength[i]] = 0;
words8[i] = fromUnicode(words[i], wordlength[i]);
}
_G(_unicodeWords) = words;
_G(_wordsInInput) = words_found;
return words8;
}
int findVerb(const char *string, Common::StringArray *list) {
*list = _G(_verbs);
int verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
if (verb) {
return verb;
}
*list = _G(_directions);
verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
if (verb) {
if (verb == 13)
verb = 4;
if (verb > 6)
verb -= 6;
return verb;
}
*list = _G(_abbreviations);
verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
if (verb) {
verb = whichWord(_G(_abbreviationsKey)[verb].c_str(), _G(_verbs), _G(_gameHeader)->_wordLength);
if (verb) {
list = &_G(_verbs);
return verb;
}
}
int stringlength = strlen(string);
*list = _G(_skipList);
verb = whichWord(string, *list, stringlength);
if (verb) {
return 0;
}
*list = _G(_nouns);
verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
if (verb) {
return verb;
}
*list = _G(_extraCommands);
verb = whichWord(string, *list, stringlength);
if (verb) {
verb = _G(_extraCommandsKey)[verb];
return verb + _G(_gameHeader)->_numWords;
}
*list = _G(_extraNouns);
verb = whichWord(string, *list, stringlength);
if (verb) {
verb = _G(_extraNounsKey)[verb];
return verb + _G(_gameHeader)->_numWords;
}
*list = _G(_delimiterList);
verb = whichWord(string, *list, stringlength);
if (!verb)
*list = Common::StringArray();
return verb;
}
int findExtraneousWords(int *index, int noun) {
/* Looking for extraneous words that should invalidate the command */
int originalIndex = *index;
if (*index >= _G(_wordsInInput)) {
return 0;
}
Common::StringArray list;
int verb = 0;
int stringlength = strlen(_G(_charWords)[*index]);
list = _G(_skipList);
do {
verb = whichWord(_G(_charWords)[*index], _G(_skipList), stringlength);
if (verb)
*index = *index + 1;
} while (verb && *index < _G(_wordsInInput));
if (*index >= _G(_wordsInInput))
return 0;
verb = findVerb(_G(_charWords)[*index], &list);
if (list == _G(_delimiterList)) {
if (*index > originalIndex)
*index = *index - 1;
return 0;
}
if (list == _G(_nouns) && noun) {
if (g_scott->mapSynonym(noun) == g_scott->mapSynonym(verb)) {
*index = *index + 1;
return 0;
}
}
if (list.empty()) {
if (*index >= _G(_wordsInInput))
*index = _G(_wordsInInput) - 1;
createErrorMessage(_G(_sys)[I_DONT_KNOW_WHAT_A].c_str(), _G(_unicodeWords)[*index], _G(_sys)[IS].c_str());
} else {
createErrorMessage(_G(_sys)[I_DONT_UNDERSTAND].c_str(), nullptr, nullptr);
}
return 1;
}
Command *commandFromStrings(int index, Command *previous);
Command *createCommandStruct(int verb, int noun, int verbIndex, int nounIndex, Command *previous) {
Command *command = new Command;
command->_verb = verb;
command->_noun = noun;
command->_allFlag = 0;
command->_item = 0;
command->_previous = previous;
command->_verbWordIndex = verbIndex;
if (noun && nounIndex > 0) {
command->_nounWordIndex = nounIndex - 1;
} else {
command->_nounWordIndex = 0;
}
command->_next = commandFromStrings(nounIndex, command);
return command;
}
int findNoun(const char *string, Common::StringArray *list) {
*list = _G(_nouns);
int noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
if (noun) {
return noun;
}
*list = _G(_directions);
noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
if (noun) {
if (noun > 6)
noun -= 6;
*list = _G(_nouns);
return noun;
}
int stringLength = strlen(string);
*list = _G(_extraNouns);
noun = whichWord(string, *list, stringLength);
if (noun) {
noun = _G(_extraNounsKey)[noun];
return noun + _G(_gameHeader)->_numWords;
}
*list = _G(_skipList);
noun = whichWord(string, *list, stringLength);
if (noun) {
return 0;
}
*list = _G(_verbs);
noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
if (noun) {
return noun;
}
*list = _G(_delimiterList);
noun = whichWord(string, *list, stringLength);
if (!noun)
*list = Common::StringArray();
return 0;
}
Command *commandFromStrings(int index, Command *previous) {
if (index < 0 || index >= _G(_wordsInInput)) {
return nullptr;
}
Common::StringArray list;
int verb = 0;
int i = index;
do {
/* Checking if it is a verb */
verb = findVerb(_G(_charWords)[i++], &list);
} while ((list == _G(_skipList) || list == _G(_delimiterList)) && i < _G(_wordsInInput));
int verbindex = i - 1;
if (list == _G(_directions)) {
/* It is a direction */
if (verb == 0 || findExtraneousWords(&i, 0) != 0)
return nullptr;
return createCommandStruct(GO, verb, 0, i, previous);
}
int found_noun_at_verb_position = 0;
int lastverb = 0;
if (list == _G(_nouns) || list == _G(_extraNouns)) {
/* It is a noun */
/* If we find no verb, we try copying the verb from the previous command */
if (previous) {
lastverb = previous->_verb;
}
/* Unless the game is German, where we allow the noun to come before the
* verb */
if (CURRENT_GAME != GREMLINS_GERMAN && CURRENT_GAME != GREMLINS_GERMAN_C64) {
if (!previous) {
createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
return nullptr;
} else {
verbindex = previous->_verbWordIndex;
}
if (findExtraneousWords(&i, verb) != 0)
return nullptr;
return createCommandStruct(lastverb, verb, verbindex, i, previous);
} else {
found_noun_at_verb_position = 1;
}
}
if (list.empty() || list == _G(_skipList)) {
createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
return nullptr;
}
if (i == _G(_wordsInInput)) {
if (lastverb) {
return createCommandStruct(lastverb, verb, previous->_verbWordIndex, i, previous);
} else if (found_noun_at_verb_position) {
createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
return nullptr;
} else {
return createCommandStruct(verb, 0, i - 1, i, previous);
}
}
int noun = 0;
do {
/* Check if it is a noun */
noun = findNoun(_G(_charWords)[i++], &list);
} while (list == _G(_skipList) && i < _G(_wordsInInput));
if (list == _G(_nouns) || list == _G(_extraNouns)) {
/* It is a noun */
/* Check if it is an ALL followed by EXCEPT */
int except = 0;
if (list == _G(_extraNouns) && i < _G(_wordsInInput) && noun - _G(_gameHeader)->_numWords == ALL) {
int stringlength = strlen(_G(_charWords)[i]);
except = whichWord(_G(_charWords)[i], _G(_extraCommands), stringlength);
}
if (_G(_extraCommandsKey)[except] != EXCEPT && findExtraneousWords(&i, noun) != 0)
return nullptr;
if (found_noun_at_verb_position) {
int realverb = whichWord(_G(_charWords)[i - 1], _G(_verbs), _G(_gameHeader)->_wordLength);
if (realverb) {
noun = verb;
verb = realverb;
} else if (lastverb) {
noun = verb;
verb = lastverb;
}
}
return createCommandStruct(verb, noun, verbindex, i, previous);
}
if (list == _G(_delimiterList)) {
/* It is a delimiter */
return createCommandStruct(verb, 0, verbindex, i, previous);
}
if (list == _G(_verbs) && found_noun_at_verb_position) {
/* It is a verb */
/* Check if it is an ALL followed by EXCEPT */
int except = 0;
if (i < _G(_wordsInInput) && verb - _G(_gameHeader)->_numWords == ALL) {
int stringlength = strlen(_G(_charWords)[i]);
except = whichWord(_G(_charWords)[i], _G(_extraCommands), stringlength);
}
if (_G(_extraCommandsKey)[except] != EXCEPT && findExtraneousWords(&i, 0) != 0)
return nullptr;
return createCommandStruct(noun, verb, i - 1, i, previous);
}
createErrorMessage(_G(_sys)[I_DONT_KNOW_WHAT_A].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[IS].c_str());
return nullptr;
}
int createAllCommands(Command *command) {
Common::Array<int> exceptions(_G(_gameHeader)->_numItems);
int exceptioncount = 0;
int location = CARRIED;
if (command->_verb == TAKE)
location = MY_LOC;
Command *next = command->_next;
/* Check if the ALL command is followed by EXCEPT */
/* and if it is, build an array of items to be excepted */
while (next && next->_verb == _G(_gameHeader)->_numWords + EXCEPT) {
for (int i = 0; i <= _G(_gameHeader)->_numItems; i++) {
if (!_G(_items)[i]._autoGet.empty() && scumm_strnicmp(_G(_items)[i]._autoGet.c_str(), _G(_charWords)[next->_nounWordIndex], _G(_gameHeader)->_wordLength) == 0) {
exceptions[exceptioncount++] = i;
}
}
/* Remove the EXCEPT command from the linked list of commands */
next = next->_next;
delete command->_next;
command->_next = next;
}
Command *c = command;
int found = 0;
for (int i = 0; i < _G(_gameHeader)->_numItems; i++) {
if (!_G(_items)[i]._autoGet.empty() && _G(_items)[i]._autoGet[0] != '*' && _G(_items)[i]._location == location) {
int exception = 0;
for (int j = 0; j < exceptioncount; j++) {
if (exceptions[j] == i) {
exception = 1;
break;
}
}
if (!exception) {
if (found) {
c->_next = new Command;
c->_next->_previous = c;
c = c->_next;
}
found = 1;
c->_verb = command->_verb;
c->_noun = whichWord(_G(_items)[i]._autoGet.c_str(), _G(_nouns), _G(_gameHeader)->_wordLength);
c->_item = i;
c->_next = nullptr;
c->_nounWordIndex = 0;
c->_allFlag = 1;
}
}
}
if (found == 0) {
if (command->_verb == TAKE)
createErrorMessage(_G(_sys)[NOTHING_HERE_TO_TAKE].c_str(), nullptr, nullptr);
else
createErrorMessage(_G(_sys)[YOU_HAVE_NOTHING].c_str(), nullptr, nullptr);
return 0;
} else {
c->_next = next;
c->_allFlag = 1 | LASTALL;
}
return 1;
}
int getInput(int *vb, int *no) {
if (_G(_currentCommand) && _G(_currentCommand)->_next) {
_G(_currentCommand) = _G(_currentCommand)->_next;
} else {
printPendingError();
if (_G(_currentCommand))
freeCommands();
_G(_charWords) = LineInput();
if (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr)
return 0;
_G(_currentCommand) = commandFromStrings(0, nullptr);
}
if (_G(_currentCommand) == nullptr) {
printPendingError();
return 1;
}
/* We use NumWords + verb for our extra commands */
/* such as UNDO and TRANSCRIPT */
if (_G(_currentCommand)->_verb > _G(_gameHeader)->_numWords) {
if (!g_scott->performExtraCommand(0)) {
createErrorMessage(_G(_sys)[I_DONT_UNDERSTAND].c_str(), nullptr, nullptr);
}
return 1;
/* And NumWords + noun for our extra nouns */
/* such as ALL */
} else if (_G(_currentCommand)->_noun > _G(_gameHeader->_numWords)) {
_G(_currentCommand)->_noun -= _G(_gameHeader)->_numWords;
if (_G(_currentCommand)->_noun == ALL) {
if (_G(_currentCommand)->_verb != TAKE && _G(_currentCommand)->_verb != DROP) {
createErrorMessage(_G(_sys)[CANT_USE_ALL].c_str(), nullptr, nullptr);
return 1;
}
if (!createAllCommands(_G(_currentCommand)))
return 1;
} else if (_G(_currentCommand)->_noun == IT) {
_G(_currentCommand)->_noun = _G(_lastNoun);
}
}
*vb = _G(_currentCommand)->_verb;
*no = _G(_currentCommand)->_noun;
if (*no > 6) {
_G(_lastNoun) = *no;
}
return 0;
}
void freeCommands() {
while (_G(_currentCommand) && _G(_currentCommand)->_previous)
_G(_currentCommand) = _G(_currentCommand)->_previous;
while (_G(_currentCommand)) {
Command *temp = _G(_currentCommand);
_G(_currentCommand) = _G(_currentCommand)->_next;
delete temp;
}
_G(_currentCommand) = nullptr;
freeStrings();
if (_G(_firstErrorMessage))
delete[] _G(_firstErrorMessage);
_G(_firstErrorMessage) = nullptr;
}
glui32 *toUnicode(const char *string) {
if (string == nullptr)
return nullptr;
glui32 unicode[2048];
int i;
int dest = 0;
for (i = 0; string[i] != 0 && i < 2047; i++) {
char c = string[i];
if (c == '\n')
c = 10;
glui32 unichar = (glui32)c;
if (_G(_game) && (CURRENT_GAME == GREMLINS_GERMAN || CURRENT_GAME == GREMLINS_GERMAN_C64)) {
const char d = string[i + 1];
if (c == 'u' && d == 'e') { // ü
if (!(i > 2 && string[i - 1] == 'e')) {
unichar = 0xfc;
i++;
}
} else if (c == 'o' && d == 'e') {
unichar = 0xf6; // ö
i++;
} else if (c == 'a' && d == 'e') {
unichar = 0xe4; // ä
i++;
} else if (c == 's' && d == 's') {
if (string[i + 2] != 'c' && string[i - 2] != 'W' && !(string[i - 1] == 'a' && string[i - 2] == 'l') && string[i + 2] != '-' && string[i - 2] != 'b') {
unichar = 0xdf; // ß
i++;
}
} else if (c == 'U' && d == 'E') {
unichar = 0xdc; // Ü
i++;
}
if (c == '\"') {
unichar = 0x2019; //
}
} else if (_G(_game) && CURRENT_GAME == GREMLINS_SPANISH) {
switch (c) {
case '\x83':
unichar = 0xbf; // ¿
break;
case '\x80':
unichar = 0xa1; // ¡
break;
case '\x82':
unichar = 0xfc; // ü
break;
case '{':
unichar = 0xe1; // á
break;
case '}':
unichar = 0xed; // í
break;
case '|':
unichar = 0xf3; // ó
break;
case '~':
unichar = 0xf1; // ñ
break;
case '\x84':
unichar = 0xe9; // é
break;
case '\x85':
unichar = 0xfa; // ú
break;
}
} else if (_G(_game) && CURRENT_GAME == TI994A) {
switch (c) {
case '@':
unicode[dest++] = 0xa9;
unichar = ' ';
break;
case '}':
unichar = 0xfc;
break;
case 12:
unichar = 0xf6;
break;
case '{':
unichar = 0xe4;
break;
}
}
unicode[dest++] = unichar;
}
unicode[dest] = 0;
glui32 *result = new glui32[(dest + 1) * 4];
memcpy(result, unicode, (dest + 1) * 4);
return result;
}
char *fromUnicode(glui32 *unicodeString, int origLength) {
int sourcepos = 0;
int destpos = 0;
char dest[MAX_WORDLENGTH];
glui32 unichar = unicodeString[sourcepos];
while (unichar != 0 && destpos < MAX_WORDLENGTH && sourcepos < origLength) {
switch (unichar) {
case '.':
if (origLength == 1) {
dest[destpos++] = 'a';
dest[destpos++] = 'n';
dest[destpos++] = 'd';
} else {
dest[destpos] = (char)unichar;
}
break;
case 0xf6: // ö
dest[destpos++] = 'o';
dest[destpos] = 'e';
break;
case 0xe4: // ä
dest[destpos++] = 'a';
dest[destpos] = 'e';
break;
case 0xfc: // ü
dest[destpos] = 'u';
if (CURRENT_GAME == GREMLINS_GERMAN || CURRENT_GAME == GREMLINS_GERMAN_C64) {
destpos++;
dest[destpos] = 'e';
}
break;
case 0xdf: // ß
dest[destpos++] = 's';
dest[destpos] = 's';
break;
case 0xed: // í
dest[destpos] = 'i';
break;
case 0xe1: // á
dest[destpos] = 'a';
break;
case 0xf3: // ó
dest[destpos] = 'o';
break;
case 0xf1: // ñ
dest[destpos] = 'n';
break;
case 0xe9: // é
dest[destpos] = 'e';
break;
default:
dest[destpos] = (char)unichar;
break;
}
sourcepos++;
destpos++;
unichar = unicodeString[sourcepos];
}
if (destpos == 0)
return nullptr;
char *result = new char[destpos + 1];
memcpy(result, dest, destpos);
result[destpos] = 0;
return result;
}
int recheckForExtraCommand() {
const char *verbWord = _G(_charWords)[_G(_currentCommand)->_verbWordIndex];
int extraVerb = whichWord(verbWord, _G(_extraCommands), _G(_gameHeader)->_wordLength);
if (!extraVerb) {
return 0;
}
int ExtraNoun = 0;
if (_G(_currentCommand)->_noun) {
const char *nounWord = _G(_charWords)[_G(_currentCommand)->_nounWordIndex];
ExtraNoun = whichWord(nounWord, _G(_extraNouns), strlen(nounWord));
}
_G(_currentCommand)->_verb = _G(_extraCommandsKey)[extraVerb];
if (ExtraNoun)
_G(_currentCommand)->_noun = _G(_extraNounsKey)[ExtraNoun];
return g_scott->performExtraCommand(1);
}
int whichWord(const char *word, Common::StringArray list, int wordLength) {
int n = 1;
unsigned int ne = 1;
const char *tp;
while (ne < list.size()) {
tp = list[ne].c_str();
if (*tp == '*')
tp++;
else
n = ne;
if (scumm_strnicmp(word, tp, wordLength) == 0)
return (n);
ne++;
}
return (0);
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,89 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_PARSER_H
#define GLK_SCOTT_PARSER_H
#include "common/str-array.h"
#include "glk/glk_types.h"
namespace Glk {
namespace Scott {
#define NUMBER_OF_DIRECTIONS 14
#define NUMBER_OF_SKIPPABLE_WORDS 18
#define NUMBER_OF_DELIMITERS 5
#define NUMBER_OF_EXTRA_COMMANDS 20
#define NUMBER_OF_EXTRA_NOUNS 16
struct Command {
int _verb;
int _noun;
int _item;
int _verbWordIndex;
int _nounWordIndex;
int _allFlag;
struct Command *_previous;
struct Command *_next;
};
enum ExtraCommand : int {
NO_COMMAND,
RESTART,
SAVE,
RESTORE,
SCRIPT,
ON,
OFF,
UNDO,
RAM,
RAMSAVE,
RAMLOAD,
GAME,
COMMAND,
ALL,
IT,
EXCEPT
};
char **splitIntoWords(glui32 *string, int length);
int getInput(int *vb, int *no);
void freeCommands();
glui32 *toUnicode(const char *string);
char *fromUnicode(glui32 *unicodeString, int origLength);
int recheckForExtraCommand();
int whichWord(const char *word, Common::StringArray list, int wordLength);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,123 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "common/str.h"
#include "glk/scott/decompress_text.h"
namespace Glk {
namespace Scott {
int rotateLeftWithCarry(uint8_t *byte, int lastCarry) {
int carry = ((*byte & 0x80) > 0);
*byte = *byte << 1;
if (lastCarry)
*byte = *byte | 0x01;
return carry;
}
int decompressOne(uint8_t *bytes) {
uint8_t result = 0;
int carry;
for (int i = 0; i < 5; i++) {
carry = 0;
for (int j = 0; j < 5; j++) {
carry = rotateLeftWithCarry(bytes + 4 - j, carry);
}
rotateLeftWithCarry(&result, carry);
}
return result;
}
char *decompressText(uint8_t *source, int stringIndex) {
// Lookup table
const char *alphabet = " abcdefghijklmnopqrstuvwxyz'\x01,.\x00";
//Common::String alphabet = " abcdefghijklmnopqrstuvwxyz'\x01,.\x00";
int pos, c, uppercase, i, j;
uint8_t decompressed[256];
uint8_t buffer[5];
int idx = 0;
// Find the start of the compressed message
for (i = 0; i < stringIndex; i++) {
pos = *source;
pos = pos & 0x7F;
source += pos;
};
uppercase = ((*source & 0x40) == 0); // Test bit 6
source++;
do {
// Get five compressed bytes
for (i = 0; i < 5; i++) {
buffer[i] = *source++;
}
for (j = 0; j < 8; j++) {
// Decompress one character:
int next = decompressOne(buffer);
c = alphabet[next];
if (c == 0x01) {
uppercase = 1;
c = ' ';
}
if (c >= 'a' && uppercase) {
c = toupper(c);
uppercase = 0;
}
decompressed[idx++] = c;
if (idx > 255)
return nullptr;
if (idx == 255)
c = 0; // We've gone too far, return
if (c == 0) {
char *result = new char[idx];
memcpy(result, decompressed, idx);
return result;
} else if (c == '.' || c == ',') {
if (c == '.')
uppercase = 1;
decompressed[idx++] = ' ';
}
}
} while (idx < 0xff); // Chosen arbitrarily, might be too small
return nullptr;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,46 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_DECOMPRESSTEXT_H
#define GLK_SCOTT_DECOMPRESSTEXT_H
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
char *decompressText(uint8_t *source, int stringIndex);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,915 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "common/algorithm.h"
#include "common/array.h"
#include "common/textconsole.h"
#include "glk/scott/decompress_z80.h"
namespace Glk {
namespace Scott {
/* Sizes of some of the arrays in the snap structure */
const int SNAPSHOT_RAM_PAGES = 16;
//const int SNAPSHOT_SLT_PAGES = 256;
//const int SNAPSHOT_ZXATASP_PAGES = 32;
//const int SNAPSHOT_ZXCF_PAGES = 64;
//const int SNAPSHOT_DOCK_EXROM_PAGES = 8;
//const int SNAPSHOT_JOYSTICKS = 7;
//const int SNAPSHOT_DIVIDE_PAGES = 4;
void *libspectrumReallocN(void *ptr, size_t nmemb, size_t size) {
if (nmemb > SIZE_MAX / size)
error("libspectrumReallocN: Can't reallocate to required size");
return realloc(ptr, nmemb * size);
}
/* Ensure there is room for `requested' characters after the current
position `ptr' in `buffer'. If not, renew() and update the
pointers as necessary */
void libspectrumMakeRoom(uint8_t **dest, size_t requested, uint8_t **ptr,
size_t *allocated) {
size_t current_length = 0;
if (*allocated == 0) {
(*allocated) = requested;
*dest = new uint8_t[requested];
} else {
current_length = *ptr - *dest;
/* If there's already enough room here, just return */
if (current_length + requested <= (*allocated))
return;
/* Make the new size the maximum of the new needed size and the
old allocated size * 2 */
(*allocated) = current_length + requested > 2 * (*allocated)
? current_length + requested
: 2 * (*allocated);
*dest = static_cast<uint8_t *>(libspectrumReallocN(*dest, *allocated, sizeof(uint8_t)));
}
/* Update the secondary pointer to the block */
*ptr = *dest + current_length;
}
struct LibspectrumSnap {
LibspectrumSnap() : pages(SNAPSHOT_RAM_PAGES) {}
/* Which machine are we using here? */
int machine;
/* Registers and the like */
uint16_t pc;
Common::Array<uint8_t *> pages;
};
/* Error handling */
/* The various errors which can occur */
enum LibspectrumError {
LIBSPECTRUM_ERROR_NONE = 0,
LIBSPECTRUM_ERROR_WARNING,
LIBSPECTRUM_ERROR_MEMORY,
LIBSPECTRUM_ERROR_UNKNOWN,
LIBSPECTRUM_ERROR_CORRUPT,
LIBSPECTRUM_ERROR_SIGNATURE,
LIBSPECTRUM_ERROR_SLT, /* .slt data found at end of a .z80 file */
LIBSPECTRUM_ERROR_INVALID, /* Invalid parameter supplied */
LIBSPECTRUM_ERROR_LOGIC = -1,
};
enum LibspectrumMachine {
LIBSPECTRUM_MACHINE_48,
LIBSPECTRUM_MACHINE_TC2048,
LIBSPECTRUM_MACHINE_128,
LIBSPECTRUM_MACHINE_PLUS2,
LIBSPECTRUM_MACHINE_PENT,
LIBSPECTRUM_MACHINE_PLUS2A,
LIBSPECTRUM_MACHINE_PLUS3,
/* Used by libspectrum_tape_guess_hardware if we can't work out what
hardware should be used */
LIBSPECTRUM_MACHINE_UNKNOWN,
LIBSPECTRUM_MACHINE_16,
LIBSPECTRUM_MACHINE_TC2068,
LIBSPECTRUM_MACHINE_SCORP,
LIBSPECTRUM_MACHINE_PLUS3E,
LIBSPECTRUM_MACHINE_SE,
LIBSPECTRUM_MACHINE_TS2068,
LIBSPECTRUM_MACHINE_PENT512,
LIBSPECTRUM_MACHINE_PENT1024,
LIBSPECTRUM_MACHINE_48_NTSC,
LIBSPECTRUM_MACHINE_128E,
};
enum LibspectrumMachineCapability {
LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY = (1u << 0),
LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY = (1u << 1),
LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY = (1u << 2),
LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY = (1u << 3),
LIBSPECTRUM_MACHINE_CAPABILITY_SE_MEMORY = (1u << 4),
LIBSPECTRUM_MACHINE_CAPABILITY_PENT512_MEMORY = (1u << 5),
LIBSPECTRUM_MACHINE_CAPABILITY_PENT1024_MEMORY = (1u << 6),
};
/* Given a machine type, what features does it have? */
int libspectrumMachineCapabilities(LibspectrumMachine type) {
int capabilities = 0;
/* 128K Spectrum-style 0x7ffd memory paging */
switch (type) {
case LIBSPECTRUM_MACHINE_128:
case LIBSPECTRUM_MACHINE_PLUS2:
case LIBSPECTRUM_MACHINE_PLUS2A:
case LIBSPECTRUM_MACHINE_PLUS3:
case LIBSPECTRUM_MACHINE_PLUS3E:
case LIBSPECTRUM_MACHINE_128E:
case LIBSPECTRUM_MACHINE_PENT:
case LIBSPECTRUM_MACHINE_PENT512:
case LIBSPECTRUM_MACHINE_PENT1024:
case LIBSPECTRUM_MACHINE_SCORP:
/* FIXME: SE needs to have this capability to be considered a 128k machine
*/
case LIBSPECTRUM_MACHINE_SE:
capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY;
break;
default:
break;
}
/* +3 Spectrum-style 0x1ffd memory paging */
switch (type) {
case LIBSPECTRUM_MACHINE_PLUS2A:
case LIBSPECTRUM_MACHINE_PLUS3:
case LIBSPECTRUM_MACHINE_PLUS3E:
case LIBSPECTRUM_MACHINE_128E:
capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY;
break;
default:
break;
}
/* T[CS]20[46]8-style 0x00fd memory paging */
switch (type) {
case LIBSPECTRUM_MACHINE_TC2048:
case LIBSPECTRUM_MACHINE_TC2068:
case LIBSPECTRUM_MACHINE_TS2068:
capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY;
break;
default:
break;
}
/* Scorpion-style 0x1ffd memory paging */
switch (type) {
case LIBSPECTRUM_MACHINE_SCORP:
capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY;
break;
default:
break;
}
/* SE-style 0x7ffd and 0x00fd memory paging */
switch (type) {
case LIBSPECTRUM_MACHINE_SE:
capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_SE_MEMORY;
break;
default:
break;
}
/* Pentagon 512-style memory paging */
switch (type) {
case LIBSPECTRUM_MACHINE_PENT512:
case LIBSPECTRUM_MACHINE_PENT1024:
capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_PENT512_MEMORY;
break;
default:
break;
}
/* Pentagon 1024-style memory paging */
switch (type) {
case LIBSPECTRUM_MACHINE_PENT1024:
capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_PENT1024_MEMORY;
break;
default:
break;
}
return capabilities;
}
/* Length of the basic .z80 headers */
static const int LIBSPECTRUM_Z80_HEADER_LENGTH = 30;
/* Length of the v2 extensions */
#define LIBSPECTRUM_Z80_V2_LENGTH 23
/* Length of the v3 extensions */
#define LIBSPECTRUM_Z80_V3_LENGTH 54
/* Length of xzx's extensions */
#define LIBSPECTRUM_Z80_V3X_LENGTH 55
/* The constants used for each machine type */
enum {
/* v2 constants */
Z80_MACHINE_48_V2 = 0,
Z80_MACHINE_48_IF1_V2 = 1,
Z80_MACHINE_48_SAMRAM_V2 = 2,
Z80_MACHINE_128_V2 = 3,
Z80_MACHINE_128_IF1_V2 = 4,
/* v3 constants */
Z80_MACHINE_48 = 0,
Z80_MACHINE_48_IF1 = 1,
Z80_MACHINE_48_SAMRAM = 2,
Z80_MACHINE_48_MGT = 3,
Z80_MACHINE_128 = 4,
Z80_MACHINE_128_IF1 = 5,
Z80_MACHINE_128_MGT = 6,
/* Extensions */
Z80_MACHINE_PLUS3 = 7,
Z80_MACHINE_PLUS3_XZX_ERROR = 8,
Z80_MACHINE_PENTAGON = 9,
Z80_MACHINE_SCORPION = 10,
Z80_MACHINE_PLUS2 = 12,
Z80_MACHINE_PLUS2A = 13,
Z80_MACHINE_TC2048 = 14,
Z80_MACHINE_TC2068 = 15,
Z80_MACHINE_TS2068 = 128,
/* The first extension ID; anything here or greater applies to both
v2 and v3 files */
Z80_MACHINE_FIRST_EXTENSION = Z80_MACHINE_PLUS3,
};
static LibspectrumError readHeader(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **data, int *version, int *compressed);
static LibspectrumError getMachineType(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type, int version);
static LibspectrumError getMachineTypeV2(LibspectrumSnap *snap, uint8_t type);
static LibspectrumError getMachineTypeV3(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type);
static LibspectrumError getMachineTypeExtension(LibspectrumSnap *snap, uint8_t type);
static LibspectrumError readBlocks(const uint8_t *buffer, size_t buffer_length, LibspectrumSnap *snap, int version, int compressed);
static LibspectrumError readBlock(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **next_block, const uint8_t *end, int version, int compressed);
static LibspectrumError readV1Block(const uint8_t *buffer, int is_compressed, uint8_t **uncompressed, const uint8_t **next_block, const uint8_t *end);
static LibspectrumError readV2Block(const uint8_t *buffer, uint8_t **block, size_t *length, int *page, const uint8_t **next_block, const uint8_t *end);
static void uncompressBlock(uint8_t **dest, size_t *dest_length, const uint8_t *src, size_t src_length);
void libspectrumSnapSetMachine(LibspectrumSnap *snap, int val) {
snap->machine = val;
}
uint8_t *libspectrumSnapPages(LibspectrumSnap *snap, int page) {
return snap->pages[page];
}
void libspectrumSnapSetPages(LibspectrumSnap *snap, int page, uint8_t *buf) {
snap->pages[page] = buf;
}
void libspectrumPrintError(LibspectrumError error) {
switch (error) {
case LIBSPECTRUM_ERROR_WARNING:
warning("warning");
break;
case LIBSPECTRUM_ERROR_MEMORY:
warning("memory error");
break;
case LIBSPECTRUM_ERROR_UNKNOWN:
warning("unknown error");
break;
case LIBSPECTRUM_ERROR_CORRUPT:
warning("corruption error");
break;
case LIBSPECTRUM_ERROR_SIGNATURE:
warning("signature error");
break;
case LIBSPECTRUM_ERROR_SLT:
warning("SLT data in Z80 error");
break;
case LIBSPECTRUM_ERROR_INVALID:
warning("invalid parameter error");
break;
case LIBSPECTRUM_ERROR_LOGIC:
warning("logic error");
break;
default:
warning("unhandled error");
break;
}
}
int libspectrumSnapMachine(LibspectrumSnap *snap) {
return snap->machine;
}
/* Read an LSB dword from buffer */
uint32_t libspectrumReadDword(const uint8_t **buffer) {
uint32_t value;
value = (*buffer)[0] + (*buffer)[1] * 0x100 + (*buffer)[2] * 0x10000 + (*buffer)[3] * 0x1000000;
(*buffer) += 4;
return value;
}
static LibspectrumError getMachineTypeExtension(LibspectrumSnap *snap, uint8_t type) {
switch (type) {
case Z80_MACHINE_PLUS3:
case Z80_MACHINE_PLUS3_XZX_ERROR:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS3);
break;
case Z80_MACHINE_PENTAGON:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PENT);
break;
case Z80_MACHINE_SCORPION:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_SCORP);
break;
case Z80_MACHINE_PLUS2:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2);
break;
case Z80_MACHINE_PLUS2A:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2A);
break;
case Z80_MACHINE_TC2048:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_TC2048);
break;
case Z80_MACHINE_TC2068:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_TC2068);
break;
case Z80_MACHINE_TS2068:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_TS2068);
break;
default:
libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
warning("%s:get_machine_type: unknown extension machine type %d", __FILE__, type);
return LIBSPECTRUM_ERROR_UNKNOWN;
}
return LIBSPECTRUM_ERROR_NONE;
}
LibspectrumError internalZ80Read(LibspectrumSnap *snap, const uint8_t *buffer, size_t buffer_length);
uint8_t *decompressZ80(uint8_t *raw_data, size_t length) {
LibspectrumSnap *snap = new LibspectrumSnap;
for (int i = 0; i < SNAPSHOT_RAM_PAGES; i++)
libspectrumSnapSetPages(snap, i, nullptr);
if (internalZ80Read(snap, raw_data, length) != LIBSPECTRUM_ERROR_NONE) {
return nullptr;
}
uint8_t *uncompressed = new uint8_t[0xC000];
if (uncompressed == nullptr)
return nullptr;
Common::copy(snap->pages[5], snap->pages[5] + 0x4000, uncompressed);
Common::copy(snap->pages[2], snap->pages[2] + 0x4000, uncompressed + 0x4000);
Common::copy(snap->pages[0], snap->pages[0] + 0x4000, uncompressed + 0x8000);
for (int i = 0; i < SNAPSHOT_RAM_PAGES; i++)
if (snap->pages[i] != nullptr)
delete snap->pages[i];
delete snap;
return uncompressed;
}
LibspectrumError internalZ80Read(LibspectrumSnap *snap, const uint8_t *buffer, size_t buffer_length) {
LibspectrumError error;
const uint8_t *data;
int version, compressed = 1;
error = readHeader(buffer, snap, &data, &version, &compressed);
if (error != LIBSPECTRUM_ERROR_NONE)
return error;
error = readBlocks(data, buffer_length - (data - buffer), snap, version,
compressed);
if (error != LIBSPECTRUM_ERROR_NONE)
return error;
return LIBSPECTRUM_ERROR_NONE;
}
static LibspectrumError readHeader(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **data, int *version, int *compressed) {
const uint8_t *header = buffer;
LibspectrumError error;
uint8_t header_6 = header[6];
uint8_t header_7 = header[7] * 0x100;
snap->pc = header_6 + header_7;
if (snap->pc == 0) { /* PC == 0x0000 => v2 or greater */
size_t extra_length;
const uint8_t *extra_header;
extra_length = header[LIBSPECTRUM_Z80_HEADER_LENGTH] + header[LIBSPECTRUM_Z80_HEADER_LENGTH + 1] * 0x100;
switch (extra_length) {
case LIBSPECTRUM_Z80_V2_LENGTH:
*version = 2;
break;
case LIBSPECTRUM_Z80_V3_LENGTH:
case LIBSPECTRUM_Z80_V3X_LENGTH:
*version = 3;
break;
default:
libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
warning("libspectrumReadZ80Header: unknown header length %d", (int)extra_length);
return LIBSPECTRUM_ERROR_UNKNOWN;
}
extra_header = buffer + LIBSPECTRUM_Z80_HEADER_LENGTH + 2;
snap->pc = extra_header[0] + extra_header[1] * 0x100;
error = getMachineType(snap, extra_header[2], extra_header[51], *version);
if (error)
return error;
if (extra_header[5] & 0x80) {
switch (libspectrumSnapMachine(snap)) {
case LIBSPECTRUM_MACHINE_48:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_16);
break;
case LIBSPECTRUM_MACHINE_128:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2);
break;
case LIBSPECTRUM_MACHINE_PLUS3:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2A);
break;
default:
break; /* Do nothing */
}
}
(*data) = buffer + LIBSPECTRUM_Z80_HEADER_LENGTH + 2 + extra_length;
} else { /* v1 .z80 file */
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
*version = 1;
/* Need to flag this for later */
*compressed = (header[12] & 0x20) ? 1 : 0;
(*data) = buffer + LIBSPECTRUM_Z80_HEADER_LENGTH;
}
return LIBSPECTRUM_ERROR_NONE;
}
static LibspectrumError getMachineType(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type, int version) {
LibspectrumError error;
if (type < Z80_MACHINE_FIRST_EXTENSION) {
switch (version) {
case 2:
error = getMachineTypeV2(snap, type);
if (error)
return error;
break;
case 3:
error = getMachineTypeV3(snap, type, mgt_type);
if (error)
return error;
break;
default:
libspectrumPrintError(LIBSPECTRUM_ERROR_LOGIC);
warning("%s:getMachineType: unknown version %d", __FILE__, version);
return LIBSPECTRUM_ERROR_LOGIC;
}
} else {
error = getMachineTypeExtension(snap, type);
if (error)
return error;
}
return LIBSPECTRUM_ERROR_NONE;
}
static LibspectrumError getMachineTypeV2(LibspectrumSnap *snap, uint8_t type) {
switch (type) {
case Z80_MACHINE_48_V2:
case Z80_MACHINE_48_SAMRAM_V2:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
break;
case Z80_MACHINE_48_IF1_V2:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
break;
case Z80_MACHINE_128_V2:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
break;
case Z80_MACHINE_128_IF1_V2:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
break;
default:
libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
warning("%s: getMachineType: unknown v2 machine type %d", __FILE__, type);
return LIBSPECTRUM_ERROR_UNKNOWN;
}
return LIBSPECTRUM_ERROR_NONE;
}
static LibspectrumError getMachineTypeV3(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type) {
switch (type) {
case Z80_MACHINE_48:
case Z80_MACHINE_48_SAMRAM:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
break;
case Z80_MACHINE_48_IF1:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
break;
case Z80_MACHINE_48_MGT:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
break;
case Z80_MACHINE_128:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
break;
case Z80_MACHINE_128_MGT:
libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
break;
default:
libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
warning("%s:getMachineType: unknown v3 machine type %d", __FILE__, type);
return LIBSPECTRUM_ERROR_UNKNOWN;
}
return LIBSPECTRUM_ERROR_NONE;
}
/* Given a 48K memory dump `data', place it into the
appropriate bits of `snap' for a 48K machine */
LibspectrumError libspectrumSplitTo48kPages(LibspectrumSnap *snap, const uint8_t *data) {
uint8_t *buffer[3];
size_t i;
/* If any of the three pages are already occupied, barf */
if (libspectrumSnapPages(snap, 5) || libspectrumSnapPages(snap, 2) || libspectrumSnapPages(snap, 0)) {
libspectrumPrintError(LIBSPECTRUM_ERROR_LOGIC);
warning("libspectrumSplitTo48kPages: RAM page already in use");
return LIBSPECTRUM_ERROR_LOGIC;
}
for (i = 0; i < 3; i++) {
buffer[i] = new uint8_t[0x4000];
}
libspectrumSnapSetPages(snap, 5, buffer[0]);
libspectrumSnapSetPages(snap, 2, buffer[1]);
libspectrumSnapSetPages(snap, 0, buffer[2]);
/* Finally, do the copies... */
Common::copy(&data[0x0000], &data[0x0000] + 0x4000, libspectrumSnapPages(snap, 5));
Common::copy(&data[0x4000], &data[0x4000] + 0x4000, libspectrumSnapPages(snap, 2));
Common::copy(&data[0x8000], &data[0x8000] + 0x4000, libspectrumSnapPages(snap, 0));
return LIBSPECTRUM_ERROR_NONE;
}
static LibspectrumError readBlocks(const uint8_t *buffer, size_t buffer_length, LibspectrumSnap *snap, int version, int compressed) {
const uint8_t *end, *next_block;
end = buffer + buffer_length;
next_block = buffer;
while (next_block < end) {
LibspectrumError error;
error = readBlock(next_block, snap, &next_block, end, version, compressed);
if (error != LIBSPECTRUM_ERROR_NONE)
return error;
}
return LIBSPECTRUM_ERROR_NONE;
}
static LibspectrumError readBlock(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **next_block, const uint8_t *end, int version, int compressed) {
LibspectrumError error;
uint8_t *uncompressed;
int capabilities = libspectrumMachineCapabilities(static_cast<LibspectrumMachine>(libspectrumSnapMachine(snap)));
if (version == 1) {
error = readV1Block(buffer, compressed, &uncompressed, next_block, end);
if (error != LIBSPECTRUM_ERROR_NONE)
return error;
libspectrumSplitTo48kPages(snap, uncompressed);
delete[] uncompressed;
} else {
size_t length;
int page;
error = readV2Block(buffer, &uncompressed, &length, &page, next_block, end);
if (error != LIBSPECTRUM_ERROR_NONE)
return error;
if (page <= 0 || page > 18) {
libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
warning("readBlock: unknown page %d", page);
delete[] uncompressed;
return LIBSPECTRUM_ERROR_UNKNOWN;
}
/* If it's a ROM page, just throw it away */
if (page < 3) {
delete[] uncompressed;
return LIBSPECTRUM_ERROR_NONE;
}
/* Page 11 is the Multiface ROM unless we're emulating something
Scorpion-like */
if (page == 11 && !(capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY)) {
delete[] uncompressed;
return LIBSPECTRUM_ERROR_NONE;
}
/* Deal with 48K snaps -- first, throw away page 3, as it's a ROM.
Then remap the numbers slightly */
if (!(capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY)) {
switch (page) {
case 3:
delete[] uncompressed;
return LIBSPECTRUM_ERROR_NONE;
case 4:
page = 5;
break;
case 5:
page = 3;
break;
}
}
/* Now map onto RAM page numbers */
page -= 3;
if (libspectrumSnapPages(snap, page) == nullptr) {
libspectrumSnapSetPages(snap, page, uncompressed);
} else {
delete[] uncompressed;
libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
warning("readBlock: page %d duplicated", page);
return LIBSPECTRUM_ERROR_CORRUPT;
}
}
return LIBSPECTRUM_ERROR_NONE;
}
static LibspectrumError readV1Block(const uint8_t *buffer, int is_compressed, uint8_t **uncompressed, const uint8_t **next_block, const uint8_t *end) {
if (is_compressed) {
const uint8_t *ptr;
int state;
size_t uncompressed_length = 0;
state = 0;
ptr = buffer;
while (state != 4) {
if (ptr == end) {
libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
warning("readV1Block: end marker not found");
return LIBSPECTRUM_ERROR_CORRUPT;
}
switch (state) {
case 0:
switch (*ptr++) {
case 0x00:
state = 1;
break;
default:
state = 0;
break;
}
break;
case 1:
switch (*ptr++) {
case 0x00:
state = 1;
break;
case 0xed:
state = 2;
break;
default:
state = 0;
break;
}
break;
case 2:
switch (*ptr++) {
case 0x00:
state = 1;
break;
case 0xed:
state = 3;
break;
default:
state = 0;
break;
}
break;
case 3:
switch (*ptr++) {
case 0x00:
state = 4;
break;
default:
state = 0;
break;
}
break;
default:
libspectrumPrintError(LIBSPECTRUM_ERROR_LOGIC);
warning("readV1Block: unknown state %d", state);
return LIBSPECTRUM_ERROR_LOGIC;
}
}
/* Length passed here is reduced by 4 to remove the end marker */
uncompressBlock(uncompressed, &uncompressed_length, buffer, (ptr - buffer - 4));
/* Uncompressed data must be exactly 48Kb long */
if (uncompressed_length != 0xc000) {
delete *uncompressed;
libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
warning("readV1Block: data does not uncompress to 48Kb");
return LIBSPECTRUM_ERROR_CORRUPT;
}
*next_block = ptr;
} else { /* Snap isn't compressed */
/* Check we've got enough bytes to read */
if (end - *next_block < 0xc000) {
libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
warning("readV1Block: not enough data in buffer");
return LIBSPECTRUM_ERROR_CORRUPT;
}
*uncompressed = new uint8_t[0xC000];
Common::copy(buffer, buffer + 0xC000, *uncompressed);
*next_block = buffer + 0xc000;
}
return LIBSPECTRUM_ERROR_NONE;
}
/* The signature used to designate the .slt extensions */
static uint8_t slt_signature[] = "\0\0\0SLT";
static size_t slt_signature_length = 6;
static LibspectrumError readV2Block(const uint8_t *buffer, uint8_t **block, size_t *length, int *page, const uint8_t **next_block, const uint8_t *end) {
size_t length2;
length2 = buffer[0] + buffer[1] * 0x100;
(*page) = buffer[2];
if (length2 == 0 && *page == 0) {
if (buffer + 8 < end && !memcmp(buffer, slt_signature, slt_signature_length)) {
/* Ah, we have what looks like SLT data... */
*next_block = buffer + 6;
return LIBSPECTRUM_ERROR_SLT;
}
}
/* A length of 0xffff => 16384 bytes of uncompressed data */
if (length2 != 0xffff) {
/* Check we're not going to run over the end of the buffer */
if (buffer + 3 + length2 > end) {
libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
warning("readV2Block: not enough data in buffer");
return LIBSPECTRUM_ERROR_CORRUPT;
}
*length = 0;
uncompressBlock(block, length, buffer + 3, length2);
*next_block = buffer + 3 + length2;
} else { /* Uncompressed block */
/* Check we're not going to run over the end of the buffer */
if (buffer + 3 + 0x4000 > end) {
libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
warning("readV2Block: not enough data in buffer");
return LIBSPECTRUM_ERROR_CORRUPT;
}
*block = new uint8_t[0x4000];
Common::copy(buffer + 3, buffer + 3 + 0x4000, *block);
*length = 0x4000;
*next_block = buffer + 3 + 0x4000;
}
return LIBSPECTRUM_ERROR_NONE;
}
static void uncompressBlock(uint8_t **dest, size_t *dest_length, const uint8_t *src, size_t src_length) {
const uint8_t *in_ptr;
uint8_t *out_ptr;
/* Allocate memory for dest if requested */
if (*dest_length == 0) {
*dest_length = src_length / 2;
*dest = new uint8_t[*dest_length];
}
in_ptr = src;
out_ptr = *dest;
while (in_ptr < src + src_length) {
/* If we're pointing at the last byte, just copy it across and exit */
if (in_ptr == src + src_length - 1) {
libspectrumMakeRoom(dest, 1, &out_ptr, dest_length);
*out_ptr++ = *in_ptr++;
continue;
}
/* If we're pointing at two successive 0xed bytes, that's a run. If not, just copy the byte across */
if (*in_ptr == 0xed && *(in_ptr + 1) == 0xed) {
size_t run_length;
uint8_t repeated;
in_ptr += 2;
run_length = *in_ptr++;
repeated = *in_ptr++;
libspectrumMakeRoom(dest, run_length, &out_ptr, dest_length);
while (run_length--) {
*out_ptr++ = repeated;
}
} else {
libspectrumMakeRoom(dest, 1, &out_ptr, dest_length);
*out_ptr++ = *in_ptr++;
}
}
*dest_length = out_ptr - *dest;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,47 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_DECOMPRESSZ80_H
#define GLK_SCOTT_DECOMPRESSZ80_H
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
// Will return nullptr on error or 0xc000 (49152) bytes of uncompressed raw data on success
uint8_t *decompressZ80(uint8_t *rawData, size_t length);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,89 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/definitions.h"
namespace Glk {
namespace Scott {
GameInfo::GameInfo() {}
GameInfo::GameInfo(
const char *title,
GameIDType gameID,
GameType type,
Subtype subType,
DictionaryType dictionary,
int numberOfItems,
int numberOfActions,
int numberOfWords,
int numberOfRooms,
int maxCarried,
int wordLength,
int numberOfMessages,
int numberOfVerbs,
int numberOfNouns,
int startOfHeader,
HeaderType headerStyle,
int startOfRoomImageList,
int startOfItemFlags,
int startOfItemImageList,
int startOfActions,
ActionTableType actionsStyle,
int startOfDictionary,
int startOfRoomDescriptions,
int startOfRoomConnections,
int startOfMessages,
int startOfItemDescriptions,
int startOfItemLocations,
int startOfSystemMessages,
int startOfDirections,
int startOfCharacters,
int startOfImageData,
int imageAddressOffset,
int numberOfPictures,
PaletteType palette,
int pictureFormatVersion,
int startOfIntroText)
: _title(title), _gameID(gameID), _type(type), _subType(subType), _dictionary(dictionary), _numberOfItems(numberOfItems),
_numberOfActions(numberOfActions), _numberOfWords(numberOfWords), _numberOfRooms(numberOfRooms), _maxCarried(maxCarried),
_wordLength(wordLength), _numberOfMessages(numberOfMessages), _numberOfVerbs(numberOfVerbs), _numberOfNouns(numberOfNouns),
_startOfHeader(startOfHeader), _headerStyle(headerStyle), _startOfRoomImageList(startOfRoomImageList),
_startOfItemFlags(startOfItemFlags), _startOfItemImageList(startOfItemImageList), _startOfActions(startOfActions),
_actionsStyle(actionsStyle), _startOfDictionary(startOfDictionary), _startOfRoomDescriptions(startOfRoomDescriptions),
_startOfRoomConnections(startOfRoomConnections), _startOfMessages(startOfMessages), _startOfItemDescriptions(startOfItemDescriptions),
_startOfItemLocations(startOfItemLocations), _startOfSystemMessages(startOfSystemMessages), _startOfDirections(startOfDirections),
_startOfCharacters(startOfCharacters), _startOfImageData(startOfImageData), _imageAddressOffset(imageAddressOffset),
_numberOfPictures(numberOfPictures), _palette(palette), _pictureFormatVersion(pictureFormatVersion), _startOfIntroText(startOfIntroText) {}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,320 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_DEFINITIONS_H
#define GLK_SCOTT_DEFINITIONS_H
#include "common/str.h"
namespace Glk {
namespace Scott {
const int FOLLOWS = 0xFFFF;
const int GO = 1;
const int TAKE = 10;
const int DROP = 18;
const int LASTALL = 128;
enum GameIDType {
UNKNOWN_GAME,
SCOTTFREE,
TI994A,
PIRATE,
VOODOO,
STRANGE,
BANZAI,
BATON,
BATON_C64,
TIME_MACHINE,
TIME_MACHINE_C64,
ARROW1,
ARROW1_C64,
ARROW2,
ARROW2_C64,
PULSAR7,
PULSAR7_C64,
CIRCUS,
CIRCUS_C64,
FEASIBILITY,
FEASIBILITY_C64,
AKYRZ,
AKYRZ_C64,
PERSEUS,
PERSEUS_C64,
PERSEUS_ITALIAN,
INDIANS,
INDIANS_C64,
WAXWORKS,
WAXWORKS_C64,
HULK,
HULK_C64,
ADVENTURELAND,
ADVENTURELAND_C64,
SECRET_MISSION,
SECRET_MISSION_C64,
CLAYMORGUE,
CLAYMORGUE_C64,
SPIDERMAN,
SPIDERMAN_C64,
SAVAGE_ISLAND,
SAVAGE_ISLAND_C64,
SAVAGE_ISLAND2,
SAVAGE_ISLAND2_C64,
GREMLINS,
GREMLINS_ALT,
GREMLINS_C64,
GREMLINS_GERMAN,
GREMLINS_GERMAN_C64,
GREMLINS_SPANISH,
SUPERGRAN,
SUPERGRAN_C64,
ROBIN_OF_SHERWOOD,
ROBIN_OF_SHERWOOD_C64,
SEAS_OF_BLOOD,
SEAS_OF_BLOOD_C64,
NUMGAMES
};
enum ExplicitResultType {
ER_NO_RESULT,
ER_SUCCESS = 0,
ER_RAN_ALL_LINES_NO_MATCH = -1,
ER_RAN_ALL_LINES = -2
};
enum ActionResultType {
ACT_SUCCESS = 0,
ACT_FAILURE = 1,
ACT_CONTINUE,
ACT_GAMEOVER
};
enum SysMessageType {
NORTH,
SOUTH,
EAST,
WEST,
UP,
DOWN,
PLAY_AGAIN,
IVE_STORED,
TREASURES,
ON_A_SCALE_THAT_RATES,
DROPPED,
TAKEN,
OK,
YOUVE_SOLVED_IT,
I_DONT_UNDERSTAND,
YOU_CANT_DO_THAT_YET,
HUH,
DIRECTION,
YOU_HAVENT_GOT_IT,
YOU_HAVE_IT,
YOU_DONT_SEE_IT,
THATS_BEYOND_MY_POWER,
DANGEROUS_TO_MOVE_IN_DARK,
YOU_FELL_AND_BROKE_YOUR_NECK,
YOU_CANT_GO_THAT_WAY,
I_DONT_KNOW_HOW_TO,
SOMETHING,
I_DONT_KNOW_WHAT_A,
IS,
TOO_DARK_TO_SEE,
YOU_ARE,
YOU_SEE,
EXITS,
INVENTORY,
NOTHING,
WHAT_NOW,
HIT_ENTER,
LIGHT_HAS_RUN_OUT,
LIGHT_RUNS_OUT_IN,
TURNS,
YOURE_CARRYING_TOO_MUCH,
IM_DEAD,
RESUME_A_SAVED_GAME,
NONE,
NOTHING_HERE_TO_TAKE,
YOU_HAVE_NOTHING,
LIGHT_GROWING_DIM,
EXITS_DELIMITER,
MESSAGE_DELIMITER,
ITEM_DELIMITER,
WHAT,
YES,
NO,
ANSWER_YES_OR_NO,
ARE_YOU_SURE,
MOVE_UNDONE,
CANT_UNDO_ON_FIRST_TURN,
NO_UNDO_STATES,
SAVED,
CANT_USE_ALL,
TRANSCRIPT_OFF,
TRANSCRIPT_ON,
NO_TRANSCRIPT,
TRANSCRIPT_ALREADY,
FAILED_TRANSCRIPT,
TRANSCRIPT_START,
TRANSCRIPT_END,
BAD_DATA,
STATE_SAVED,
STATE_RESTORED,
NO_SAVED_STATE,
LAST_SYSTEM_MESSAGE
};
const SysMessageType MAX_SYSMESS = LAST_SYSTEM_MESSAGE;
enum DictionaryType {
NOT_A_GAME,
FOUR_LETTER_UNCOMPRESSED,
THREE_LETTER_UNCOMPRESSED,
FIVE_LETTER_UNCOMPRESSED,
FOUR_LETTER_COMPRESSED,
FIVE_LETTER_COMPRESSED,
GERMAN,
SPANISH,
ITALIAN
};
enum GameType {
NO_TYPE,
GREMLINS_VARIANT,
SHERWOOD_VARIANT,
SAVAGE_ISLAND_VARIANT,
SECRET_MISSION_VARIANT,
SEAS_OF_BLOOD_VARIANT,
OLD_STYLE,
};
enum Subtype {
ENGLISH = 0x1,
MYSTERIOUS = 0x2,
LOCALIZED = 0x4,
C64 = 0x8
};
enum PaletteType {
NO_PALETTE,
ZX,
ZXOPT,
C64A,
C64B,
VGA
};
enum HeaderType {
NO_HEADER,
EARLY,
LATE,
HULK_HEADER,
GREMLINS_C64_HEADER,
ROBIN_C64_HEADER,
SUPERGRAN_C64_HEADER,
SEAS_OF_BLOOD_C64_HEADER,
MYSTERIOUS_C64_HEADER,
ARROW_OF_DEATH_PT_2_C64_HEADER,
INDIANS_C64_HEADER
};
enum ActionTableType {
UNKNOWN_ACTIONS_TYPE,
COMPRESSED,
UNCOMPRESSED,
HULK_ACTIONS
};
struct GameInfo {
GameInfo();
GameInfo(const char *title, GameIDType gameID, GameType type, Subtype subType, DictionaryType dictionary,
int numberOfItems, int numberOfActions, int numberOfWords, int numberOfRooms, int maxCarried,
int wordLength, int numberOfMessages, int numberOfVerbs, int numberOfNouns, int startOfHeader,
HeaderType headerStyle, int startOfRoomImageList, int startOfItemFlags, int startOfItemImageList,
int startOfActions, ActionTableType actionsStyle, int startOfDictionary, int startOfRoomDescriptions,
int startOfRoomConnections, int startOfMessages, int startOfItemDescriptions, int startOfItemLocations,
int startOfSystemMessages, int startOfDirections, int startOfCharacters, int startOfImageData,
int imageAddressOffset, int numberOfPictures, PaletteType palette, int pictureFormatVersion,
int startOfIntroText);
const char *_title;
GameIDType _gameID = UNKNOWN_GAME;
GameType _type = NO_TYPE;
Subtype _subType = ENGLISH;
DictionaryType _dictionary = NOT_A_GAME;
int _numberOfItems = 0;
int _numberOfActions = 0;
int _numberOfWords = 0;
int _numberOfRooms = 0;
int _maxCarried = 0;
int _wordLength = 0;
int _numberOfMessages = 0;
int _numberOfVerbs = 0;
int _numberOfNouns = 0;
int _startOfHeader = 0;
HeaderType _headerStyle = NO_HEADER;
int _startOfRoomImageList = 0;
int _startOfItemFlags = 0;
int _startOfItemImageList = 0;
int _startOfActions = 0;
ActionTableType _actionsStyle = UNKNOWN_ACTIONS_TYPE;
int _startOfDictionary = 0;
int _startOfRoomDescriptions = 0;
int _startOfRoomConnections = 0;
int _startOfMessages = 0;
int _startOfItemDescriptions = 0;
int _startOfItemLocations = 0;
int _startOfSystemMessages = 0;
int _startOfDirections = 0;
int _startOfCharacters = 0;
int _startOfImageData = 0;
int _imageAddressOffset = 0; /* This is the difference between the value given by
the image data lookup table and a usable file
offset */
int _numberOfPictures = 0;
PaletteType _palette = NO_PALETTE;
int _pictureFormatVersion = 0;
int _startOfIntroText = 0;
};
} // End of namespace Scott
} // End of namespace Glk
#endif /* definitions_h */

View File

@@ -0,0 +1,127 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "common/file.h"
#include "common/md5.h"
#include "engines/game.h"
#include "glk/blorb.h"
#include "glk/scott/detection.h"
#include "glk/scott/detection_tables.h"
namespace Glk {
namespace Scott {
void ScottMetaEngine::getSupportedGames(PlainGameList &games) {
for (const PlainGameDescriptor *pd = SCOTT_GAME_LIST; pd->gameId; ++pd)
games.push_back(*pd);
}
const GlkDetectionEntry* ScottMetaEngine::getDetectionEntries() {
return SCOTT_GAMES;
}
GameDescriptor ScottMetaEngine::findGame(const char *gameId) {
for (const PlainGameDescriptor *pd = SCOTT_GAME_LIST; pd->gameId; ++pd) {
if (!strcmp(gameId, pd->gameId))
return *pd;
}
return GameDescriptor::empty();
}
bool ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
const char *const EXTENSIONS[] = {".z80", ".saga", ".dat", ".D64", ".T64", "fiad", nullptr};
// Loop through the files of the folder
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
// Check for a recognised filename
if (file->isDirectory())
continue;
Common::String filename = file->getName();
bool hasExt = Blorb::hasBlorbExt(filename), isBlorb = false;
for (const char *const *ext = &EXTENSIONS[0]; *ext && !hasExt; ++ext)
hasExt = filename.hasSuffixIgnoreCase(*ext);
if (!hasExt)
continue;
Common::File gameFile;
if (!gameFile.open(*file))
continue;
Common::String md5;
if (filename.hasSuffixIgnoreCase(".D64"))
md5 = Common::computeStreamMD5AsString(gameFile);
else
md5 = Common::computeStreamMD5AsString(gameFile, 5000);
size_t filesize = (size_t)gameFile.size();
gameFile.seek(0);
isBlorb = Blorb::isBlorb(gameFile, ID_SAAI);
gameFile.close();
if (!isBlorb && Blorb::hasBlorbExt(filename))
continue;
// Scan through the Scott game list for a match
const GlkDetectionEntry *p = SCOTT_GAMES;
while (p->_md5 && (p->_filesize != filesize || md5 != p->_md5))
++p;
if (!p->_gameId) {
// ignore possible variants for common extensions to prevent flooding in mass-add
if (!isBlorb && (filename.hasSuffixIgnoreCase(".z80") || filename.hasSuffixIgnoreCase(".dat") ||
filename.hasSuffixIgnoreCase(".d64") || filename.hasSuffixIgnoreCase(".t64")))
continue;
const PlainGameDescriptor &desc = SCOTT_GAME_LIST[0];
gameList.push_back(GlkDetectedGame(desc.gameId, desc.description, filename, md5, filesize));
} else {
// Found a match
PlainGameDescriptor gameDesc = findGame(p->_gameId);
gameList.push_back(GlkDetectedGame(p->_gameId, gameDesc.description, filename, p->_language, p->_platform));
}
}
return !gameList.empty();
}
void ScottMetaEngine::detectClashes(Common::StringMap &map) {
for (const PlainGameDescriptor *pd = SCOTT_GAME_LIST; pd->gameId; ++pd) {
if (map.contains(pd->gameId))
error("ScottMetaEngine::detectClashes: Duplicate game Id found - %s", pd->gameId);
map[pd->gameId] = "";
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,75 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_DETECTION_H
#define GLK_SCOTT_DETECTION_H
#include "common/fs.h"
#include "common/hash-str.h"
#include "engines/game.h"
#include "glk/detection.h"
namespace Glk {
namespace Scott {
class ScottMetaEngine {
public:
/**
* Get a list of supported games
*/
static void getSupportedGames(PlainGameList &games);
/**
* Get the detection entries
*/
static const GlkDetectionEntry* getDetectionEntries();
/**
* Returns a game description for the given game Id, if it's supported
*/
static GameDescriptor findGame(const char *gameId);
/**
* Detect supported games
*/
static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
/**
* Check for game Id clashes with other sub-engines
*/
static void detectClashes(Common::StringMap &map);
};
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,291 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "common/language.h"
#include "engines/game.h"
namespace Glk {
namespace Scott {
const PlainGameDescriptor SCOTT_GAME_LIST[] = {
{ "scottadams", "Scott Adams IF Game" },
// Scott Adams games
{ "adventureland", "Adventureland" },
{ "pirateadventure", "Pirate Adventure" },
{ "missionimpossible", "Mission Impossible" },
{ "voodoocastle", "Voodoo Castle" },
{ "thecount", "The Count" },
{ "strangeodyssey", "Strange Odyssey" },
{ "mysteryfunhouse", "Mystery Fun House" },
{ "pyramidofdoom", "Pyramid Of Doom" },
{ "ghosttown_sa", "Ghost Town (by Scott Adams)" },
{ "savageisland", "Savage Island" },
{ "savageisland1", "Savage Island, Part 1" },
{ "savageisland2", "Savage Island, Part 2" },
{ "goldenvoyage", "The Golden Voyage" },
{ "claymorguesorcerer", "Sorcerer of Claymorgue Castle" },
{ "pirateisle", "Return to Pirate Isle" },
{ "buckaroobanzai", "Buckaroo Banzai" },
{ "marveladventure", "Marvel Adventure #1" },
{ "marveladventure2", "Marvel Adventure #2" },
{ "scottsampler", "Adventure International's Mini-Adventure Sampler" },
{ "robinofsherwood", "Robin Of Sherwood" },
// 11 Mysterious Adventures by Brian Howarth
{ "goldenbaton", "Mysterious Adventures 1: The Golden Baton" },
{ "timemachine", "Mysterious Adventures 2: The Time Machine" },
{ "arrowofdeath1", "Mysterious Adventures 3: Arrow of Death Part 1" },
{ "arrowofdeath2", "Mysterious Adventures 4: Arrow of Death Part 2" },
{ "pulsar7", "Mysterious Adventures 5: Escape from Pulsar 7" },
{ "circus", "Mysterious Adventures 6: Circus" },
{ "feasibility", "Mysterious Adventures 7: Feasibility Experiment" },
{ "akyrz", "Mysterious Adventures 8: The Wizard of Akyrz" },
{ "perseus", "Mysterious Adventures 9: Perseus and Andromeda" },
{ "10indians", "Mysterious Adventures 10: Ten Little Indians" },
{ "waxworks11", "Mysterious Adventures 11: Waxworks" },
{ "mysadv1", "11 Mysterious Adventures - Disk 1" },
{ "mysadv2", "11 Mysterious Adventures - Disk 2" },
// Home Brew TI99 Games
{ "advocadoadv", "The Great Advocado Adventure" },
{ "matildadilemma", "Matilda's Dilemma" },
{ "escapecannibal", "Escape from Cannibal Island" },
{ "captkiddmystery", "Mystery of Captain Kidd" },
{ "advscott", "Adventure, Colossal Cave (adaptation)" },
{ "advscottmaster", "Adventure, Colossal Cave (adaptation) - Masters Game" },
{ "computorama", "Computorama" },
{ "escapealcatraz", "Escape from Alcatraz" },
{ "gerryplace", "Gerry's Place" },
{ "grayelftomb", "Tomb of the Gray Elf" },
{ "knightiron", "Knight Ironheart" },
{ "moonadv", "Moon Adventure" },
{ "nessy", "Nessy" },
{ "travelling", "Travelling" },
// Other Games
{ "desert", "Desert Adventure" },
{ "jamesbondadv", "James Bond Adventure" },
{ "burglarsadv", "Burglar's Adventure" },
{ "underseaconquest", "Undersea Conquest part 1" },
{ "gammaworld", "Gamma World" },
{ "marooned_kw", "Marooned (by Kim Watt)" },
{ "minersadv", "Miner's Adventure" },
{ "romulanadv", "Romulan Adventure" },
{ "topsecretadv", "Top Secret Adventure" },
{ "gremlins", "Gremlins" },
{ "seasofblood", "Seas Of Blood" },
{ "supergran", "Super Gran" },
{ "ghostking", "Ghost King" },
{ nullptr, nullptr }
};
const GlkDetectionEntry SCOTT_GAMES[] = {
// PC game versions
DT_ENTRY0("adventureland", "7c6f495d757a54e73d259efc718d8024", 15896),
DT_ENTRY0("pirateadventure", "ea535fa7684508410151b4561de1f323", 16325),
DT_ENTRY0("missionimpossible", "379c77a9a483886366b3b5c425e56410", 15275),
DT_ENTRY0("voodoocastle", "a530a6857d1092eaa177eee575c94c71", 15852),
DT_ENTRY0("thecount", "5ebb4ade985670bb2eac54f8fa202214", 17476),
DT_ENTRY0("strangeodyssey", "c57bb6df04dc77a2b232bc5bcab6e417", 17489),
DT_ENTRY0("mysteryfunhouse", "ce2931ac3d5cbc270a5cb7be9e614f6e", 17165),
DT_ENTRY0("pyramidofdoom", "4e6127fad6b5d75eccd3f3b101f8c9c8", 17673),
DT_ENTRY0("ghosttown_sa", "2c08327ab06d5490bd9e367ddaeca627", 17831),
DT_ENTRY0("savageisland1", "8feb77f11d32e9567ce2fc7d435eaf44", 19533),
DT_ENTRY0("savageisland2", "20c40a349f7a214ac515fb1d63c30a87", 18367),
DT_ENTRY0("goldenvoyage", "e2a8f956ab215012d1495550c4c11ee8", 18513),
DT_ENTRY0("claymorguesorcerer","f986d7e1ee074f65b6c1d00461c9b3c3", 19232),
DT_ENTRY0("pirateisle", "6d98f422cc986d959a3c74351785aea3", 19013),
DT_ENTRY0("buckaroobanzai", "ee2c7139d58423c5e25dd918fcb48383", 17989),
DT_ENTRY0("marveladventure", "aadcc04e6b37eb9d30a58b5bc775842e", 18876),
DT_ENTRY0("marveladventure2", "f7e772643a24eaf94cfb2d48e3949ec1", 18643),
DT_ENTRY0("scottsampler", "d569a769f304dc02b3062d97458ddd01", 13854),
// PDA game versions
DT_ENTRY0("adventureland", "ae541fc1085da2f7d561b72ed20a6bc1", 18003),
DT_ENTRY0("pirateadventure", "cbd47ab4fcfe00231ffd71d52378d410", 18482),
DT_ENTRY0("missionimpossible", "9251ab2c64e63559d8a6e9e6246760a5", 17227),
DT_ENTRY0("voodoocastle", "be849c5747c7fc3b201984afb4403b8e", 18140),
DT_ENTRY0("thecount", "85b75b6079b5ee572b5259b29a0e5d21", 19999),
DT_ENTRY0("strangeodyssey", "c423cae841ac1927b5b2e503607b21bc", 20115),
DT_ENTRY0("mysteryfunhouse", "326b98b991d401605074e64d474ce566", 19700),
DT_ENTRY0("pyramidofdoom", "8ef9010399f055da9adb15ce7745a11c", 20320),
DT_ENTRY0("ghosttown", "fcdcca8b2acf76ba2d0006cefa3630a1", 20687),
DT_ENTRY0("savageisland1", "c8aaa80f07c40fa8e4b17432644919dc", 22669),
DT_ENTRY0("savageisland2", "2add0f28d9b236c866890cdf8d86ee60", 21169),
DT_ENTRY0("goldenvoyage", "675126bd0477e8ed9230ad3db5afc45f", 21401),
DT_ENTRY0("claymorguesorcerer","0ef0def798d895ed766041fa99dd28a0", 22346),
DT_ENTRY0("pirateisle", "0bf1bcc649422798332a38c88588fdff", 22087),
DT_ENTRY0("buckaroobanzai", "a0a5423967287dae9cbeb9abe8324479", 21038),
// 11 Mysterious Adventures
DT_ENTRY0("goldenbaton", "2ebf7b1dd515aff34b99b40f27af00e3", 13373),
DT_ENTRY0("timemachine", "a10cde2063bb56694000013b32dc0798", 13742),
DT_ENTRY0("arrowofdeath1", "6f7040fe091eeb9ebdc2fd7533b94ceb", 13412),
DT_ENTRY0("arrowofdeath2", "c5758034b83b06258e46091a80abebde", 15554),
DT_ENTRY0("pulsar7", "7597704b423c1ca2bea3a48263b5f4dc", 17777),
DT_ENTRY0("circus", "93a7947e2edb7c45036446168b427546", 13621),
DT_ENTRY0("feasibility", "c8f423a79ad0e508f43ba4ce91f1e573", 13441),
DT_ENTRY0("akyrz", "645c089d6d66324472d939993793eb57", 16803),
DT_ENTRY0("perseus", "2f5bb15b461fbdc000243acabd324c67", 15080),
DT_ENTRY0("10indians", "3bb85fd505b4d42bd1767c1a76347e2f", 14215),
DT_ENTRY0("waxworks11", "af134c32cc0d50329d6e7335639ded88", 16068),
DT_ENTRY0("goldenbaton", "028303fd062c39b59b28982cde75f085", 53374),
DT_ENTRY0("timemachine", "b2ff405412d92b06373d9a5efbb048d4", 54062),
DT_ENTRY0("arrowofdeath1", "f11f85802eff8aed9e212aef7f26ede1", 68150),
DT_ENTRY0("arrowofdeath2", "8f2609b1270248c93cdf254435f9d410", 77544),
DT_ENTRY0("pulsar7", "72e14dbfca7ced5c0aadf019b47b4024", 55516),
DT_ENTRY0("circus", "64d44e66c4f5353150f8fbaceaa99800", 48624),
DT_ENTRY0("feasibility", "2d717aa4f0eba77b77e259f81f92fc37", 78456),
DT_ENTRY0("akyrz", "3467e09eb247c474ae7e8a8d503968d0", 65008),
DT_ENTRY0("perseus", "4d8389c0ea3425bd4d92492538f63b19", 56914),
DT_ENTRY0("10indians", "72e91da7590138b78d370a3cb1448f3c", 64660),
DT_ENTRY0("waxworks11", "4d4ee8aa1f24e1745ab1aa017590dcff", 54556),
// Other games
DT_ENTRY0("desert", "c43e19228bae08eab74bc080e17bbe16", 12287),
DT_ENTRY0("jamesbondadv", "5c59396a2db040148a5d86beb37b2bb9", 12405),
DT_ENTRY0("burglarsadv", "0072d8afcd30aa1577350dcfad269e47", 11541),
DT_ENTRY0("underseaconquest", "d57705f8f17f0b6044a575accf9cbfd1", 5616),
DT_ENTRY0("gammaworld", "b980c44e8a49aa9d71e92f6b6bf1d136", 11531),
DT_ENTRY0("marooned_kw", "a1ac54630a0583c19269901ec10cd0b1", 12576),
DT_ENTRY0("minersadv", "0000d9da5a13701601fb3e7399daa128", 11898),
DT_ENTRY0("romulanadv", "d97b5cb5ed66eb276ef9f1c1bae0b8dd", 13959),
DT_ENTRY0("topsecretadv", "effb411e74dfe3a8d69b57b9bc3a2cef", 15575),
DT_ENTRY0("ghostking", "28d433d9f5d2de99dac460c5e1dfa9c5", 14206),
// ZX Spectrum games
DT_ENTRYP1("marveladventure", "ZXSpectrum", "0eec511d3cde815c73e5464ab0cdbef9", 40727, Common::kPlatformZX),
// 11 Mysterious Adventures
DT_ENTRYP1("goldenbaton", "ZXSpectrum", "cb7dadc9d5f8bce453b9139265e4dd7d", 32060, Common::kPlatformZX),
DT_ENTRYP1("timemachine", "ZXSpectrum", "b22d1f4d46c99ff4443d541d3fe424c1", 30928, Common::kPlatformZX),
DT_ENTRYP1("arrowofdeath1", "ZXSpectrum", "3a5c3f4079c1c0347f03420db8ad4596", 34105, Common::kPlatformZX),
DT_ENTRYP1("arrowofdeath2", "ZXSpectrum", "d3f8943c4f5f71ce00139065055a72ee", 38043, Common::kPlatformZX),
DT_ENTRYP1("pulsar7", "ZXSpectrum", "441edd90fc7f9ff39a5eebe035a974e9", 29961, Common::kPlatformZX),
DT_ENTRYP1("circus", "ZXSpectrum", "ed99306a2fb23bf6579068a4d74034ee", 27746, Common::kPlatformZX),
DT_ENTRYP1("feasibility", "ZXSpectrum", "5e381e83f15d77e3542be4a4cffc8e25", 37395, Common::kPlatformZX),
DT_ENTRYP1("akyrz", "ZXSpectrum", "b0f8676817475753f1edd7f1eeea31fb", 33753, Common::kPlatformZX),
DT_ENTRYP1("perseus", "ZXSpectrum", "84d5fbb16a37e495abf09d191fd8b1a2", 31504, Common::kPlatformZX),
DT_ENTRYP1("10indians", "ZXSpectrum", "afde056c152de79ea20453c42a2d08af", 31664, Common::kPlatformZX),
DT_ENTRYP1("waxworks11", "ZXSpectrum", "6c6fbbbb50032463a6ea71c6750ea1f5", 32662, Common::kPlatformZX),
// C64 Games
DT_ENTRYP1("robinofsherwood", "C64", "10109d9776b9372f9c768b53a664b113", 174848, Common::kPlatformC64),
DT_ENTRYP1("robinofsherwood", "C64", "552c95ec15d750cbfa02c1f11dcbca1e", 36278, Common::kPlatformC64),
DT_ENTRYP1("robinofsherwood", "C64", "4262f85382d1bc3b8924a1929511a558", 45807, Common::kPlatformC64),
DT_ENTRYP1("robinofsherwood", "C64", "bf3a4d72cff5ef97bebce6b12c756df2", 46736, Common::kPlatformC64),
DT_ENTRYP1("gremlins", "C64", "33c920f7ba150dfa1a832d510eebd8fe", 174848, Common::kPlatformC64),
DT_ENTRYP1("gremlins", "C64", "947e35037cf02269ac013857925137ce", 174848, Common::kPlatformC64),
DT_ENTRYP1("gremlins", "C64", "95b2582a89c59961d5b709c9b32e4131", 174848, Common::kPlatformC64),
DT_ENTRYP1("gremlins", "C64", "644c366956202d41df0ea1c4303c5895", 174848, Common::kPlatformC64),
DT_ENTRYP1("gremlins", "C64", "108063b2a16a199794f2ecf52ce26377", 56724, Common::kPlatformC64),
DT_ENTRYLP1("gremlins", Common::DE_DEU, "C64", "c60977950ff22ae57483f073345b8373", 174848, Common::kPlatformC64),
DT_ENTRYLP1("gremlins", Common::DE_DEU, "C64", "e5d743d8727c8aca011a737bbb5ad818", 49155, Common::kPlatformC64),
DT_ENTRYP1("seasofblood", "C64", "267c3fe2bb150365de0358f07b5df15c", 174848, Common::kPlatformC64),
DT_ENTRYP1("seasofblood", "C64", "0300c2d21289157539bbd03ab4e366ee", 174848, Common::kPlatformC64),
DT_ENTRYP1("seasofblood", "C64", "46280fb1d701a41492b8434467c31029", 41481, Common::kPlatformC64),
DT_ENTRYP1("marveladventure", "C64", "d80e133dd396565f773052cb317e8222", 174848, Common::kPlatformC64),
DT_ENTRYP1("marveladventure2", "C64", "5d0ea85ca1f260ca718a6bbb6da4cdb9", 174848, Common::kPlatformC64),
DT_ENTRYP1("marveladventure2", "C64", "3d88539a6dd7e6e179bb61041125cc0f", 36466, Common::kPlatformC64),
DT_ENTRYP1("supergran", "C64", "36a5b1b2afb38902933856b3446d760e", 29295, Common::kPlatformC64),
// Scott Adams
DT_ENTRYP1("adventureland", "C64", "05cf6c64ecde5288ae2e46099bfd19a3", 27152, Common::kPlatformC64),
DT_ENTRYP1("adventureland", "C64", "5af919881417920ec6a3961b4577f587", 27152, Common::kPlatformC64),
DT_ENTRYP1("adventureland", "C64", "ccd3e3c805134b4fc36ad92e1cae623f", 174848, Common::kPlatformC64),
DT_ENTRYP1("adventureland", "C64", "547036c586bfcd53e741ecfad74e3001", 175531, Common::kPlatformC64),
DT_ENTRYP1("adventureland", "C64", "6c3de0b0ef39fad9d63e788de8cd972c", 175531, Common::kPlatformC64),
DT_ENTRYP1("claymorguesorcerer", "C64", "a1db488c49ad221fa0dc79591cb5a3db", 28663, Common::kPlatformC64),
DT_ENTRYP1("claymorguesorcerer", "C64", "1ebaf9a378355246aa7ed2623bb27fab", 49373, Common::kPlatformC64),
DT_ENTRYP1("claymorguesorcerer", "C64", "dde67117a432420ef05f8d665fbbbe10", 37167, Common::kPlatformC64),
DT_ENTRYP1("claymorguesorcerer", "C64", "8d2af429e53df1c4da0d21bdc9de6826", 48223, Common::kPlatformC64),
DT_ENTRYP1("claymorguesorcerer", "C64", "64fcee173adecc0f03c595e25d4def04", 174848, Common::kPlatformC64),
DT_ENTRYP1("savageisland", "C64", "414d459ceb211230356ad823475866b3", 174848, Common::kPlatformC64),
DT_ENTRYP1("savageisland", "C64", "f0087b1f42ea9a0656462bf339278b08", 174848, Common::kPlatformC64),
// 11 Mysterious Adventures
DT_ENTRYP1("mysadv1", "C64", "b4d8fc4eabed4f2400717303561ad0fa", 174848, Common::kPlatformC64),
DT_ENTRYP1("mysadv2", "C64", "3ce5ea1a0473244bf469fd3c51f1dc48", 174848, Common::kPlatformC64),
DT_ENTRYP1("goldenbaton", "C64", "f2711fe0376442f6f320da1b73b5b1a3", 20848, Common::kPlatformC64),
DT_ENTRYP1("goldenbaton", "C64", "ed22cb234af638e7d9f570b937f9fc52", 174848, Common::kPlatformC64),
DT_ENTRYP1("timemachine", "C64", "cc8e94d3fb665d5d23b728e9c1f262ba", 20530, Common::kPlatformC64),
DT_ENTRYP1("arrowofdeath1", "C64", "da044c4c57dc194002ba47f5b2921411", 23366, Common::kPlatformC64),
DT_ENTRYP1("arrowofdeath2", "C64", "3d40cb011167e7ae9f6695cdd1f4a1bf", 24546, Common::kPlatformC64),
DT_ENTRYP1("pulsar7", "C64", "db68753e4c4c536693edca2f58747044", 18111, Common::kPlatformC64),
DT_ENTRYP1("circus", "C64", "4f732cb27e2a0bb484443a7dd1974ccf", 17001, Common::kPlatformC64),
DT_ENTRYP1("feasibility", "C64", "6c7ed2fd5f0247a55beb266344967662", 23163, Common::kPlatformC64),
DT_ENTRYP1("akyrz", "C64", "94e4b070e45204b12d1655091c56045d", 19425, Common::kPlatformC64),
DT_ENTRYP1("akyrz", "C64", "c423ad31ab3f6b45f0215c2e7fc3eb7e", 174848, Common::kPlatformC64),
DT_ENTRYP1("perseus", "C64", "96a1ccb3212808eee03e74cdc1f0d1a4", 20523, Common::kPlatformC64),
DT_ENTRYP1("10indians", "C64", "79ee3669ccfff7338dfc1810228005dc", 20383, Common::kPlatformC64),
DT_ENTRYP1("waxworks11", "C64", "facc11aa8b51e88a807236b765203eb0", 18961, Common::kPlatformC64),
// TI99 Games
// Scott Adams
DT_ENTRYP1("adventureland", "TI99/4A", "c677576bd33a0fe0ff95a9d5c0e3b3ba", 10774, Common::kPlatformTI994),
DT_ENTRYP1("pirateadventure", "TI99/4A", "6f293ad8fcce6b0adf56e98fdfe3eaf4", 10488, Common::kPlatformTI994),
DT_ENTRYP1("missionimpossible", "TI99/4A", "ac977babc6d7cce815b05be42deeec55", 10562, Common::kPlatformTI994),
DT_ENTRYP1("voodoocastle", "TI99/4A", "ff8383afe5addf2f302975b0085b5d5e", 10424, Common::kPlatformTI994),
DT_ENTRYP1("thecount", "TI99/4A", "c75701c886bc476e1cbb5321ebd594b2", 10326, Common::kPlatformTI994),
DT_ENTRYP1("strangeodyssey", "TI99/4A", "46dbc05fc0177cfee1e9ccd3ec4a9a4c", 10170, Common::kPlatformTI994),
DT_ENTRYP1("mysteryfunhouse", "TI99/4A", "9f236bd084f5d1fabb7f9591b6ad1c44", 10594, Common::kPlatformTI994),
DT_ENTRYP1("pyramidofdoom", "TI99/4A", "2912111425d87af5b156f95e5766206d", 10242, Common::kPlatformTI994),
DT_ENTRYP1("ghosttown", "TI99/4A", "34feb31e2265fd8721d2192443595a8c", 10170, Common::kPlatformTI994),
DT_ENTRYP1("savageisland1", "TI99/4A", "a59c7841037fce63cf54899e8f562f25", 10170, Common::kPlatformTI994),
DT_ENTRYP1("savageisland2", "TI99/4A", "b40ec602d4c4c442b910b4f109929562", 12616, Common::kPlatformTI994),
DT_ENTRYP1("goldenvoyage", "TI99/4A", "ce4a136b4c2f3d56ce0341d830760bb5", 10346, Common::kPlatformTI994),
// Home Brew
DT_ENTRYP1("advocadoadv", "TI99/4A", "80d6b91ea10c26c8139c4fa39dbd8161", 12416, Common::kPlatformTI994),
DT_ENTRYP1("matildadilemma", "TI99/4A", "d540575c23434b5ff424513820158a84", 13440, Common::kPlatformTI994),
DT_ENTRYP1("escapecannibal", "TI99/4A", "3d0f7d76a81c1fd5c0f2bb52c6dda37e", 12672, Common::kPlatformTI994),
DT_ENTRYP1("captkiddmystery", "TI99/4A", "2ec292ced102259f10bd4089f3d31d27", 11136, Common::kPlatformTI994),
DT_ENTRYP1("advscott", "TI99/4A", "01bbf82b4047294322e63a7622bb9803", 13440, Common::kPlatformTI994),
DT_ENTRYP1("advscottmaster", "TI99/4A", "229b84804d3b74af2fc52e18f6241f36", 10880, Common::kPlatformTI994),
DT_ENTRYP1("computorama", "TI99/4A", "c5e9a015d59c584cd48bffde3f394ff8", 5147, Common::kPlatformTI994),
DT_ENTRYP1("escapealcatraz", "TI99/4A", "1a01336ce0799f67403d50192a99d63f", 9600, Common::kPlatformTI994),
DT_ENTRYP1("gerryplace", "TI99/4A", "36f427bd810e60554ef6ff3b91851816", 9600, Common::kPlatformTI994),
DT_ENTRYP1("grayelftomb", "TI99/4A", "b88a4644652589ff6633c400f4943305", 10112, Common::kPlatformTI994),
DT_ENTRYP1("knightiron", "TI99/4A", "c97aac994799840b634a501599ec5059", 13305, Common::kPlatformTI994),
DT_ENTRYP1("moonadv", "TI99/4A", "40af101895149d397bef50dfdf3cedbd", 9600, Common::kPlatformTI994),
DT_ENTRYP1("nessy", "TI99/4A", "49d3b3402c49f947577a14f8588f0dcd", 11392, Common::kPlatformTI994),
DT_ENTRYP1("travelling", "TI99/4A", "1f2624be91a256901e555f4d49b09f85", 11648, Common::kPlatformTI994),
DT_END_MARKER
};
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,811 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* https://paradroid.automac.se/diskimage/
* Copyright (c) 2003-2006, Per Olofsson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "common/scummsys.h"
#include "common/str.h"
#include "common/textconsole.h"
#include "glk/scott/disk_image.h"
namespace Glk {
namespace Scott {
/* dos errors as used by the DOS internally (and as saves in the error info) */
struct DosError {
signed int _number; /* dos error number */
signed int _errnum; /* reported error number */
const char *_string; /* description */
};
DosError g_dosError[] = {
/* non-errors */
{0x01, 0, "ok"},
/* errors */
{0x02, 20, "Header descriptor byte not found (Seek)"},
/* { 0x02, 20, "Header block not found (Seek)" }, */
{0x03, 21, "No SYNC sequence found (Seek)"},
{0x04, 22, "Data descriptor byte not found (Read)"},
{0x05, 23, "Checksum error in data block (Read)"},
{0x06, 24, "Write verify (Write)"},
{0x07, 25, "Write verify error (Write)"},
{0x08, 26, "Write protect on (Write)"},
{0x09, 27, "Checksum error in header block (Seek)"},
{0x0A, 28, "Write error (Write)"},
{0x0B, 29, "Disk sector ID mismatch (Seek)"},
{0x0F, 74, "Drive Not Ready (Read)"},
{-1, -1, nullptr}
};
int setStatus(DiskImage *di, int status, int track, int sector) {
di->_status = status;
di->_statusts._track = track;
di->_statusts._sector = sector;
return status;
}
/* check if given track/sector is within valid range */
int diTsIsValid(ImageType type, TrackSector ts) {
if ((ts._track < 1) || (ts._track > diTracks(type))) {
return 0; /* track out of range */
}
if (ts._sector > (diSectorsPerTrack(type, ts._track) - 1)) {
return 0; /* sector out of range */
}
return 1;
}
int matchPattern(byte *rawPattern, byte *rawname) {
int i;
for (i = 0; i < 16; ++i) {
if (rawPattern[i] == '*') {
return 1;
}
if (rawname[i] == 0xa0) {
if (rawPattern[i] == 0xa0) {
return 1;
} else {
return 0;
}
} else {
if (rawPattern[i] == '?' || rawPattern[i] == rawname[i]) {
} else {
return 0;
}
}
}
return 1;
}
/* return a pointer to the next block in the chain */
TrackSector nextTsInChain(DiskImage *di, TrackSector ts) {
byte *p;
TrackSector newTs;
p = diGetTsAddr(di, ts);
newTs._track = p[0];
newTs._sector = p[1];
return newTs;
}
RawDirEntry *findFileEntry(DiskImage *di, byte *rawPattern, int type) {
byte *buffer;
TrackSector ts;
RawDirEntry *rde;
int offset;
ts = diGetDirTs(di);
while (ts._track) {
buffer = diGetTsAddr(di, ts);
for (offset = 0; offset < 256; offset += 32) {
rde = (RawDirEntry *)(buffer + offset);
if (matchPattern(rawPattern, rde->_rawname)) {
return rde;
}
}
/* todo: add sanity checking */
ts = nextTsInChain(di, ts);
}
return nullptr;
}
/* allocate next available directory block */
TrackSector allocNextDirTs(DiskImage *di) {
byte *p;
int spt;
TrackSector ts, lastTs;
if (diTrackBlocksFree(di, di->_bam._track)) {
lastTs._track = di->_bam._track;
lastTs._sector = 0;
if ((di->_type == D64) || (di->_type == D71)) {
ts._track = 18;
ts._sector = 1;
} else {
ts = nextTsInChain(di, lastTs);
}
while (ts._track) {
lastTs = ts;
ts = nextTsInChain(di, ts);
}
ts._track = lastTs._track;
ts._sector = lastTs._sector + 3;
spt = diSectorsPerTrack(di->_type, ts._track);
for (;; ts._sector = (ts._sector + 1) % spt) {
if (diIsTsFree(di, ts)) {
diAllocTs(di, ts);
p = diGetTsAddr(di, lastTs);
p[0] = ts._track;
p[1] = ts._sector;
p = diGetTsAddr(di, ts);
memset(p, 0, 256);
p[1] = 0xff;
di->_modified = 1;
return ts;
}
}
} else {
ts._track = 0;
ts._sector = 0;
return ts;
}
}
RawDirEntry *allocFileEntry(DiskImage *di, byte *rawName, int type) {
byte *buffer;
TrackSector ts;
RawDirEntry *rde;
int offset;
/* check if file already exists */
ts = nextTsInChain(di, di->_bam);
while (ts._track) {
buffer = diGetTsAddr(di, ts);
for (offset = 0; offset < 256; offset += 32) {
rde = (RawDirEntry *)(buffer + offset);
if (rde->_type) {
if (scumm_strnicmp((char *)rawName, (char *)rde->_rawname, 16) == 0) {
setStatus(di, 63, 0, 0);
return nullptr;
}
}
}
ts = nextTsInChain(di, ts);
}
/* allocate empty slot */
ts = nextTsInChain(di, di->_bam);
while (ts._track) {
buffer = diGetTsAddr(di, ts);
for (offset = 0; offset < 256; offset += 32) {
rde = (RawDirEntry *)(buffer + offset);
if (rde->_type == 0) {
memset((byte *)rde + 2, 0, 30);
memcpy(rde->_rawname, rawName, 16);
rde->_type = type;
di->_modified = 1;
return rde;
}
}
ts = nextTsInChain(di, ts);
}
/* allocate new dir block */
ts = allocNextDirTs(di);
if (ts._track) {
rde = (RawDirEntry *)diGetTsAddr(di, ts);
memset((byte *)rde + 2, 0, 30);
memcpy(rde->_rawname, rawName, 16);
rde->_type = type;
di->_modified = 1;
return rde;
} else {
setStatus(di, 72, 0, 0);
return nullptr;
}
}
ImageFile *diOpen(DiskImage *di, byte *rawname, byte type, const char *mode) {
ImageFile *imgFile;
RawDirEntry *rde;
byte *p;
setStatus(di, 255, 0, 0);
if (scumm_stricmp("rb", mode) == 0) {
if ((imgFile = new ImageFile) == nullptr) {
return nullptr;
}
memset(imgFile->_visited, 0, sizeof(imgFile->_visited));
if (scumm_stricmp("$", (char *)rawname) == 0) {
imgFile->_mode = 'r';
imgFile->_ts = di->_dir;
p = diGetTsAddr(di, di->_dir);
imgFile->_buffer = p + 2;
imgFile->_nextts = diGetDirTs(di);
imgFile->_buflen = 254;
if (!diTsIsValid(di->_type, imgFile->_nextts)) {
setStatus(di, 66, imgFile->_nextts._track, imgFile->_nextts._sector);
delete imgFile;
return nullptr;
}
rde = nullptr;
} else {
if ((rde = findFileEntry(di, rawname, type)) == nullptr) {
setStatus(di, 62, 0, 0);
delete imgFile;
return nullptr;
}
imgFile->_mode = 'r';
imgFile->_ts = rde->_startts;
if (!diTsIsValid(di->_type, imgFile->_ts)) {
setStatus(di, 66, imgFile->_ts._track, imgFile->_ts._sector);
delete imgFile;
return nullptr;
}
p = diGetTsAddr(di, rde->_startts);
imgFile->_buffer = p + 2;
imgFile->_nextts._track = p[0];
imgFile->_nextts._sector = p[1];
if (imgFile->_nextts._track == 0) {
if (imgFile->_nextts._sector != 0) {
imgFile->_buflen = imgFile->_nextts._sector - 1;
} else {
imgFile->_buflen = 254;
}
} else {
if (!diTsIsValid(di->_type, imgFile->_nextts)) {
setStatus(di, 66, imgFile->_nextts._track, imgFile->_nextts._sector);
delete imgFile;
return nullptr;
}
imgFile->_buflen = 254;
}
}
} else if (strcmp("wb", mode) == 0) {
if ((rde = allocFileEntry(di, rawname, type)) == nullptr) {
return nullptr;
}
if ((imgFile = new ImageFile) == nullptr) {
return nullptr;
}
imgFile->_mode = 'w';
imgFile->_ts._track = 0;
imgFile->_ts._sector = 0;
if ((imgFile->_buffer = new byte[254]) == nullptr) {
delete imgFile;
return nullptr;
}
imgFile->_buflen = 254;
di->_modified = 1;
} else {
return nullptr;
}
imgFile->_diskimage = di;
imgFile->_rawdirentry = rde;
imgFile->_position = 0;
imgFile->_bufptr = 0;
++(di->_openfiles);
setStatus(di, 0, 0, 0);
return imgFile;
}
int diRead(ImageFile *imgFile, byte *buffer, int len) {
byte *p;
int bytesLeft;
int counter = 0;
int err;
while (len) {
bytesLeft = imgFile->_buflen - imgFile->_bufptr;
err = diGetTsErr(imgFile->_diskimage, imgFile->_ts);
if (err) {
setStatus(imgFile->_diskimage, err, imgFile->_ts._track, imgFile->_ts._sector);
return counter;
}
if (bytesLeft == 0) {
if (imgFile->_nextts._track == 0) {
return counter;
}
if (((imgFile->_diskimage->_type == D64) || (imgFile->_diskimage->_type == D71)) && imgFile->_ts._track == 18 && imgFile->_ts._sector == 0) {
imgFile->_ts._track = 18;
imgFile->_ts._sector = 1;
} else {
imgFile->_ts = nextTsInChain(imgFile->_diskimage, imgFile->_ts);
}
if (imgFile->_ts._track == 0) {
return counter;
}
/* check for cyclic files */
if (imgFile->_visited[imgFile->_ts._track][imgFile->_ts._sector]) {
/* return 52, file too long error */
setStatus(imgFile->_diskimage, 52, imgFile->_ts._track, imgFile->_ts._sector);
} else {
imgFile->_visited[imgFile->_ts._track][imgFile->_ts._sector] = 1;
}
err = diGetTsErr(imgFile->_diskimage, imgFile->_ts);
if (err) {
setStatus(imgFile->_diskimage, err, imgFile->_ts._track, imgFile->_ts._sector);
return counter;
}
p = diGetTsAddr(imgFile->_diskimage, imgFile->_ts);
imgFile->_buffer = p + 2;
imgFile->_nextts._track = p[0];
imgFile->_nextts._sector = p[1];
if (imgFile->_nextts._track == 0) {
if (imgFile->_nextts._sector == 0) {
/* fixme, something is wrong if this happens, should be a proper error */
imgFile->_buflen = 0;
setStatus(imgFile->_diskimage, -1, imgFile->_ts._track, imgFile->_ts._sector);
} else {
imgFile->_buflen = imgFile->_nextts._sector - 1;
}
} else {
if (!diTsIsValid(imgFile->_diskimage->_type, imgFile->_nextts)) {
setStatus(imgFile->_diskimage, 66, imgFile->_nextts._track, imgFile->_nextts._sector);
return counter;
}
imgFile->_buflen = 254;
}
imgFile->_bufptr = 0;
} else {
if (len >= bytesLeft) {
while (bytesLeft) {
*buffer++ = imgFile->_buffer[imgFile->_bufptr++];
--len;
--bytesLeft;
++counter;
++(imgFile->_position);
}
} else {
while (len) {
*buffer++ = imgFile->_buffer[imgFile->_bufptr++];
--len;
--bytesLeft;
++counter;
++(imgFile->_position);
}
}
}
}
return counter;
}
// get a pointer to block data
byte *diGetTsAddr(DiskImage *di, TrackSector ts) {
return di->_image + diGetBlockNum(di->_type, ts) * 256;
}
/* get error info for a sector */
int getTsDosErr(DiskImage *di, TrackSector ts) {
// return 1;
if (di->_errinfo == nullptr) {
return 1; /* return OK if image has no error info */
}
return di->_errinfo[diGetBlockNum(di->_type, ts)];
}
int diGetTsErr(DiskImage *di, TrackSector ts) {
int errnum;
DosError *err = g_dosError;
errnum = getTsDosErr(di, ts);
while (err->_number >= 0) {
if (errnum == err->_number) {
return err->_errnum;
}
++err;
}
return -1; /* unknown error */
}
/* return disk geometry for track */
int diSectorsPerTrack(ImageType type, int track) {
switch (type) {
case D71:
if (track > 35) {
track -= 35;
}
/* fall through */
case D64:
if (track < 18) {
return 21;
} else if (track < 25) {
return 19;
} else if (track < 31) {
return 18;
} else {
return 17;
}
break;
case D81:
return 40;
break;
}
return 0;
}
/* return number of tracks for image type */
int diTracks(ImageType type) {
switch (type) {
case D64:
return 35;
break;
case D71:
return 70;
break;
case D81:
return 80;
break;
}
return 0;
}
int diGetBlockNum(ImageType type, TrackSector ts) {
int block;
/* assertion, should never happen (indicates bad error handling elsewhere) */
if (!diTsIsValid(type, ts)) {
error("diGetBlockNum: internal error, track/sector out of range");
}
switch (type) {
case D64:
if (ts._track < 18) {
block = (ts._track - 1) * 21;
} else if (ts._track < 25) {
block = (ts._track - 18) * 19 + 17 * 21;
} else if (ts._track < 31) {
block = (ts._track - 25) * 18 + 17 * 21 + 7 * 19;
} else {
block = (ts._track - 31) * 17 + 17 * 21 + 7 * 19 + 6 * 18;
}
return block + ts._sector;
break;
case D71:
if (ts._track > 35) {
block = 683;
ts._track -= 35;
} else {
block = 0;
}
if (ts._track < 18) {
block += (ts._track - 1) * 21;
} else if (ts._track < 25) {
block += (ts._track - 18) * 19 + 17 * 21;
} else if (ts._track < 31) {
block += (ts._track - 25) * 18 + 17 * 21 + 7 * 19;
} else {
block += (ts._track - 31) * 17 + 17 * 21 + 7 * 19 + 6 * 18;
}
return block + ts._sector;
break;
case D81:
return (ts._track - 1) * 40 + ts._sector;
break;
}
return 0;
}
/* return t/s of first directory sector */
TrackSector diGetDirTs(DiskImage *di) {
TrackSector newTs;
byte *p;
p = diGetTsAddr(di, di->_dir);
if ((di->_type == D64) || (di->_type == D71)) {
newTs._track = 18; /* 1541/71 ignores bam t/s link */
newTs._sector = 1;
} else {
newTs._track = p[0];
newTs._sector = p[1];
}
return newTs;
}
/* return number of free blocks in track */
int diTrackBlocksFree(DiskImage *di, int track) {
byte *bam;
switch (di->_type) {
default:
case D64:
bam = diGetTsAddr(di, di->_bam);
break;
case D71:
bam = diGetTsAddr(di, di->_bam);
if (track >= 36) {
return bam[track + 185];
}
break;
case D81:
if (track <= 40) {
bam = diGetTsAddr(di, di->_bam);
} else {
bam = diGetTsAddr(di, di->_bam2);
track -= 40;
}
return bam[track * 6 + 10];
break;
}
return bam[track * 4];
}
/* check if track, sector is free in BAM */
int diIsTsFree(DiskImage *di, TrackSector ts) {
byte mask;
byte *bam;
switch (di->_type) {
case D64:
bam = diGetTsAddr(di, di->_bam);
if (bam[ts._track * 4]) {
mask = 1 << (ts._sector & 7);
return bam[ts._track * 4 + ts._sector / 8 + 1] & mask ? 1 : 0;
} else {
return 0;
}
break;
case D71:
mask = 1 << (ts._sector & 7);
if (ts._track < 36) {
bam = diGetTsAddr(di, di->_bam);
return bam[ts._track * 4 + ts._sector / 8 + 1] & mask ? 1 : 0;
} else {
bam = diGetTsAddr(di, di->_bam2);
return bam[(ts._track - 35) * 3 + ts._sector / 8 - 3] & mask ? 1 : 0;
}
break;
case D81:
mask = 1 << (ts._sector & 7);
if (ts._track < 41) {
bam = diGetTsAddr(di, di->_bam);
} else {
bam = diGetTsAddr(di, di->_bam2);
ts._track -= 40;
}
return bam[ts._track * 6 + ts._sector / 8 + 11] & mask ? 1 : 0;
break;
}
return 0;
}
/* allocate track, sector in BAM */
void diAllocTs(DiskImage *di, TrackSector ts) {
byte mask;
byte *bam;
di->_modified = 1;
switch (di->_type) {
case D64:
bam = diGetTsAddr(di, di->_bam);
bam[ts._track * 4] -= 1;
mask = 1 << (ts._sector & 7);
bam[ts._track * 4 + ts._sector / 8 + 1] &= ~mask;
break;
case D71:
mask = 1 << (ts._sector & 7);
if (ts._track < 36) {
bam = diGetTsAddr(di, di->_bam);
bam[ts._track * 4] -= 1;
bam[ts._track * 4 + ts._sector / 8 + 1] &= ~mask;
} else {
bam = diGetTsAddr(di, di->_bam);
bam[ts._track + 185] -= 1;
bam = diGetTsAddr(di, di->_bam2);
bam[(ts._track - 35) * 3 + ts._sector / 8 - 3] &= ~mask;
}
break;
case D81:
if (ts._track < 41) {
bam = diGetTsAddr(di, di->_bam);
} else {
bam = diGetTsAddr(di, di->_bam2);
ts._track -= 40;
}
bam[ts._track * 6 + 10] -= 1;
mask = 1 << (ts._sector & 7);
bam[ts._track * 6 + ts._sector / 8 + 11] &= ~mask;
break;
}
}
/* convert to rawname */
int diRawnameFromName(byte *rawname, const char *name) {
int i;
memset(rawname, 0xa0, 16);
for (i = 0; i < 16 && name[i]; ++i)
rawname[i] = name[i];
return i;
}
/* count number of free blocks */
int blocksFree(DiskImage *di) {
int blocks = 0;
for (int track = 1; track <= diTracks(di->_type); ++track) {
if (track != di->_dir._track) {
blocks += diTrackBlocksFree(di, track);
}
}
return blocks;
}
/* return write interleave */
int interleave(ImageType type) {
switch (type) {
case D64:
return 10;
break;
case D71:
return 6;
break;
default:
return 1;
break;
}
}
RawDirEntry *findLargestFileEntry(DiskImage *di) {
byte *buffer;
TrackSector ts;
RawDirEntry *rde, *largest = nullptr;
int offset, largestSize = 0;
ts = diGetDirTs(di);
while (ts._track) {
buffer = diGetTsAddr(di, ts);
for (offset = 0; offset < 256; offset += 32) {
rde = (RawDirEntry *)(buffer + offset);
int size = rde->_sizelo + rde->_sizehi * 0x100;
if (size > largestSize) {
largest = rde;
largestSize = size;
}
}
/* todo: add sanity checking */
ts = nextTsInChain(di, ts);
}
return largest;
}
DiskImage *diCreateFromData(uint8_t *data, int length) {
DiskImage *di;
if (data == nullptr)
return nullptr;
if ((di = new DiskImage) == nullptr) {
return nullptr;
}
di->_size = length;
di->_image = data;
di->_errinfo = nullptr;
/* check image type */
switch (length) {
case D64ERRSIZE: /* D64 with error info */
// di->_errinfo = &(di->_error_);
// fallthrough
case D64SIZE: /* standard D64 */
di->_type = D64;
di->_bam._track = 18;
di->_bam._sector = 0;
di->_dir = di->_bam;
break;
case D71ERRSIZE: /* D71 with error info */
di->_errinfo = &(di->_image[D71SIZE]);
// fallthrough
case D71SIZE:
di->_type = D71;
di->_bam._track = 18;
di->_bam._sector = 0;
di->_bam2._track = 53;
di->_bam2._sector = 0;
di->_dir = di->_bam;
break;
case D81ERRSIZE: /* D81 with error info */
di->_errinfo = &(di->_image[D81SIZE]);
// fallthrough
case D81SIZE:
di->_type = D81;
di->_bam._track = 40;
di->_bam._sector = 1;
di->_bam2._track = 40;
di->_bam2._sector = 2;
di->_dir._track = 40;
di->_dir._sector = 0;
break;
default:
delete di;
return nullptr;
}
di->_filename = nullptr;
di->_openfiles = 0;
di->_blocksfree = blocksFree(di);
di->_modified = 0;
di->_interleave = interleave(di->_type);
setStatus(di, 254, 0, 0);
return di;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,144 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* https://paradroid.automac.se/diskimage/
* Copyright (c) 2003-2006, Per Olofsson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef GLK_SCOTT_DISKIMAGE_H
#define GLK_SCOTT_DISKIMAGE_H
#include "common/scummsys.h"
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
// constants for the supported disk formats
#define MAXTRACKS 80
#define MAXSECTORS 40
#define D64SIZE 174848
#define D64ERRSIZE 175531
#define D71SIZE 349696
#define D71ERRSIZE 351062
#define D81SIZE 819200
#define D81ERRSIZE 822400
enum ImageType {
D64 = 1,
D71,
D81
};
struct TrackSector {
byte _track;
byte _sector;
};
struct DiskImage {
char *_filename;
int _size;
ImageType _type;
byte *_image;
byte *_errinfo;
TrackSector _bam;
TrackSector _bam2;
TrackSector _dir;
int _openfiles;
int _blocksfree;
int _modified;
int _status;
int _interleave;
TrackSector _statusts;
};
struct RawDirEntry {
TrackSector _nextts;
byte _type;
TrackSector _startts;
byte _rawname[16];
TrackSector _relsidets;
byte _relrecsize;
byte _unused[4];
TrackSector _replacetemp;
byte _sizelo;
byte _sizehi;
};
struct ImageFile {
DiskImage *_diskimage;
RawDirEntry *_rawdirentry;
char _mode;
int _position;
TrackSector _ts;
TrackSector _nextts;
byte *_buffer;
int _bufptr;
int _buflen;
byte _visited[MAXTRACKS][MAXSECTORS];
};
ImageFile *diOpen(DiskImage *di, byte *rawname, byte type, const char *mode);
int diRead(ImageFile *imgfile, byte *buffer, int len);
byte *diGetTsAddr(DiskImage *di, TrackSector ts);
int diGetTsErr(DiskImage *di, TrackSector ts);
int diSectorsPerTrack(ImageType type, int track);
int diTracks(ImageType type);
int diGetBlockNum(ImageType type, TrackSector ts);
TrackSector diGetDirTs(DiskImage *di);
int diTrackBlocksFree(DiskImage *di, int track);
int diIsTsFree(DiskImage *di, TrackSector ts);
void diAllocTs(DiskImage *di, TrackSector ts);
int diRawnameFromName(byte *rawname, const char *name);
RawDirEntry *findLargestFileEntry(DiskImage *di);
DiskImage *diCreateFromData(uint8_t *data, int length);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,282 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "common/array.h"
#include "common/str.h"
#include "common/str-array.h"
#include "glk/scott/definitions.h"
namespace Glk {
namespace Scott {
/* This is supposed to be the original ScottFree system
messages in second person, as far as possible */
const char *g_sysDict[] = {
"North",
"South",
"East",
"West",
"Up",
"Down",
"The game is now over. Play again?",
"You have stored",
"treasures. ",
"On a scale of 0 to 100 that rates",
"O.K.",
"O.K.",
"O.K. ",
"Well done.\n",
"I don't understand ",
"You can't do that yet. ",
"Huh ? ",
"Give me a direction too. ",
"You haven't got it. ",
"You have it. ",
"You don't see it. ",
"It is beyond your power to do that. ",
"\nDangerous to move in the dark! ",
"You fell down and broke your neck. ",
"You can't go in that direction. ",
"I don't know how to \"",
"\" something. ",
"I don't know what a \"",
"\" is. ",
"You can't see. It is too dark!\n",
"You are in a ",
"\nYou can also see: ",
"Obvious exits: ",
"You are carrying:\n",
"Nothing.\n",
"Tell me what to do ? ",
"<HIT ENTER>",
"Light has run out. ",
"Light runs out in",
"turns! ",
"You are carrying too much. \n",
"You're dead. ",
"Resume a saved game? ",
"None",
"There's nothing here to take. ",
"You carry nothing. ",
"Your light is growing dim. ",
", ",
"\n",
" - ",
"What ?",
"yes",
"no",
"Answer yes or no.\n",
"Are you sure? ",
"Move undone. ",
"Can't undo on first turn. ",
"No more undo states stored. ",
"Saved. ",
"You can't use ALL with that verb. ",
"Transcript is now off.\n",
"Transcript is now on.\n",
"No transcript is currently running.\n",
"A transcript is already running.\n",
"Failed to create transcript file. ",
"Start of transcript\n\n",
"\n\nEnd of transcript\n",
"BAD DATA! Invalid save file.\n",
"State saved.\n",
"State restored.\n",
"No saved state exists.\n"
};
/* These are supposed to be the original ScottFree system
messages in first person, as far as possible */
const char *g_sysDictIAm[] = {
"North",
"South",
"East",
"West",
"Up",
"Down",
"The game is now over. Play again?",
"You have stored",
"treasures. ",
"On a scale of 0 to 100 that rates",
"O.K.",
"O.K.",
"O.K. ",
"Well done.\n",
"I don't understand ",
"I can't do that yet. ",
"Huh ? ",
"Give me a direction too.",
"I'm not carrying it. ",
"I already have it. ",
"I don't see it here. ",
"It is beyond my power to do that. ",
"Dangerous to move in the dark! ",
"\nI fell and broke my neck.",
"I can't go in that direction. ",
"I don't know how to \"",
"\" something. ",
"I don't know what a \"",
"\" is. ",
"I can't see. It is too dark!\n",
"I'm in a ",
"\nI can also see: ",
"Obvious exits: ",
"I'm carrying: \n",
"Nothing.\n",
"Tell me what to do ? ",
"<HIT ENTER>",
"Light has run out. ",
"Light runs out in",
"turns! ",
"I've too much to carry. \n",
"I'm dead. ",
"Resume a saved game? ",
"None",
"There's nothing here to take. ",
"I have nothing to drop. ",
"My light is growing dim. ",
nullptr
};
/* These are supposed to be the original TI-99/4A system
messages in first person, as far as possible */
const char *g_sysDictTI994A[] = {
"North",
"South",
"East",
"West",
"Up",
"Down",
"This adventure is over. Play again?",
"You have stored",
"treasures. ",
"On a scale of 0 to 100 that rates",
"OK. ",
"OK. ",
"OK. ",
"Well done.\n",
"I don't understand the command. ",
"I can't do that yet. ",
"Huh? ",
"Give me a direction too.",
"I'm not carrying it. ",
"I already have it. ",
"I don't see it here. ",
"It is beyond my power to do that. ",
"Dangerous to move in the dark!\n",
"\nI fell down and broke my neck.",
"I can't go in that direction. ",
"I don't know how to \"",
"\" something. ",
"I don't know what a \"",
"\" is. ",
"I can't see. It is too dark!\n",
"I am in a ",
"\nVisible items are : ",
"Obvious exits : ",
"I am carrying : ",
"Nothing. ",
"What shall I do? ",
"<HIT ENTER>",
"Light went out! ",
"Light runs out in",
"turns! ",
"I am carrying too much.\n",
"I'm dead... ",
"Resume a saved game? ",
"None",
"There's nothing here to take. ",
"I have nothing to drop. ",
"Light is growing dim ",
", ",
" ",
", ",
nullptr,
};
const char *g_sysDictZX[] = {
"NORTH",
"SOUTH",
"EAST",
"WEST",
"UP",
"DOWN",
"The Adventure is over. Want to try this Adventure again? ",
"I've stored",
"Treasures. ",
"On a scale of 0 to 100 that rates",
"Dropped.",
"Taken.",
"O.K. ",
"FANTASTIC! You've solved it ALL! \n",
"I must be stupid, but I just don't understand what you mean ",
"I can't do that...yet! ",
"Huh? ",
"I need a direction too. ",
"I'm not carrying it. ",
"I already have it. ",
"I don't see it here. ",
"It's beyond my Power to do that. ",
"It's dangerous to move in the dark! ",
"\nI fell and broke my neck! I'm DEAD! ",
"I can't go in that direction. ",
"I don't know how to \"",
"\" something. ",
"I don't know what a \"",
"\" is. ",
"It's too dark to see!\n",
"I am in a ",
". Visible items:\n",
"Exits: ",
"I'm carrying the following: ",
"Nothing at all. ",
"---TELL ME WHAT TO DO ? ",
"<HIT ENTER> ",
"Light has run out. ",
"Light runs out in",
"turns! ",
"I'm carrying too much! Try: TAKE INVENTORY. ",
"I'm DEAD!! ",
"Restore a previously saved game ? ",
"None",
"There's nothing here to take. ",
"I have nothing to drop. ",
"My light is growing dim. ",
" ",
" ",
". ",
"What ? ",
nullptr
};
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,50 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_GAMEINFO_H
#define GLK_SCOTT_GAMEINFO_H
#include "common/array.h"
#include "glk/scott/definitions.h"
namespace Glk {
namespace Scott {
extern const char *g_sysDict[];
extern const char *g_sysDictIAm[];
extern const char *g_sysDictZX[];
extern const char *g_sysDictTI994A[];
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,366 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "common/str.h"
#include "glk/scott/scott.h"
#include "glk/scott/globals.h"
#include "glk/scott/game_specific.h"
namespace Glk {
namespace Scott {
void mysterious64Sysmess() {
SysMessageType messageKey[] = {
NORTH,
SOUTH,
EAST,
WEST,
UP,
DOWN,
EXITS,
YOU_SEE,
YOU_ARE,
TOO_DARK_TO_SEE,
LIGHT_HAS_RUN_OUT,
LIGHT_RUNS_OUT_IN,
TURNS,
I_DONT_KNOW_HOW_TO,
SOMETHING,
I_DONT_KNOW_WHAT_A,
IS,
YOU_CANT_GO_THAT_WAY,
OK,
WHAT_NOW,
HUH,
YOU_HAVENT_GOT_IT,
INVENTORY,
YOU_DONT_SEE_IT,
THATS_BEYOND_MY_POWER,
DANGEROUS_TO_MOVE_IN_DARK,
DIRECTION,
YOU_FELL_AND_BROKE_YOUR_NECK,
YOURE_CARRYING_TOO_MUCH,
IM_DEAD,
PLAY_AGAIN,
RESUME_A_SAVED_GAME,
IVE_STORED,
TREASURES,
ON_A_SCALE_THAT_RATES,
YOU_CANT_DO_THAT_YET,
I_DONT_UNDERSTAND,
NOTHING,
YOUVE_SOLVED_IT,
YOUVE_SOLVED_IT};
for (int i = 0; i < 40; i++) {
_G(_sys)[messageKey[i]] = _G(_systemMessages)[i];
}
_G(_sys)[ITEM_DELIMITER] = " - ";
_G(_sys)[MESSAGE_DELIMITER] = "\n";
_G(_sys)[YOU_SEE] = "\nThings I can see:\n";
_G(_sys)[I_DONT_KNOW_HOW_TO] = "\"";
_G(_sys)[PLAY_AGAIN] = "The game is over, thanks for playing\nWant to play again ? ";
char *dictword = nullptr;
for (int i = 1; i <= 6; i++) {
dictword = new char[_G(_gameHeader)->_wordLength];
Common::strlcpy(dictword, _G(_sys)[i - 1].c_str(), _G(_gameHeader)->_wordLength);
_G(_nouns)[i] = dictword;
}
_G(_nouns)[0] = "ANY\0";
switch (CURRENT_GAME) {
case BATON_C64:
_G(_nouns)[79] = "CAST\0";
_G(_verbs)[79] = ".\0";
_G(_gameHeader)->_numWords = 79;
break;
case TIME_MACHINE_C64:
_G(_verbs)[86] = ".\0";
break;
case ARROW1_C64:
_G(_nouns)[82] = ".\0";
break;
case ARROW2_C64:
_G(_verbs)[80] = ".\0";
break;
case PULSAR7_C64:
_G(_nouns)[102] = ".\0";
break;
case CIRCUS_C64:
_G(_nouns)[96] = ".\0";
break;
case FEASIBILITY_C64:
_G(_nouns)[80] = ".\0";
break;
case PERSEUS_C64:
_G(_nouns)[82] = ".\0";
break;
default:
break;
}
}
void claymorgue64Sysmess() {
SysMessageType messagekey[] = {
NORTH,
SOUTH,
EAST,
WEST,
UP,
DOWN,
EXITS,
YOU_SEE,
YOU_ARE,
TOO_DARK_TO_SEE,
LIGHT_HAS_RUN_OUT,
LIGHT_RUNS_OUT_IN,
TURNS,
I_DONT_KNOW_HOW_TO,
SOMETHING,
I_DONT_KNOW_WHAT_A,
IS,
YOU_CANT_GO_THAT_WAY,
OK,
WHAT_NOW,
HUH,
YOU_HAVE_IT,
YOU_HAVENT_GOT_IT,
INVENTORY,
YOU_DONT_SEE_IT,
THATS_BEYOND_MY_POWER,
DANGEROUS_TO_MOVE_IN_DARK,
DIRECTION,
YOU_FELL_AND_BROKE_YOUR_NECK,
YOURE_CARRYING_TOO_MUCH,
IM_DEAD,
PLAY_AGAIN,
RESUME_A_SAVED_GAME,
IVE_STORED,
TREASURES,
ON_A_SCALE_THAT_RATES,
YOU_CANT_DO_THAT_YET,
I_DONT_UNDERSTAND,
NOTHING,
YOUVE_SOLVED_IT};
for (int i = 0; i < 40; i++) {
_G(_sys)[messagekey[i]] = _G(_systemMessages)[i];
}
_G(_sys)[I_DONT_KNOW_HOW_TO] = "I don't know how to \"";
_G(_sys)[SOMETHING] = "\" something. ";
}
void adventurelandAction(int p) {
int image = 0;
switch (p) {
case 1:
image = 36;
break;
case 2:
image = 34;
break;
case 3:
image = 33;
break;
case 4:
image = 35;
break;
default:
error("Adventureland: Unsupported action parameter %d", p);
return;
}
g_scott->drawImage(image);
g_scott->output("\n");
g_scott->output(_G(_sys)[HIT_ENTER]);
g_scott->hitEnter();
return;
}
void adventurelandDarkness() {
if ((_G(_rooms)[MY_LOC]._image & 128) == 128)
_G(_bitFlags) |= 1 << DARKBIT;
else
_G(_bitFlags) &= ~(1 << DARKBIT);
}
void adventureland64Sysmess() {
SysMessageType messagekey[] = {
NORTH,
SOUTH,
EAST,
WEST,
UP,
DOWN,
EXITS,
YOU_SEE,
YOU_ARE,
TOO_DARK_TO_SEE,
LIGHT_HAS_RUN_OUT,
LIGHT_RUNS_OUT_IN,
TURNS,
I_DONT_KNOW_HOW_TO,
SOMETHING,
I_DONT_KNOW_WHAT_A,
IS,
YOU_CANT_GO_THAT_WAY,
OK,
WHAT_NOW,
HUH,
YOU_HAVENT_GOT_IT,
INVENTORY,
YOU_DONT_SEE_IT,
THATS_BEYOND_MY_POWER,
DANGEROUS_TO_MOVE_IN_DARK,
DIRECTION,
YOU_FELL_AND_BROKE_YOUR_NECK,
YOURE_CARRYING_TOO_MUCH,
IM_DEAD,
PLAY_AGAIN,
RESUME_A_SAVED_GAME,
IVE_STORED,
TREASURES,
ON_A_SCALE_THAT_RATES,
YOU_CANT_DO_THAT_YET,
I_DONT_UNDERSTAND,
NOTHING,
YOUVE_SOLVED_IT};
for (int i = 0; i < 39; i++) {
_G(_sys)[messagekey[i]] = _G(_systemMessages)[i];
}
_G(_sys)[I_DONT_KNOW_HOW_TO] = "I don't know how to \"";
_G(_sys)[SOMETHING] = "\" something. ";
}
void spiderman64Sysmess() {
SysMessageType messagekey[] = {
NORTH,
SOUTH,
EAST,
WEST,
UP,
DOWN,
EXITS,
YOU_SEE,
YOU_ARE,
TOO_DARK_TO_SEE,
LIGHT_HAS_RUN_OUT,
LIGHT_RUNS_OUT_IN,
TURNS,
I_DONT_KNOW_HOW_TO,
SOMETHING,
I_DONT_KNOW_WHAT_A,
IS,
YOU_CANT_GO_THAT_WAY,
OK,
WHAT_NOW,
HUH,
YOU_HAVE_IT,
YOU_HAVENT_GOT_IT,
INVENTORY,
YOU_DONT_SEE_IT,
THATS_BEYOND_MY_POWER,
DANGEROUS_TO_MOVE_IN_DARK,
DIRECTION,
YOU_FELL_AND_BROKE_YOUR_NECK,
YOURE_CARRYING_TOO_MUCH,
IM_DEAD,
DROPPED,
TAKEN,
PLAY_AGAIN,
RESUME_A_SAVED_GAME,
IVE_STORED,
TREASURES,
ON_A_SCALE_THAT_RATES,
YOU_CANT_DO_THAT_YET,
I_DONT_UNDERSTAND,
NOTHING,
YOUVE_SOLVED_IT};
for (int i = 0; i < 42; i++) {
_G(_sys)[messagekey[i]] = _G(_systemMessages)[i];
}
_G(_sys)[I_DONT_KNOW_HOW_TO] = "I don't know how to \"";
_G(_sys)[SOMETHING] = "\" something. ";
}
void supergran64Sysmess(void) {
SysMessageType messagekey[] = {
NORTH,
SOUTH,
EAST,
WEST,
UP,
DOWN,
EXITS,
YOU_SEE,
YOU_ARE,
I_DONT_KNOW_WHAT_A,
IS,
YOU_CANT_GO_THAT_WAY,
OK,
WHAT_NOW,
HUH,
YOU_HAVE_IT,
TAKEN,
DROPPED,
YOU_HAVENT_GOT_IT,
INVENTORY,
YOU_DONT_SEE_IT,
THATS_BEYOND_MY_POWER,
DIRECTION,
YOURE_CARRYING_TOO_MUCH,
IM_DEAD,
PLAY_AGAIN,
RESUME_A_SAVED_GAME,
YOU_CANT_DO_THAT_YET,
I_DONT_UNDERSTAND,
NOTHING};
for (int i = 0; i < 30; i++) {
_G(_sys)[messagekey[i]] = _G(_systemMessages)[i];
}
_G(_sys)[I_DONT_KNOW_WHAT_A] = "\"";
_G(_sys)[IS] = "\" is a word I don't know. ";
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,50 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_GAMESPECIFIC_H
#define GLK_SCOTT_GAMESPECIFIC_H
namespace Glk {
namespace Scott {
void mysterious64Sysmess();
void claymorgue64Sysmess();
void adventurelandAction(int p);
void adventurelandDarkness();
void adventureland64Sysmess();
void spiderman64Sysmess();
void supergran64Sysmess(void);
} // End of namespace Scott
} // End of namespace Glk
#endif

1994
engines/glk/scott/games.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,162 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "common/algorithm.h"
#include "glk/scott/scott.h"
#include "glk/scott/command_parser.h"
#include "glk/scott/line_drawing.h"
#include "glk/scott/saga_draw.h"
#include "glk/scott/globals.h"
namespace Glk {
namespace Scott {
Globals *g_globals;
Globals::Globals() : _sys(MAX_SYSMESS), _directions(NUMBER_OF_DIRECTIONS), _extraNouns(NUMBER_OF_EXTRA_NOUNS),
_skipList(NUMBER_OF_SKIPPABLE_WORDS), _delimiterList(NUMBER_OF_DELIMITERS), _systemMessages(60),
_vectorState(NO_VECTOR_IMAGE) {
g_globals = this;
_gameHeader = new Header;
_englishDirections = {nullptr, "north", "south", "east", "west", "up", "down", "n", "s", "e", "w", "u", "d", " "};
_extraCommands = {nullptr, "restart", "save", "restore", "load", "transcript", "script", "oops", "undo", "ram",
"ramload", "ramrestore", "ramsave", "except", "but", " ", " ", " ", " ", " "};
_extraCommandsKey = {NO_COMMAND, RESTART, SAVE, RESTORE, RESTORE, SCRIPT, SCRIPT, UNDO, UNDO, RAM,
RAMLOAD, RAMLOAD, RAMSAVE, EXCEPT, EXCEPT, RESTORE, RESTORE, SCRIPT, UNDO, RESTART};
_englishExtraNouns = {nullptr, "game", "story", "on", "off", "load", "restore", "save", "move",
"command", "turn", "all", "everything", "it", " ", " "};
_extraNounsKey = {NO_COMMAND, GAME, GAME, ON, OFF, RAMLOAD, RAMLOAD, RAMSAVE, COMMAND, COMMAND, COMMAND,
ALL, ALL, IT, ON, OFF};
_abbreviations = {nullptr, "i", "l", "x", "z", "q"};
_abbreviationsKey = {nullptr, "inventory", "look", "examine", "wait", "quit"};
_englishSkipList = {nullptr, "at", "to", "in", "into", "the", "a", "an", "my", "quickly",
"carefully", "quietly", "slowly", "violently", "fast", "hard", "now", "room"};
_englishDelimiterList = {nullptr, ",", "and", "then", " "};
_spanishDirections = {nullptr, "norte", "sur", "este", "oeste", "arriba", "abajo", "n", "s", "e", "o", "u", "d", "w"};
_germanDirections = {nullptr, "norden", "sueden", "osten", "westen", "oben", "unten", "n", "s", "o", "w", "u", "d", " "};
_germanExtraCommands = {nullptr, "restart", "save", "restore", "load", "transcript", "script", "oops", "undo", "ram", "ramload", "ramrestore", "ramsave",
"ausser", "bis", "laden", "wiederherstellen", "transkript", "rueckgaengig", "neustarten"};
_spanishExtraCommands = {nullptr, "restart", "save", "restore", "load", "transcript", "script", "oops", "undo", "ram", "ramload", "ramrestore", "ramsave",
"excepto", "menos", "reanuda", "cargar", "transcripcion", "deshacer", "reinicia"};
_germanExtraNouns = {nullptr, "spiel", "story", "on", "off", "wiederherstellen", "laden", "speichern", "move", "verschieben", "runde",
"alle", "alles", "es", "einschalten", "ausschalten"};
_spanishExtraNouns = {nullptr, "juego", "story", "on", "off", "cargar", "reanuda", "conserva", "move", "command", "jugada", "toda", "todo", "eso", "activar", "desactivar"};
_germanSkipList = {nullptr, "nach", "die", "der", "das", "im", "mein", "meine", "an", "auf", "den", "lassen", "lass", "fallen", " ", " ", " ", " "};
_germanDelimiterList = {nullptr, ",", "und", "dann", "and"};
Common::fill(&_counters[0], &_counters[16], 0);
Common::fill(&_roomSaved[0], &_roomSaved[16], 0);
_md5Index.setVal("cb7dadc9d5f8bce453b9139265e4dd7d", 0); // goldenbaton
_md5Index.setVal("b22d1f4d46c99ff4443d541d3fe424c1", 2); // timemachine
_md5Index.setVal("3a5c3f4079c1c0347f03420db8ad4596", 4); // arrowofdeath1
_md5Index.setVal("d3f8943c4f5f71ce00139065055a72ee", 6); // arrowofdeath2
_md5Index.setVal("441edd90fc7f9ff39a5eebe035a974e9", 8); // pulsar7
_md5Index.setVal("ed99306a2fb23bf6579068a4d74034ee", 10); // circus
_md5Index.setVal("5e381e83f15d77e3542be4a4cffc8e25", 12); // feasibility
_md5Index.setVal("b0f8676817475753f1edd7f1eeea31fb", 14); // akyrz
_md5Index.setVal("84d5fbb16a37e495abf09d191fd8b1a2", 16); // perseus
_md5Index.setVal("afde056c152de79ea20453c42a2d08af", 18); // 10indians
_md5Index.setVal("6c6fbbbb50032463a6ea71c6750ea1f5", 20); // waxworks11
_md5Index.setVal("0eec511d3cde815c73e5464ab0cdbef9" ,22); // marveladventure
_md5Index.setVal("b4d8fc4eabed4f2400717303561ad0fa", 0); // misadv1
_md5Index.setVal("3ce5ea1a0473244bf469fd3c51f1dc48", 6); // midadv2
_md5Index.setVal("10109d9776b9372f9c768b53a664b113", 12); // robin of sherwood
_md5Index.setVal("4262f85382d1bc3b8924a1929511a558", 13); // robin of sherwood
_md5Index.setVal("bf3a4d72cff5ef97bebce6b12c756df2", 14); // robin of sherwood
_md5Index.setVal("552c95ec15d750cbfa02c1f11dcbca1e", 15); // robin of sherwood
_md5Index.setVal("108063b2a16a199794f2ecf52ce26377", 16); // gremlins
_md5Index.setVal("947e35037cf02269ac013857925137ce", 17); // gremlins
_md5Index.setVal("95b2582a89c59961d5b709c9b32e4131", 18); // gremlins
_md5Index.setVal("33c920f7ba150dfa1a832d510eebd8fe", 19); // gremlins
_md5Index.setVal("644c366956202d41df0ea1c4303c5895", 20); // gremlins
_md5Index.setVal("e5d743d8727c8aca011a737bbb5ad818", 21); // gremlins
_md5Index.setVal("c60977950ff22ae57483f073345b8373", 22); // gremlins
_md5Index.setVal("46280fb1d701a41492b8434467c31029", 23); // seas of blood
_md5Index.setVal("267c3fe2bb150365de0358f07b5df15c", 24); // seas of blood
_md5Index.setVal("0300c2d21289157539bbd03ab4e366ee", 25); // seas of blood
_md5Index.setVal("a1db488c49ad221fa0dc79591cb5a3db", 26); // sorcerer of claymorgue castle
_md5Index.setVal("dde67117a432420ef05f8d665fbbbe10", 27); // sorcerer of claymorgue castle
_md5Index.setVal("1ebaf9a378355246aa7ed2623bb27fab", 28); // sorcerer of claymorgue castle
_md5Index.setVal("8d2af429e53df1c4da0d21bdc9de6826", 29); // sorcerer of claymorgue castle
_md5Index.setVal("64fcee173adecc0f03c595e25d4def04", 30); // sorcerer of claymorgue castle
_md5Index.setVal("05cf6c64ecde5288ae2e46099bfd19a3", 31); // adventureland
_md5Index.setVal("5af919881417920ec6a3961b4577f587", 32); // adventureland
_md5Index.setVal("ccd3e3c805134b4fc36ad92e1cae623f", 33); // adventureland
_md5Index.setVal("6c3de0b0ef39fad9d63e788de8cd972c", 34); // adventureland
_md5Index.setVal("547036c586bfcd53e741ecfad74e3001", 35); // adventureland
_md5Index.setVal("f0087b1f42ea9a0656462bf339278b08", 36); // savage island
_md5Index.setVal("414d459ceb211230356ad823475866b3", 38); // savage island
_md5Index.setVal("d80e133dd396565f773052cb317e8222", 40); // questprobe hulk
_md5Index.setVal("5d0ea85ca1f260ca718a6bbb6da4cdb9", 41); // questprobe spiderman
_md5Index.setVal("3d88539a6dd7e6e179bb61041125cc0f", 42); // questprobe spiderman
_md5Index.setVal("f2711fe0376442f6f320da1b73b5b1a3", 43); // golden baton
_md5Index.setVal("ed22cb234af638e7d9f570b937f9fc52", 11); // golden baton
_md5Index.setVal("cc8e94d3fb665d5d23b728e9c1f262ba", 46); // time machine
_md5Index.setVal("da044c4c57dc194002ba47f5b2921411", 47); // arrow of death 1
_md5Index.setVal("3d40cb011167e7ae9f6695cdd1f4a1bf", 48); // arrow of death 2
_md5Index.setVal("db68753e4c4c536693edca2f58747044", 49); // pulsar 7
_md5Index.setVal("4f732cb27e2a0bb484443a7dd1974ccf", 50); // circus
_md5Index.setVal("6c7ed2fd5f0247a55beb266344967662", 51); // feasibility
_md5Index.setVal("94e4b070e45204b12d1655091c56045d", 53); // akyrz
_md5Index.setVal("c423ad31ab3f6b45f0215c2e7fc3eb7e", 52); // akyrz
_md5Index.setVal("96a1ccb3212808eee03e74cdc1f0d1a4", 54); // perseus
_md5Index.setVal("79ee3669ccfff7338dfc1810228005dc", 55); // 10 indians
_md5Index.setVal("facc11aa8b51e88a807236b765203eb0", 56); // waxworks11
_md5Index.setVal("36a5b1b2afb38902933856b3446d760e", 57); // super gran
}
Globals::~Globals() {
delete _gameHeader;
}
} // End of namespace Scott
} // End of namespace Glk

236
engines/glk/scott/globals.h Normal file
View File

@@ -0,0 +1,236 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_GLOBALS_H
#define GLK_SCOTT_GLOBALS_H
#include "common/array.h"
#include "common/str.h"
#include "common/str-array.h"
#include "common/hashmap.h"
#include "glk/glk_types.h"
#include "glk/windows.h"
#include "glk/scott/definitions.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
namespace Glk {
namespace Scott {
struct Command;
enum ExtraCommand : int;
struct LineImage;
enum VectorStateType : int;
struct PixelToDraw;
struct Image;
typedef uint8_t RGB[3];
typedef RGB PALETTE[16];
struct Header;
struct Item;
struct Room;
struct Action;
struct SavedState;
class Globals {
public:
// scott
Header *_gameHeader;
Common::Array<Item> _items;
Common::Array<Room> _rooms;
Common::StringArray _verbs;
Common::StringArray _nouns;
Common::StringArray _messages;
Common::Array<Action> _actions;
Common::StringArray _sys;
Common::StringArray _systemMessages;
winid_t _graphics = nullptr;
uint8_t *_entireFile = nullptr;
size_t _fileLength = 0;
strid_t _roomDescriptionStream = nullptr;
int _fileBaselineOffset = 0;
int _header[24];
int _lightRefill = 0;
winid_t _bottomWindow = nullptr, _topWindow = nullptr;
Command *_currentCommand = nullptr;
int _justStarted = 1;
int _shouldRestart = 0;
int _stopTime = 0;
strid_t _transcript = nullptr;
int _counters[16]; ///< Range unknown
int _currentCounter = 0;
int _savedRoom = 0;
int _roomSaved[16]; ///< Range unknown
uint32 _bitFlags = 0; ///< Might be >32 flags - I haven't seen >32 yet
int _autoInventory = 0;
int _animationFlag = 0;
uint8_t _enemyTable[126];
const char *_battleMessages[33];
int _options = 0; ///< Option flags set
Common::String _titleScreen;
int _shouldLookInTranscript = 0;
// sagadraw
int _drawToBuffer = 0;
uint8_t _sprite[256][8];
uint8_t _screenchars[768][8];
uint8_t _buffer[384][9];
Common::Array<Image> _images;
int _pixelSize = 0;
int _xOffset = 0;
PALETTE _pal;
int _whiteColour = 15;
int _blueColour = 9;
glui32 _diceColour = 0xFF0000;
int32_t _errorCount = 0;
PaletteType _palChosen = NO_PALETTE;
size_t _hulkCoordinates = 0x26DB;
size_t _hulkItemImageOffsets = 0x2798;
size_t _hulkLookImageOffsets = 0x27BC;
size_t _hulkSpecialImageOffsets = 0x276E;
size_t _hulkImageOffset = 0x441B;
// line_drawing
Common::Array<LineImage> _lineImages;
VectorStateType _vectorState;
PixelToDraw **_pixelsToDraw = nullptr;
int _totalDrawInstructions = 0;
int _currentDrawInstruction = 0;
int _vectorImageShown = -1;
uint8_t *_pictureBitmap = nullptr;
int _lineColour = 15;
int _bgColour = 0;
int _scottGraphicsWidth = 255;
int _scottGraphicsHeight = 94;
// connect
int _gliSlowDraw = 0;
// parser
int _lastNoun = 0;
glui32 *_firstErrorMessage = nullptr;
glui32 **_unicodeWords = nullptr;
char **_charWords = nullptr;
int _wordsInInput = 0;
Common::StringArray _directions;
Common::StringArray _englishDirections;
Common::StringArray _skipList;
Common::StringArray _englishSkipList;
Common::StringArray _delimiterList;
Common::StringArray _englishDelimiterList;
Common::StringArray _extraCommands;
Common::StringArray _extraNouns;
Common::StringArray _englishExtraNouns;
Common::Array<ExtraCommand> _extraNounsKey;
Common::Array<ExtraCommand> _extraCommandsKey;
Common::StringArray _abbreviations;
Common::StringArray _abbreviationsKey;
Common::StringArray _spanishDirections;
Common::StringArray _germanDirections;
Common::StringArray _germanExtraCommands;
Common::StringArray _spanishExtraCommands;
Common::StringArray _germanExtraNouns;
Common::StringArray _spanishExtraNouns;
Common::StringArray _germanSkipList;
Common::StringArray _germanDelimiterList;
// restore state
int _justUndid = 0;
SavedState *_initialState = nullptr;
SavedState *_ramSave = nullptr;
SavedState *_lastUndo = nullptr;
SavedState *_oldestUndo = nullptr;
int _numberOfUndos = 0;
const GameInfo *_game;
GameInfo _fallbackGame;
// Include game list
#include "glk/scott/games.h"
// detect game
Common::HashMap<Common::String, int> _md5Index;
// unp64
UnpStr _unp;
int _parsePar = 1;
int _iter = 0;
// 6502 emu
int _byted011[2] = {0, 0};
int _retfire = 0xff;
int _retspace = 0xff;
// robin of sherwood]
uint8_t *_forestImages = nullptr;
// seas of blood
winid_t _leftDiceWin = nullptr;
winid_t _rightDiceWin = nullptr;
winid_t _battleRight = nullptr;
uint _backgroundColour = 0;
uint8_t *_bloodImageData = nullptr;
glui32 _dicePixelSize = 0;
glui32 _diceXOffset = 0;
glui32 _diceYOffset = 0;
int _shouldDrawObjectImages = 0;
// savage island
uint8_t *_saveIslandAppendix1 = nullptr;
int _saveIslandAppendix1Length = 0;
uint8_t *_saveIslandAppendix2 = nullptr;
int _saveIslandAppendix2Length = 0;
// load TI994A
int _maxMessages = 0;
int _maxItemDescr = 0;
size_t _ti99ImplicitExtent = 0;
size_t _ti99ExplicitExtent = 0;
uint8_t *_ti99ImplicitActions = nullptr;
uint8_t *_ti99ExplicitActions = nullptr;
uint8_t **_verbActionOffsets = nullptr;
public:
Globals();
~Globals();
};
extern Globals *g_globals;
#define _G(FIELD) (g_globals->FIELD)
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,335 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/scott.h"
#include "glk/scott/command_parser.h"
#include "glk/scott/saga_draw.h"
#include "glk/scott/globals.h"
namespace Glk {
namespace Scott {
#define GREMLINS_ANIMATION_RATE 670
void updateGremlinsAnimations(void) {
if (_G(_rooms)[MY_LOC]._image == 255) {
g_scott->glk_request_timer_events(0);
return;
}
g_scott->openGraphicsWindow();
if (_G(_graphics) == nullptr) {
g_scott->glk_request_timer_events(0);
return;
}
int timer_delay = GREMLINS_ANIMATION_RATE;
switch (MY_LOC) {
case 1: /* Bedroom */
if (_G(_items)[50]._location == 1) /* Gremlin throwing darts */
{
if (_G(_animationFlag)) {
g_scott->drawImage(60); /* Gremlin throwing dart frame 1 */
} else {
g_scott->drawImage(59); /* Gremlin throwing dart frame 2 */
}
}
break;
case 17: /* Dotty's Tavern */
if (_G(_items)[82]._location == 17) /* Gang of GREMLINS */
{
if (_G(_animationFlag)) {
g_scott->drawImage(49); /* Gremlin hanging from curtains frame 1 */
g_scott->drawImage(51); /* Gremlin ear frame 1 */
g_scott->drawImage(54); /* Gremlin's mouth frame 1 */
} else {
g_scott->drawImage(50); /* Gremlin hanging from curtains frame 2 */
g_scott->drawImage(52); /* Gremlin ear frame 2 */
g_scott->drawImage(53); /* Gremlin's mouth frame 2 */
}
}
break;
case 16: /* Behind a Bar */
if (_G(_items)[82]._location == 16) /* Gang of GREMLINS */
{
if (_G(_animationFlag)) {
g_scott->drawImage(57); /* Flasher gremlin frame 1 */
g_scott->drawImage(24); /* Gremlin tongue frame 1 */
if (CURRENT_GAME == GREMLINS_GERMAN)
g_scott->drawImage(46); /* Gremlin ear frame 1 */
else
g_scott->drawImage(73); /* Gremlin ear frame 1 */
} else {
g_scott->drawImage(58); /* Flasher gremlin frame 2 */
if (CURRENT_GAME == GREMLINS_GERMAN) {
g_scott->drawImage(33); /* Gremlin tongue frame 2 */
g_scott->drawImage(23); /* Gremlin ear frame 2 */
} else {
g_scott->drawImage(72); /* Gremlin tongue frame 2 */
if (CURRENT_GAME == GREMLINS_SPANISH)
g_scott->drawImage(23); /* Gremlin ear frame 2 */
else
g_scott->drawImage(74); /* Gremlin ear frame 2 */
}
}
}
break;
case 19: /* Square */
if (_G(_items)[82]._location == 19) /* Gang of GREMLINS */
{
if (_G(_animationFlag)) {
g_scott->drawImage(55); /* Silhouette of Gremlins frame 1 */
} else {
g_scott->drawImage(71); /* Silhouette of Gremlins frame 1 */
}
}
break;
case 6: /* on a road */
if (_G(_items)[82]._location == 6) /* Gang of GREMLINS */
{
if (_G(_animationFlag)) {
if ((_G(_game)->_subType & (LOCALIZED | C64)) == LOCALIZED) {
g_scott->drawImage(25); /* Silhouette 2 of Gremlins */
} else {
g_scott->drawImage(75); /* Silhouette 2 of Gremlins */
}
} else {
g_scott->drawImage(48); /* Silhouette 2 of Gremlins flipped */
}
}
break;
case 3: /* Kitchen */
if (_G(_counters)[2] == 2) /* Blender is on */
{
if (_G(_animationFlag)) {
g_scott->drawImage(56); /* Blended Gremlin */
} else {
g_scott->drawImage(12); /* Blended Gremlin flipped */
}
}
break;
default:
timer_delay = 0;
break;
}
_G(_animationFlag) = (_G(_animationFlag) == 0);
g_scott->glk_request_timer_events(timer_delay);
}
void gremlinsLook(void) {
if (_G(_rooms)[MY_LOC]._image != 255) {
if (MY_LOC == 17 && _G(_items)[82]._location == 17)
g_scott->drawImage(45); /* Bar full of Gremlins */
else
g_scott->drawImage(_G(_rooms)[MY_LOC]._image);
_G(_animationFlag) = 0;
updateGremlinsAnimations();
}
/* Ladder image at the top of the department store */
if (MY_LOC == 34 && _G(_items)[53]._location == MY_LOC) {
g_scott->drawImage(42);
} else if (MY_LOC == 10 && _G(_items)[15]._location == 0) {
if (_G(_items)[99]._location == MY_LOC && CURRENT_GAME == GREMLINS_GERMAN_C64)
g_scott->drawImage(90); /* Dazed Stripe */
g_scott->drawImage(82); /* Empty pool with puddle */
/* Draw puddle on top of Stripe */
/* Doesn't look great, but better than the other way round */
}
}
void fillInGermanSystemMessages() {
_G(_sys)[I_DONT_KNOW_HOW_TO] = "Ich weiss nicht, wie man etwas \"";
_G(_sys)[SOMETHING] = "\" macht. ";
_G(_sys)[I_DONT_KNOW_WHAT_A] = "\"";
_G(_sys)[IS] = "\" kenne ich nicht. ";
_G(_sys)[YES] = "Ja";
_G(_sys)[NO] = "Nein";
_G(_sys)[ANSWER_YES_OR_NO] = "Antworte Ja oder Nein.\n";
_G(_sys)[I_DONT_UNDERSTAND] = "Ich verstehe nicht. ";
_G(_sys)[ARE_YOU_SURE] = "Sind Sie sicher? ";
_G(_sys)[NOTHING_HERE_TO_TAKE] = "Hier gibt es nichts zu nehmen. ";
_G(_sys)[YOU_HAVE_NOTHING] = "Ich traege nichts. ";
_G(_sys)[MOVE_UNDONE] = "Verschieben rueckgaengig gemacht. ";
_G(_sys)[CANT_UNDO_ON_FIRST_TURN] = "Sie koennen die erste Runde nicht rueckgaengig machen. ";
_G(_sys)[NO_UNDO_STATES] = "Keine rueckgaengig-Zustaende mehr gespeichert. ";
_G(_sys)[SAVED] = "Spiel gespeichert. ";
_G(_sys)[CANT_USE_ALL] = "Sie koennen ALLES nicht mit diesem Verb verwenden. ";
_G(_sys)[TRANSCRIPT_ON] = "Das Transkript ist jetzt eingeschaltet. ";
_G(_sys)[TRANSCRIPT_OFF] = "Das Transkript ist jetzt deaktiviert. ";
_G(_sys)[NO_TRANSCRIPT] = "Es wird kein Transkript ausgefuehrt. ";
_G(_sys)[TRANSCRIPT_ALREADY] = "Eine Transkript laeuft bereits. ";
_G(_sys)[FAILED_TRANSCRIPT] = "Transkriptdatei konnte nicht erstellt werden. ";
_G(_sys)[TRANSCRIPT_START] = "Beginn einer Transkript.\n\n";
_G(_sys)[TRANSCRIPT_END] = "\n\nEnde eniner Transkript.\n";
_G(_sys)[BAD_DATA] = "SCHLECHTE DATEN! Ungueltige Speicherdatei.\n";
_G(_sys)[STATE_SAVED] = "Zustand speichern.\n";
_G(_sys)[NO_SAVED_STATE] = "Es ist kein gespeicherter Zustand vorhanden.\n";
_G(_sys)[STATE_RESTORED] = "Zustand wiederhergestellt.\n";
_G(_sys)[YOU_ARE] = "Ich bin ";
_G(_sys)[WHAT] = _G(_sys)[HUH];
for (int i = 0; i < NUMBER_OF_DIRECTIONS; i++)
_G(_directions)[i] = _G(_germanDirections)[i];
for (int i = 0; i < NUMBER_OF_SKIPPABLE_WORDS; i++)
_G(_skipList)[i] = _G(_germanSkipList)[i];
for (int i = 0; i < NUMBER_OF_DELIMITERS; i++)
_G(_delimiterList)[i] = _G(_germanDelimiterList)[i];
for (int i = 0; i < NUMBER_OF_EXTRA_COMMANDS; i++)
_G(_extraCommands)[i] = _G(_germanExtraCommands)[i];
for (int i = 0; i < NUMBER_OF_EXTRA_NOUNS; i++)
_G(_extraNouns)[i] = _G(_germanExtraNouns)[i];
}
void loadExtraGermanGremlinsc64Data() {
_G(_verbs)[0] = "AUTO\0";
_G(_nouns)[0] = "ANY\0";
_G(_nouns)[28] = "*Y.M.C\0";
// These are broken in some versions
_G(_actions)[0]._condition[0] = 1005;
_G(_actions)[6]._vocab = 100;
_G(_gameHeader)->_numActions = 236;
SysMessageType messagekey[] = {
NORTH,
SOUTH,
EAST,
WEST,
UP,
DOWN,
EXITS,
YOU_SEE,
YOU_ARE,
YOU_CANT_GO_THAT_WAY,
OK,
WHAT_NOW,
HUH,
YOU_HAVE_IT,
TAKEN,
DROPPED,
YOU_HAVENT_GOT_IT,
INVENTORY,
YOU_DONT_SEE_IT,
THATS_BEYOND_MY_POWER,
DIRECTION,
YOURE_CARRYING_TOO_MUCH,
IM_DEAD,
RESUME_A_SAVED_GAME,
PLAY_AGAIN,
YOU_CANT_DO_THAT_YET,
I_DONT_UNDERSTAND,
NOTHING};
for (int i = 0; i < 28; i++) {
_G(_sys)[messagekey[i]] = _G(_systemMessages)[i];
}
_G(_sys)[HIT_ENTER] = _G(_systemMessages)[30];
fillInGermanSystemMessages();
_G(_items)[99]._image = 255;
}
void loadExtraGermanGremlinsData() {
_G(_verbs)[0] = "AUTO\0";
_G(_nouns)[0] = "ANY\0";
_G(_nouns)[28] = "*Y.M.C\0";
_G(_messages)[90] = "Ehe ich etwas anderes mache, much aich erst alles andere fallenlassen. ";
fillInGermanSystemMessages();
}
void loadExtraSpanishGremlinsData() {
_G(_verbs)[0] = "AUTO\0";
_G(_nouns)[0] = "ANY\0";
for (int i = YOU_ARE; i <= HIT_ENTER; i++)
_G(_sys)[i] = _G(_systemMessages)[15 - YOU_ARE + i];
for (int i = I_DONT_UNDERSTAND; i <= THATS_BEYOND_MY_POWER; i++)
_G(_sys)[i] = _G(_systemMessages)[6 - I_DONT_UNDERSTAND + i];
for (int i = DROPPED; i <= OK; i++)
_G(_sys)[i] = _G(_systemMessages)[2 - DROPPED + i];
_G(_sys)[PLAY_AGAIN] = _G(_systemMessages)[5];
_G(_sys)[YOURE_CARRYING_TOO_MUCH] = _G(_systemMessages)[22];
_G(_sys)[IM_DEAD] = _G(_systemMessages)[23];
_G(_sys)[YOU_CANT_GO_THAT_WAY] = _G(_systemMessages)[14];
_G(_sys)[WHAT] = _G(_sys)[HUH];
_G(_sys)[YES] = "s}";
_G(_sys)[NO] = "no";
_G(_sys)[ANSWER_YES_OR_NO] = "Contesta s} o no.\n";
_G(_sys)[I_DONT_KNOW_WHAT_A] = "No s\x84 qu\x84 es un \"";
_G(_sys)[IS] = "\". ";
_G(_sys)[I_DONT_KNOW_HOW_TO] = "No s\x84 c|mo \"";
_G(_sys)[SOMETHING] = "\" algo. ";
_G(_sys)[ARE_YOU_SURE] = "\x83\x45stas segura? ";
_G(_sys)[NOTHING_HERE_TO_TAKE] = "No hay nada aqu} para tomar. ";
_G(_sys)[YOU_HAVE_NOTHING] = "No llevo nada. ";
_G(_sys)[MOVE_UNDONE] = "Deshacer. ";
_G(_sys)[CANT_UNDO_ON_FIRST_TURN] = "No se puede deshacer en el primer turno. ";
_G(_sys)[NO_UNDO_STATES] = "No hay m{s estados de deshacer disponibles. ";
_G(_sys)[SAVED] = "Juego guardado. ";
_G(_sys)[CANT_USE_ALL] = "No puedes usar TODO con este verbo. ";
_G(_sys)[TRANSCRIPT_ON] = "Transcripci|n en. ";
_G(_sys)[TRANSCRIPT_OFF] = "Transcripci|n desactivada. ";
_G(_sys)[NO_TRANSCRIPT] = "No se est{ ejecutando ninguna transcripci|n. ";
_G(_sys)[TRANSCRIPT_ALREADY] = "Ya se est{ ejecutando una transcripci|n. ";
_G(_sys)[FAILED_TRANSCRIPT] = "No se pudo crear el archivo de transcripci|n. ";
_G(_sys)[TRANSCRIPT_START] = "Comienzo de una transcripci|n.\n\n";
_G(_sys)[TRANSCRIPT_END] = "\n\nFin de una transcripci|n.\n";
_G(_sys)[BAD_DATA] = "\x80MALOS DATOS! Guardar archivo no v{lido.\n";
_G(_sys)[STATE_SAVED] = "Estado guardado.\n";
_G(_sys)[NO_SAVED_STATE] = "No existe ning\x85n estado guardado.\n";
_G(_sys)[STATE_RESTORED] = "Estado restaurado.\n";
for (int i = 0; i < NUMBER_OF_DIRECTIONS; i++)
_G(_directions)[i] = _G(_spanishDirections)[i];
for (int i = 0; i < NUMBER_OF_EXTRA_NOUNS; i++)
_G(_extraNouns)[i] = _G(_spanishExtraNouns)[i];
for (int i = 0; i < NUMBER_OF_EXTRA_COMMANDS; i++)
_G(_extraCommands)[i] = _G(_spanishExtraCommands)[i];
}
void gremlinsAction(int parameter) {
g_scott->drawImage(68); /* Mogwai */
g_scott->display(_G(_bottomWindow), "\n%s\n", _G(_sys)[HIT_ENTER].c_str());
g_scott->hitEnter();
g_scott->look();
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,50 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_GREMLINS_H
#define GLK_SCOTT_GREMLINS_H
namespace Glk {
namespace Scott {
void updateGremlinsAnimations();
void gremlinsLook();
void gremlinsAction(int p);
void loadExtraGermanGremlinsData();
void loadExtraSpanishGremlinsData();
void loadExtraGermanGremlinsc64Data();
} // End of namespace Scott
} // End of namespace Glk
#endif

431
engines/glk/scott/hulk.cpp Normal file
View File

@@ -0,0 +1,431 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/scott.h"
#include "glk/scott/definitions.h"
#include "glk/scott/resource.h"
#include "glk/scott/globals.h"
#include "glk/scott/types.h"
#include "glk/scott/hulk.h"
namespace Glk {
namespace Scott {
void hulkShowImageOnExamine(int noun) {
int image = 0;
switch (noun) {
case 55: // Dome
if (_G(_items)[11]._location == MY_LOC)
image = 28;
break;
case 108: // Natter energy egg
if (_G(_items)[17]._location == MY_LOC || _G(_items)[17]._location == CARRIED)
image = 30;
break;
case 124: // Bio-Gem
case 41:
if (_G(_items)[18]._location == MY_LOC || _G(_items)[18]._location == CARRIED)
image = 29;
break;
case 21: // Killer Bees
if (_G(_items)[24]._location == MY_LOC)
image = 31;
break;
case 83: // Iron ring
if (_G(_items)[33]._location == MY_LOC)
image = 32;
break;
case 121: // Cage
if (_G(_items)[47]._location == MY_LOC)
image = 33;
break;
default:
break;
}
if (image) {
g_scott->drawImage(image);
g_scott->output(_G(_sys)[HIT_ENTER]);
g_scott->hitEnter();
}
}
void hulkLook() {
g_scott->drawImage(_G(_rooms)[MY_LOC]._image);
for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
int image = _G(_items)[ct]._image;
if (_G(_items)[ct]._location == MY_LOC && image != 255) {
/* Don't draw bio gem in fuzzy area */
if ((ct == 18 && MY_LOC != 15) ||
/* Don't draw Dr. Strange until outlet is plugged */
(ct == 26 && _G(_items)[28]._location != MY_LOC))
continue;
g_scott->drawImage(image);
}
}
}
void drawHulkImage(int p) {
int image = 0;
switch (p) {
case 85:
image = 34;
break;
case 86:
image = 35;
break;
case 83:
image = 36;
break;
case 84:
image = 37;
break;
case 87:
image = 38;
break;
case 88:
image = 39;
break;
case 89:
image = 40;
break;
case 82:
image = 41;
break;
case 81:
image = 42;
break;
default:
error("drawHulkImage: Unhandled image number %d", p);
break;
}
if (image != 0) {
g_scott->drawImage(image);
g_scott->output(_G(_sys)[HIT_ENTER]);
g_scott->hitEnter();
}
}
uint8_t *readHulkDictionary(GameInfo info, uint8_t **pointer) {
uint8_t *ptr = *pointer;
char *dictword = new char[info._wordLength + 2];
char c = 0;
int wordnum = 0;
int charindex = 0;
int nv = info._numberOfVerbs;
int nn = info._numberOfNouns;
for (int i = 0; i < nn - nv; i++)
_G(_verbs)[nv + i] = ".\0";
for (int i = 0; i < nv - nn; i++)
_G(_nouns)[nn + i] = ".\0";
do {
for (int i = 0; i < info._wordLength; i++) {
c = *(ptr++);
if (c == 0) {
if (charindex == 0) {
c = *(ptr++);
}
}
dictword[charindex] = c;
if (c == '*')
i--;
charindex++;
dictword[charindex] = 0;
}
if (wordnum < nn) {
_G(_nouns)[wordnum] = Common::String(dictword, charindex + 1);
} else {
_G(_verbs)[wordnum - nn] = Common::String(dictword, charindex + 1);
}
wordnum++;
if (c != 0 && !isascii(c))
return ptr;
charindex = 0;
} while (wordnum <= nv + nn);
delete[] dictword;
return ptr;
}
int tryLoadingHulk(GameInfo info, int dictStart) {
int ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
int ct;
Action *ap;
Room *rp;
Item *ip;
/* Load the header */
uint8_t *ptr = _G(_entireFile);
_G(_fileBaselineOffset) = dictStart - info._startOfDictionary - 645;
int offset = info._startOfHeader + _G(_fileBaselineOffset);
ptr = seekToPos(_G(_entireFile), offset);
if (ptr == 0)
return 0;
readHeader(ptr);
parseHeader(_G(_header), info._headerStyle, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm);
_G(_gameHeader)->_numItems = ni;
_G(_items).resize(ni + 1);
_G(_gameHeader)->_numActions = na;
_G(_actions).resize(na + 1);
_G(_gameHeader)->_numWords = nw;
_G(_gameHeader)->_wordLength = wl;
_G(_verbs).resize(nw + 1);
_G(_nouns).resize(nw + 1);
_G(_gameHeader)->_numRooms = nr;
_G(_rooms).resize(nr + 1);
_G(_gameHeader)->_maxCarry = mc;
_G(_gameHeader)->_playerRoom = pr;
_G(_gameHeader)->_treasures = tr;
_G(_gameHeader)->_lightTime = lt;
_G(_lightRefill) = lt;
_G(_gameHeader)->_numMessages = mn;
_G(_messages).resize(mn + 1);
_G(_gameHeader)->_treasureRoom = trm;
if (_G(_header)[0] != info._wordLength || _G(_header)[1] != info._numberOfWords || _G(_header)[2] != info._numberOfActions || _G(_header)[3] != info._numberOfItems || _G(_header)[4] != info._numberOfMessages || _G(_header)[5] != info._numberOfRooms || _G(_header)[6] != info._maxCarried) {
return 0;
}
#pragma mark Dictionary
if (seekIfNeeded(info._startOfDictionary, &offset, &ptr) == 0)
return 0;
readHulkDictionary(info, &ptr);
#pragma mark Rooms
if (seekIfNeeded(info._startOfRoomDescriptions, &offset, &ptr) == 0)
return 0;
ct = 0;
uint8_t string_length = 0;
do {
rp = &_G(_rooms)[ct];
string_length = *(ptr++);
if (string_length == 0) {
rp->_text = ".\0";
} else {
for (int i = 0; i < string_length; i++) {
rp->_text += *(ptr++);
}
}
ct++;
} while (ct < nr + 1);
#pragma mark Messages
ct = 0;
const char *string;
do {
string_length = *(ptr++);
if (string_length == 0) {
string = ".\0";
_G(_messages)[ct] = string;
} else {
char *s = new char[string_length + 1];
for (int i = 0; i < string_length; i++) {
s[i] = *(ptr++);
}
s[string_length] = 0;
_G(_messages)[ct] = s;
}
ct++;
} while (ct < mn + 1);
#pragma mark Items
if (seekIfNeeded(info._startOfItemDescriptions, &offset, &ptr) == 0)
return 0;
ct = 0;
do {
ip = &_G(_items)[ct];
string_length = *(ptr++);
if (string_length == 0) {
ip->_text = ".\0";
} else {
for (int i = 0; i < string_length; i++) {
ip->_text += *(ptr++);
}
const char *p = strchr(ip->_text.c_str(), '/');
if (p) {
ip->_autoGet = Common::String(p);
/* Some games use // to mean no auto get/drop word! */
if (!(ip->_autoGet == "//") && !(ip->_autoGet == "/*")) {
ip->_text = Common::String(ip->_text.c_str(), p);
ip->_autoGet.deleteChar(0);
const char *t = strchr(ip->_autoGet.c_str(), '/');
if (t) {
ip->_autoGet = Common::String(ip->_autoGet.c_str(), t);
}
ptr++;
}
}
}
ct++;
} while (ct < ni + 1);
#pragma mark Room connections
if (seekIfNeeded(info._startOfRoomConnections, &offset, &ptr) == 0)
return 0;
/* The room connections are ordered by direction, not room, so all the North
* connections for all the rooms come first, then the South connections, and
* so on. */
for (int j = 0; j < 6; j++) {
ct = 0;
while (ct < nr + 1) {
rp = &_G(_rooms)[ct];
rp->_exits[j] = *(ptr++);
ptr++;
ct++;
}
}
#pragma mark item locations
if (seekIfNeeded(info._startOfItemLocations, &offset, &ptr) == 0)
return 0;
ct = 0;
while (ct < ni + 1) {
ip = &_G(_items)[ct];
ip->_location = *(ptr++);
ip->_location += *(ptr++) * 0x100;
ip->_initialLoc = ip->_location;
ct++;
}
#pragma mark room images
if (seekIfNeeded(info._startOfRoomImageList, &offset, &ptr) == 0)
return 0;
for (ct = 0; ct <= _G(_gameHeader)->_numRooms; ct++) {
rp = &_G(_rooms)[ct];
rp->_image = *(ptr++);
}
#pragma mark item images
if (seekIfNeeded(info._startOfItemImageList, &offset, &ptr) == 0)
return 0;
for (ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
ip = &_G(_items)[ct];
ip->_image = 255;
}
int index, image = 10;
for (index = (*ptr++); index != 255; index = (*ptr++)) {
_G(_items)[index]._image = image++;
}
#pragma mark item flags
/* Hulk does not seem to use item flags */
#pragma mark Actions
if (seekIfNeeded(info._startOfActions, &offset, &ptr) == 0)
return 0;
ct = 0;
int verb, noun, value, value2, plus;
while (ct < na + 1) {
ap = &_G(_actions)[ct];
plus = na + 1;
verb = _G(_entireFile)[offset + ct];
noun = _G(_entireFile)[offset + ct + plus];
ap->_vocab = verb * 150 + noun;
for (int j = 0; j < 2; j++) {
plus += na + 1;
value = _G(_entireFile)[offset + ct + plus];
plus += na + 1;
value2 = _G(_entireFile)[offset + ct + plus];
ap->_action[j] = 150 * value + value2;
}
int offset2 = offset + 0x624;
plus = 0;
for (int j = 0; j < 5; j++) {
value = _G(_entireFile)[offset2 + ct * 2 + plus];
value2 = _G(_entireFile)[offset2 + ct * 2 + plus + 1];
ap->_condition[j] = value + value2 * 0x100;
plus += (na + 1) * 2;
}
ct++;
}
if (CURRENT_GAME == HULK_C64) {
_G(_hulkCoordinates) = 0x22cd;
_G(_hulkItemImageOffsets) = 0x2731;
_G(_hulkLookImageOffsets) = 0x2761;
_G(_hulkSpecialImageOffsets) = 0x2781;
_G(_hulkImageOffset) = static_cast<size_t>(-0x7ff);
}
return 1;
}
} // End of namespace Scott
} // End of namespace Glk

49
engines/glk/scott/hulk.h Normal file
View File

@@ -0,0 +1,49 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_HULK_H
#define GLK_SCOTT_HULK_H
struct GameInfo;
namespace Glk {
namespace Scott {
void hulkShowImageOnExamine(int noun);
void hulkLook();
void drawHulkImage(int p);
int tryLoadingHulk(GameInfo info, int dictStart);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,119 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "common/util.h"
#include "glk/scott/layout_text.h"
namespace Glk {
namespace Scott {
int findBreak(const char *buf, int pos, int columns) {
int diff = 0;
while (diff < columns && !Common::isSpace((unsigned char)buf[pos])) {
pos--;
diff++;
}
if (diff >= columns || diff < 1) /* Found no space */ {
return -1;
}
return diff;
}
/* Breaks a null-terminated string up by inserting newlines, moving words
down to the next line when reaching the end of the line */
char *lineBreakText(char *source, int columns, int *rows, int *length) {
columns -= 1;
char *result = nullptr;
char buf[768];
int col = 0;
int row = 0;
int sourcepos = 0;
int destpos = 0;
int diff = 0;
*rows = 0;
while (source[sourcepos] != '\0') {
while (col < columns && source[sourcepos] != '\0') {
if (source[sourcepos] == 10 || source[sourcepos] == 13) {
/* Found a line break. */
/* Any spaces before a line break may cause trouble, */
/* so we delete them */
while (destpos && buf[destpos - 1] == ' ') {
destpos--;
}
col = 0;
row++;
} else {
col++;
}
buf[destpos++] = source[sourcepos++];
if (source[sourcepos] == 10 || source[sourcepos] == 13)
col--;
}
/* We have reached the end of a line */
row++;
col = 0;
if (source[sourcepos] == '\0') {
break;
}
diff = findBreak(source, sourcepos, columns);
if (diff > -1) { /* We found a suitable break */
sourcepos = sourcepos - diff;
destpos = destpos - diff;
buf[destpos++] = '\n';
if (Common::isSpace((unsigned char)source[sourcepos])) {
sourcepos++;
}
}
}
*rows = row;
*length = 0;
result = new char[destpos + 1];
if (result == nullptr)
return nullptr;
memcpy(result, buf, destpos);
result[destpos] = '\0';
*length = destpos;
return result;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,45 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_LAYOUTTEXT_H
#define GLK_SCOTT_LAYOUTTEXT_H
namespace Glk {
namespace Scott {
/* Breaks a null-terminated string up by inserting newlines,*/
/* moving words down to the next line when reaching the end of the line */
char *lineBreakText(char *source, int columns, int *rows, int *length);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,246 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/scott.h"
#include "glk/scott/globals.h"
#include "glk/scott/ring_buffer.h"
#include "glk/scott/saga_draw.h"
#include "glk/scott/line_drawing.h"
namespace Glk {
namespace Scott {
void scottLinegraphicsPlotClip(int x, int y, int colour) {
/*
* Clip the plot if the value is outside the context. Otherwise, plot the
* pixel as colour1 if it is currently colour2.
*/
if (x >= 0 && x <= _G(_scottGraphicsWidth) && y >= 0 && y < _G(_scottGraphicsHeight)) {
_G(_pictureBitmap)[y * 255 + x] = colour;
PixelToDraw *toDraw = new PixelToDraw;
toDraw->_x = x;
toDraw->_y = y;
toDraw->_colour = colour;
_G(_pixelsToDraw)[_G(_totalDrawInstructions)++] = toDraw;
}
}
void scottLinegraphicsDrawLine(int x1, int y1, int x2, int y2, int colour) {
int x, y, dx, dy, incx, incy, balance;
/* Normalize the line into deltas and increments. */
if (x2 >= x1) {
dx = x2 - x1;
incx = 1;
} else {
dx = x1 - x2;
incx = -1;
}
if (y2 >= y1) {
dy = y2 - y1;
incy = 1;
} else {
dy = y1 - y2;
incy = -1;
}
/* Start at x1,y1. */
x = x1;
y = y1;
/* Decide on a direction to progress in. */
if (dx >= dy) {
dy <<= 1;
balance = dy - dx;
dx <<= 1;
/* Loop until we reach the end point of the line. */
while (x != x2) {
scottLinegraphicsPlotClip(x, y, colour);
if (balance >= 0) {
y += incy;
balance -= dx;
}
balance += dy;
x += incx;
}
scottLinegraphicsPlotClip(x, y, colour);
} else {
dx <<= 1;
balance = dx - dy;
dy <<= 1;
/* Loop until we reach the end point of the line. */
while (y != y2) {
scottLinegraphicsPlotClip(x, y, colour);
if (balance >= 0) {
x += incx;
balance -= dy;
}
balance += dx;
y += incy;
}
scottLinegraphicsPlotClip(x, y, colour);
}
}
void freePixels() {
for (int i = 0; i < _G(_totalDrawInstructions); i++)
if (_G(_pixelsToDraw)[i] != nullptr)
delete _G(_pixelsToDraw)[i];
delete[] _G(_pixelsToDraw);
}
int linegraphicsGetPixel(int x, int y) {
return _G(_pictureBitmap)[y * 255 + x];
}
void diamondFill(int x, int y, int colour) {
uint8_t buffer[2048];
cbuf_handle_t ringbuf = circularBufInit(buffer, 2048);
circularBufPut(ringbuf, x, y);
while (!circularBufEmpty(ringbuf)) {
circularBufGet(ringbuf, &x, &y);
if (x >= 0 && x < _G(_scottGraphicsWidth) && y >= 0 &&
y < _G(_scottGraphicsHeight) &&
linegraphicsGetPixel(x, y) == _G(_bgColour)) {
scottLinegraphicsPlotClip(x, y, colour);
circularBufPut(ringbuf, x, y + 1);
circularBufPut(ringbuf, x, y - 1);
circularBufPut(ringbuf, x + 1, y);
circularBufPut(ringbuf, x - 1, y);
}
}
}
void drawVectorPicture(int image) {
if (image < 0) {
return;
}
if (_G(_vectorImageShown) == image) {
if (_G(_vectorState) == SHOWING_VECTOR_IMAGE) {
return;
} else {
if (_G(_gliSlowDraw))
g_scott->glk_request_timer_events(20);
drawSomeVectorPixels(1);
return;
}
}
g_scott->glk_request_timer_events(0);
_G(_vectorImageShown) = image;
if (_G(_pixelsToDraw) != nullptr)
freePixels();
_G(_pixelsToDraw) = new PixelToDraw *[255 * 97];
_G(_totalDrawInstructions) = 0;
_G(_currentDrawInstruction) = 0;
if (_G(_palChosen) == NO_PALETTE) {
_G(_palChosen) = _G(_game)->_palette;
definePalette();
}
_G(_pictureBitmap) = new uint8_t[255 * 97];
_G(_bgColour) = _G(_lineImages)[image]._bgColour;
memset(_G(_pictureBitmap), _G(_bgColour), 255 * 97);
if (_G(_bgColour) == 0)
_G(_lineColour) = 7;
else
_G(_lineColour) = 0;
int x = 0, y = 0, y2 = 0;
int arg1, arg2, arg3;
uint8_t *p = _G(_lineImages)[image]._data;
uint8_t opcode = 0;
while (((p < _G(_lineImages)[image]._data) || static_cast<size_t>(p - _G(_lineImages)[image]._data) < _G(_lineImages)[image]._size) && opcode != 0xff) {
if (p > _G(_entireFile) + _G(_fileLength)) {
error("drawVectorPicture: Out of range! Opcode: %x. Image: %d. LineImages[%d].size: %llu", opcode, image, image, _G(_lineImages)[image]._size);
break;
}
opcode = *(p++);
switch (opcode) {
case 0xc0:
y = 190 - *(p++);
x = *(p++);
break;
case 0xc1:
arg1 = *(p++);
arg2 = *(p++);
arg3 = *(p++);
diamondFill(arg3, 190 - arg2, arg1);
break;
case 0xff:
break;
default:
arg1 = *(p++);
y2 = 190 - opcode;
scottLinegraphicsDrawLine(x, y, arg1, y2, _G(_lineColour));
x = arg1;
y = y2;
break;
}
}
if (_G(_pictureBitmap) != nullptr) {
delete[] _G(_pictureBitmap);
_G(_pictureBitmap) = nullptr;
}
if (_G(_gliSlowDraw))
g_scott->glk_request_timer_events(20);
else
drawSomeVectorPixels(1);
}
void drawSomeVectorPixels(int fromStart) {
_G(_vectorState) = DRAWING_VECTOR_IMAGE;
int i = _G(_currentDrawInstruction);
if (fromStart)
i = 0;
if (i == 0)
rectFill(0, 0, _G(_scottGraphicsWidth), _G(_scottGraphicsHeight), remap(_G(_bgColour)));
for (; i < _G(_totalDrawInstructions) && (!_G(_gliSlowDraw) || i < _G(_currentDrawInstruction) + 50); i++) {
PixelToDraw toDraw = *_G(_pixelsToDraw)[i];
putPixel(toDraw._x, toDraw._y, remap(toDraw._colour));
}
_G(_currentDrawInstruction) = i;
if (_G(_currentDrawInstruction) >= _G(_totalDrawInstructions)) {
g_scott->glk_request_timer_events(0);
_G(_vectorState) = SHOWING_VECTOR_IMAGE;
}
}
int drawingVector() {
return (_G(_totalDrawInstructions) > _G(_currentDrawInstruction));
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,67 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_LINEDRAWING_H
#define GLK_SCOTT_LINEDRAWING_H
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
struct LineImage {
uint8_t *_data;
int _bgColour;
size_t _size;
};
struct PixelToDraw {
uint8_t _x = 0;
uint8_t _y = 0;
uint8_t _colour = 0;
};
void drawVectorPicture(int image);
void drawSomeVectorPixels(int fromStart);
int drawingVector();
void freePixels();
enum VectorStateType : int {
NO_VECTOR_IMAGE,
DRAWING_VECTOR_IMAGE,
SHOWING_VECTOR_IMAGE
};
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,266 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "common/str.h"
#include "glk/scott/scott.h"
#include "glk/scott/globals.h"
#include "glk/scott/command_parser.h"
#include "glk/scott/decompress_text.h"
#include "glk/scott/decompress_z80.h"
#include "glk/scott/detection.h"
#include "glk/scott/detection_tables.h"
#include "glk/scott/game_info.h"
#include "glk/scott/hulk.h"
#include "glk/scott/line_drawing.h"
#include "glk/scott/saga_draw.h"
#include "glk/scott/c64_checksums.h"
#include "glk/scott/game_specific.h"
#include "glk/scott/resource.h"
#include "glk/scott/load_game.h"
#include "glk/scott/robin_of_sherwood.h"
#include "glk/scott/gremlins.h"
#include "glk/scott/seas_of_blood.h"
#include "glk/scott/load_ti99_4a.h"
namespace Glk {
namespace Scott {
void loadZXSpectrum(Common::SeekableReadStream *f, Common::String md5) {
_G(_entireFile) = new uint8_t[_G(_fileLength)];
size_t result = f->read(_G(_entireFile), _G(_fileLength));
if (result != _G(_fileLength))
g_scott->fatal("File empty or read error!");
uint8_t *uncompressed = decompressZ80(_G(_entireFile), _G(_fileLength));
if (uncompressed != nullptr) {
delete[] _G(_entireFile);
_G(_entireFile) = uncompressed;
_G(_fileLength) = 0xc000;
}
int offset;
DictionaryType dict_type = getId(&offset);
if (dict_type == NOT_A_GAME)
return;
int index = _G(_md5Index)[md5];
if (tryLoading(_G(_games)[index], offset, 0)) {
_G(_game) = &_G(_games)[index];
}
}
void loadC64(Common::SeekableReadStream* f, Common::String md5) {
_G(_entireFile) = new uint8_t[_G(_fileLength)];
size_t result = f->read(_G(_entireFile), _G(_fileLength));
if (result != _G(_fileLength))
g_scott->fatal("File empty or read error!");
_G(_fallbackGame)._gameID = static_cast<GameIDType>(detectC64(&_G(_entireFile), &_G(_fileLength)));
}
void loadTI994A(Common::SeekableReadStream *f) {
_G(_entireFile) = new uint8_t[_G(_fileLength)];
size_t result = f->read(_G(_entireFile), _G(_fileLength));
if (result != _G(_fileLength))
g_scott->fatal("File empty or read error!");
_G(_fallbackGame)._gameID = detectTI994A(f, &_G(_entireFile), &_G(_fileLength));
}
void loadGameFile(Common::SeekableReadStream *f) {
for (int i = 0; i < NUMBER_OF_DIRECTIONS; i++)
_G(_directions)[i] = _G(_englishDirections)[i];
for (int i = 0; i < NUMBER_OF_SKIPPABLE_WORDS; i++)
_G(_skipList)[i] = _G(_englishSkipList)[i];
for (int i = 0; i < NUMBER_OF_DELIMITERS; i++)
_G(_delimiterList)[i] = _G(_englishDelimiterList)[i];
for (int i = 0; i < NUMBER_OF_EXTRA_NOUNS; i++)
_G(_extraNouns)[i] = _G(_englishExtraNouns)[i];
_G(_fileLength) = f->size();
_G(_game) = &_G(_fallbackGame);
Common::String md5 = g_vm->getGameMD5();
const GlkDetectionEntry *p = SCOTT_GAMES;
while (p->_md5) {
if (md5.equalsC(p->_md5)) {
if (!scumm_stricmp(p->_extra, "")) {
_G(_fallbackGame)._gameID = SCOTTFREE;
break;
} else if (!scumm_stricmp(p->_extra, "ZXSpectrum")) {
loadZXSpectrum(f, md5);
break;
} else if (!scumm_stricmp(p->_extra, "C64")) {
loadC64(f, md5);
break;
} else {
loadTI994A(f);
break;
}
}
++p;
}
if (CURRENT_GAME == SCOTTFREE || CURRENT_GAME == TI994A)
return;
/* Copy ZX Spectrum style system messages as base */
for (int i = 6; i < MAX_SYSMESS && g_sysDictZX[i] != nullptr; i++) {
_G(_sys)[i] = g_sysDictZX[i];
}
switch (CURRENT_GAME) {
case ROBIN_OF_SHERWOOD:
loadExtraSherwoodData();
break;
case ROBIN_OF_SHERWOOD_C64:
loadExtraSherwoodData64();
break;
case SEAS_OF_BLOOD:
loadExtraSeasOfBloodData();
break;
case SEAS_OF_BLOOD_C64:
loadExtraSeasOfBlood64Data();
break;
case CLAYMORGUE:
for (int i = OK; i <= RESUME_A_SAVED_GAME; i++)
_G(_sys)[i] = _G(_systemMessages)[6 - OK + i];
for (int i = PLAY_AGAIN; i <= ON_A_SCALE_THAT_RATES; i++)
_G(_sys)[i] = _G(_systemMessages)[2 - PLAY_AGAIN + i];
break;
case ADVENTURELAND:
for (int i = PLAY_AGAIN; i <= ON_A_SCALE_THAT_RATES; i++)
_G(_sys)[i] = _G(_systemMessages)[2 - PLAY_AGAIN + i];
for (int i = OK; i <= YOU_HAVENT_GOT_IT; i++)
_G(_sys)[i] = _G(_systemMessages)[6 - OK + i];
for (int i = YOU_DONT_SEE_IT; i <= RESUME_A_SAVED_GAME; i++)
_G(_sys)[i] = _G(_systemMessages)[13 - YOU_DONT_SEE_IT + i];
break;
case ADVENTURELAND_C64:
adventureland64Sysmess();
break;
case CLAYMORGUE_C64:
claymorgue64Sysmess();
break;
case GREMLINS_GERMAN_C64:
loadExtraGermanGremlinsc64Data();
break;
case SPIDERMAN_C64:
spiderman64Sysmess();
break;
case SUPERGRAN_C64:
supergran64Sysmess();
break;
case SAVAGE_ISLAND_C64:
_G(_items)[20]._image = 13;
// fallthrough
case SAVAGE_ISLAND2_C64:
_G(_sys)[IM_DEAD] = "I'm DEAD!! ";
if (CURRENT_GAME == SAVAGE_ISLAND2_C64)
_G(_rooms)[30]._image = 20;
break;
case SAVAGE_ISLAND:
_G(_items)[20]._image = 13;
// fallthrough
case SAVAGE_ISLAND2:
MY_LOC = 30; /* Both parts of Savage Island begin in room 30 */
// fallthrough
case GREMLINS_GERMAN:
case GREMLINS:
case SUPERGRAN:
for (int i = DROPPED; i <= OK; i++)
_G(_sys)[i] = _G(_systemMessages)[2 - DROPPED + i];
for (int i = I_DONT_UNDERSTAND; i <= THATS_BEYOND_MY_POWER; i++)
_G(_sys)[i] = _G(_systemMessages)[6 - I_DONT_UNDERSTAND + i];
for (int i = YOU_ARE; i <= HIT_ENTER; i++)
_G(_sys)[i] = _G(_systemMessages)[17 - YOU_ARE + i];
_G(_sys)[PLAY_AGAIN] = _G(_systemMessages)[5];
_G(_sys)[YOURE_CARRYING_TOO_MUCH] = _G(_systemMessages)[24];
_G(_sys)[IM_DEAD] = _G(_systemMessages)[25];
_G(_sys)[YOU_CANT_GO_THAT_WAY] = _G(_systemMessages)[14];
break;
case GREMLINS_SPANISH:
loadExtraSpanishGremlinsData();
break;
case HULK_C64:
case HULK:
for (int i = 0; i < 6; i++) {
_G(_sys)[i] = g_sysDictZX[i];
}
break;
default:
if (!(_G(_game)->_subType & C64)) {
if (_G(_game)->_subType & MYSTERIOUS) {
for (int i = PLAY_AGAIN; i <= YOU_HAVENT_GOT_IT; i++)
_G(_sys)[i] = _G(_systemMessages)[2 - PLAY_AGAIN + i];
for (int i = YOU_DONT_SEE_IT; i <= WHAT_NOW; i++)
_G(_sys)[i] = _G(_systemMessages)[15 - YOU_DONT_SEE_IT + i];
for (int i = LIGHT_HAS_RUN_OUT; i <= RESUME_A_SAVED_GAME; i++)
_G(_sys)[i] = _G(_systemMessages)[31 - LIGHT_HAS_RUN_OUT + i];
_G(_sys)[ITEM_DELIMITER] = " - ";
_G(_sys)[MESSAGE_DELIMITER] = "\n";
_G(_sys)[YOU_SEE] = "\nThings I can see:\n";
break;
} else {
for (int i = PLAY_AGAIN; i <= RESUME_A_SAVED_GAME; i++)
_G(_sys)[i] = _G(_systemMessages)[2 - PLAY_AGAIN + i];
}
}
break;
}
switch (CURRENT_GAME) {
case GREMLINS_GERMAN:
loadExtraGermanGremlinsData();
break;
case GREMLINS_GERMAN_C64:
loadExtraGermanGremlinsc64Data();
break;
default:
break;
}
if ((_G(_game)->_subType & (C64 | MYSTERIOUS)) == (MYSTERIOUS | C64)) {
mysterious64Sysmess();
}
/* If it is a C64 or a Mysterious Adventures game, we have setup the graphics already */
if (!(_G(_game)->_subType & (C64 | MYSTERIOUS)) && _G(_game)->_numberOfPictures > 0) {
sagaSetup(0);
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,48 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_DETECTGAME_H
#define GLK_SCOTT_DETECTGAME_H
#include "common/stream.h"
#include "glk/scott/definitions.h"
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
void loadGameFile(Common::SeekableReadStream *f);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,529 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/scott.h"
#include "glk/scott/globals.h"
#include "glk/scott/resource.h"
#include "glk/scott/game_info.h"
#include "glk/scott/load_ti99_4a.h"
namespace Glk {
namespace Scott {
struct DataHeader {
uint8_t _numObjects; /* number of objects */
uint8_t _numVerbs; /* number of verbs */
uint8_t _numNouns; /* number of nouns */
uint8_t _redRoom; /* the red room (dead room) */
uint8_t _maxItemsCarried; /* max number of items can be carried */
uint8_t _beginLocn; /* room to start in */
uint8_t _numTreasures; /* number of treasures */
uint8_t _cmdLength; /* number of letters in commands */
uint16_t _lightTurns; /* max number of turns light lasts */
uint8_t _treasureLocn; /* location of where to store treasures */
uint8_t _strange; /* !?! not known. */
uint16_t _pObjTable; /* pointer to object table */
uint16_t _pOrigItems; /* pointer to original items */
uint16_t _pObjLink; /* pointer to link table from noun to object */
uint16_t _pObjDescr; /* pointer to object descriptions */
uint16_t _pMessage; /* pointer to message pointers */
uint16_t _pRoomExit; /* pointer to room exits table */
uint16_t _pRoomDescr; /* pointer to room descr table */
uint16_t _pNounTable; /* pointer to noun table */
uint16_t _pVerbTable; /* pointer to verb table */
uint16_t _pExplicit; /* pointer to explicit action table */
uint16_t _pImplicit; /* pointer to implicit actions */
};
uint16_t fixAddress(uint16_t ina) {
return (ina - 0x380 + _G(_fileBaselineOffset));
}
uint16_t fixWord(uint16_t word) {
return (((word & 0xFF) << 8) | ((word >> 8) & 0xFF));
}
uint16_t getWord(uint8_t *mem) {
uint16_t x = *(uint16_t *)mem;
return fixWord(x);
}
void getMaxTI99Messages(DataHeader dh) {
uint8_t *msg;
uint16_t msg1;
msg = _G(_entireFile) + fixAddress(fixWord(dh._pMessage));
msg1 = fixAddress(getWord(msg));
_G(_maxMessages) = (msg1 - fixAddress(fixWord(dh._pMessage))) / 2;
}
void getMaxTI99Items(DataHeader dh) {
uint8_t *msg;
uint16_t msg1;
msg = _G(_entireFile) + fixAddress(fixWord(dh._pObjDescr));
msg1 = fixAddress(getWord(msg));
_G(_maxItemDescr) = (msg1 - fixAddress(fixWord(dh._pObjDescr))) / 2;
}
uint8_t *getTI994AWord(uint8_t* string, uint8_t** result, size_t* length) {
uint8_t *msg;
msg = string;
*length = msg[0];
if (*length == 0 || *length > 100) {
*length = 0;
*result = nullptr;
return nullptr;
}
msg++;
*result = new uint8_t[*length];
memcpy(*result, msg, *length);
msg += *length;
return (msg);
}
char *getTI994AString(uint16_t table, int tableOffset) {
uint8_t *msgx, *msgy, *nextword;
char *result;
uint16_t msg1, msg2;
uint8_t buffer[1024];
size_t length, totalLength = 0;
uint8_t *game = _G(_entireFile);
msgx = game + fixAddress(fixWord(table));
msgx += tableOffset * 2;
msg1 = fixAddress(getWord((uint8_t *)msgx));
msg2 = fixAddress(getWord((uint8_t *)msgx + 2));
msgy = game + msg2;
msgx = game + msg1;
while (msgx < msgy) {
msgx = getTI994AWord(msgx, &nextword, &length);
if (length == 0 || nextword == nullptr) {
return nullptr;
}
if (length > 100) {
delete[] nextword;
return nullptr;
}
memcpy(buffer + totalLength, nextword, length);
delete[] nextword;
totalLength += length;
if (totalLength > 1000)
break;
if (msgx < msgy)
buffer[totalLength++] = ' ';
}
if (totalLength == 0)
return nullptr;
totalLength++;
result = new char[totalLength];
memcpy(result, buffer, totalLength);
result[totalLength - 1] = '\0';
return result;
}
void loadTI994ADict(int vorn, uint16_t table, int numWords, Common::StringArray &dict) {
uint16_t *wtable;
int i;
int wordLen;
char *w1;
char *w2;
/* table is either verb or noun table */
wtable = (uint16_t *)(_G(_entireFile) + fixAddress(fixWord(table)));
for (i = 0; i <= numWords; i++) {
do {
w1 = (char *)_G(_entireFile) + fixAddress(getWord((uint8_t *)wtable + (i * 2)));
w2 = (char *)_G(_entireFile) + fixAddress(getWord((uint8_t *)wtable + ((1 + i) * 2)));
} while (w1 == w2);
wordLen = w2 - w1;
if (wordLen < 20) {
char *text = new char[wordLen + 1];
strncpy(text, w1, wordLen);
text[wordLen] = 0;
dict[i] = text;
}
}
}
void readTI99ImplicitActions(DataHeader dh) {
uint8_t *ptr, *implicitStart;
int loopFlag;
implicitStart = _G(_entireFile) + fixAddress(fixWord(dh._pImplicit));
ptr = implicitStart;
loopFlag = 0;
/* fall out, if no auto acts in the game. */
if (*ptr == 0x0)
loopFlag = 1;
while (loopFlag == 0) {
if (ptr[1] == 0)
loopFlag = 1;
/* skip code chunk */
ptr += 1 + ptr[1];
}
_G(_ti99ImplicitExtent) = MIN(_G(_fileLength), static_cast<size_t>(ptr - _G(_entireFile)));
if (_G(_ti99ImplicitExtent)) {
_G(_ti99ImplicitActions) = new uint8_t[_G(_ti99ImplicitExtent)];
memcpy(_G(_ti99ImplicitActions), implicitStart, _G(_ti99ImplicitExtent));
}
}
void readTI99ExplicitActions(DataHeader dh) {
uint8_t *ptr, *start, *end, *blockstart;
uint16_t address;
int loopFlag;
int i;
start = _G(_entireFile) + _G(_fileLength);
end = _G(_entireFile);
size_t explicitOffset = fixAddress(fixWord(dh._pExplicit));
blockstart = _G(_entireFile) + explicitOffset;
_G(_verbActionOffsets) = new uint8_t*[dh._numVerbs + 1];
for (i = 0; i <= dh._numVerbs; i++) {
ptr = blockstart;
address = getWord(ptr + ((i)*2));
_G(_verbActionOffsets)[i] = nullptr;
if (address != 0) {
ptr = _G(_entireFile) + fixAddress(address);
if (ptr < start)
start = ptr;
_G(_verbActionOffsets)[i] = ptr;
loopFlag = 0;
while (loopFlag != 1) {
if (ptr[1] == 0)
loopFlag = 1;
/* go to next block. */
ptr += 1 + ptr[1];
if (ptr > end)
end = ptr;
}
}
}
_G(_ti99ExplicitExtent) = end - start;
_G(_ti99ExplicitActions) = new uint8_t[_G(_ti99ExplicitExtent)];
memcpy(_G(_ti99ExplicitActions), start, _G(_ti99ExplicitExtent));
for (i = 0; i <= dh._numVerbs; i++) {
if (_G(_verbActionOffsets)[i] != nullptr) {
_G(_verbActionOffsets)[i] = _G(_ti99ExplicitActions) + (_G(_verbActionOffsets)[i] - start);
}
}
}
uint8_t *loadTitleScreen() {
char buf[3074];
int offset = 0;
uint8_t *p;
int lines;
/* title screen offset starts at 0x80 */
p = _G(_entireFile) + 0x80 + _G(_fileBaselineOffset);
if (static_cast<size_t>(p - _G(_entireFile)) > _G(_fileLength))
return nullptr;
int parens = 0;
for (lines = 0; lines < 24; lines++) {
for (int i = 0; i < 40; i++) {
char c = *(p++);
if (static_cast<size_t>(p - _G(_entireFile)) >= _G(_fileLength))
return nullptr;
if (c & 0x80) /* if not 7-bit ascii */
c = '?';
switch (c) {
case '\\':
c = ' ';
break;
case '(':
parens = 1;
break;
case ')':
if (!parens)
c = '@';
parens = 0;
break;
case '|':
if (*p != ' ')
c = 12;
break;
default:
break;
}
buf[offset++] = c;
if (offset >= 3072)
return nullptr;
}
buf[offset++] = '\n';
}
buf[offset] = '\0';
uint8_t *result = new uint8_t[offset + 1];
memcpy(result, buf, offset + 1);
return result;
}
int tryLoadingTI994A(DataHeader dh, int loud) {
int ni, nw, nr, mc, pr, tr, wl, lt, mn, trm;
int ct;
Room *rp;
Item *ip;
/* Load the header */
ni = dh._numObjects;
nw = MAX(dh._numVerbs, dh._numNouns);
nr = dh._redRoom;
mc = dh._maxItemsCarried;
pr = dh._beginLocn;
tr = 0;
trm = dh._treasureLocn;
wl = dh._cmdLength;
lt = fixWord(dh._lightTurns);
mn = _G(_maxMessages);
uint8_t *ptr = _G(_entireFile);
_G(_gameHeader)->_numItems = ni;
_G(_items).resize(ni + 1);
_G(_gameHeader)->_numActions = 0;
_G(_gameHeader)->_numWords = nw;
_G(_gameHeader)->_wordLength = wl;
_G(_verbs).resize(nw + 2);
_G(_nouns).resize(nw + 2);
_G(_gameHeader)->_numRooms = nr;
_G(_rooms).resize(nr + 1);
_G(_gameHeader)->_maxCarry = mc;
_G(_gameHeader)->_playerRoom = pr;
_G(_gameHeader)->_lightTime = lt;
_G(_lightRefill) = lt;
_G(_gameHeader)->_numMessages = mn;
_G(_messages).resize(mn + 1);
_G(_gameHeader)->_treasureRoom = trm;
int offset;
if (seekIfNeeded(fixAddress(fixWord(dh._pRoomDescr)), &offset, &ptr) == 0)
return 0;
ct = 0;
rp = &_G(_rooms)[0];
do {
char *res = getTI994AString(dh._pRoomDescr, ct);
rp->_text = res ? res : ".\0";
if (loud)
debug("Room %d: %s", ct, rp->_text.c_str());
rp->_image = 255;
ct++;
rp++;
} while (ct < nr + 1);
ct = 0;
while (ct < mn + 1) {
char *res = getTI994AString(dh._pMessage, ct);
_G(_messages)[ct] = res ? res : ".\0";
if (loud)
debug("Message %d: %s", ct, _G(_messages)[ct].c_str());
ct++;
}
ct = 0;
ip = &_G(_items)[0];
do {
char *res = getTI994AString(dh._pObjDescr, ct);
ip->_text = res ? res : ".\0";
if (ip->_text.size() && ip->_text[0] == '*')
tr++;
if (loud)
debug("Item %d: %s", ct, ip->_text.c_str());
ct++;
ip++;
} while (ct < ni + 1);
_G(_gameHeader)->_treasures = tr;
if (loud)
debug("Number of treasures %d", _G(_gameHeader)->_treasures);
if (seekIfNeeded(fixAddress(fixWord(dh._pRoomExit)), &offset, &ptr) == 0)
return 0;
ct = 0;
rp = &_G(_rooms)[0];
while (ct < nr + 1) {
for (int j = 0; j < 6; j++) {
rp->_exits[j] = *(ptr++ - _G(_fileBaselineOffset));
}
ct++;
rp++;
}
if (seekIfNeeded(fixAddress(fixWord(dh._pOrigItems)), &offset, &ptr) == 0)
return 0;
ct = 0;
ip = &_G(_items)[0];
while (ct < ni + 1) {
ip->_location = *(ptr++ - _G(_fileBaselineOffset));
ip->_initialLoc = ip->_location;
ip++;
ct++;
}
loadTI994ADict(0, dh._pVerbTable, dh._numVerbs + 1, _G(_verbs));
loadTI994ADict(1, dh._pNounTable, dh._numNouns + 1, _G(_nouns));
for (int i = 1; i <= dh._numNouns - dh._numVerbs; i++)
_G(_verbs)[dh._numVerbs + i] = ".\0";
for (int i = 1; i <= dh._numVerbs - dh._numNouns; i++)
_G(_nouns)[dh._numNouns + i] = ".\0";
if (loud) {
for (int i = 0; i <= _G(_gameHeader)->_numWords; i++)
debug("Verb %d: %s", i, _G(_verbs)[i].c_str());
for (int i = 0; i <= _G(_gameHeader)->_numWords; i++)
debug("Noun %d: %s", i, _G(_nouns)[i].c_str());
}
ct = 0;
ip = &_G(_items)[0];
if (seekIfNeeded(fixAddress(fixWord(dh._pObjLink)), &offset, &ptr) == 0) {
return 0;
}
int *objectlinks = new int[ni + 1];
do {
objectlinks[ct] = *(ptr++ - _G(_fileBaselineOffset));
if (objectlinks[ct] && objectlinks[ct] <= nw) {
ip->_autoGet = _G(_nouns)[objectlinks[ct]];
if (ct == 3 && scumm_strnicmp("bird", _G(_items)[ct]._text.c_str(), 4) == 0)
ip->_autoGet = "BIRD";
} else {
ip->_autoGet = "";
}
ct++;
ip++;
} while (ct < ni + 1);
delete[] objectlinks;
readTI99ImplicitActions(dh);
readTI99ExplicitActions(dh);
_G(_autoInventory) = 1;
_G(_sys)[INVENTORY] = "I'm carrying: ";
_G(_titleScreen) = (char *)loadTitleScreen();
delete[] _G(_entireFile);
for (int i = 0; i < MAX_SYSMESS && g_sysDictTI994A[i] != nullptr; i++) {
_G(_sys)[i] = g_sysDictTI994A[i];
}
_G(_options) |= TI994A_STYLE;
return TI994A;
}
void readHeader(Common::SeekableReadStream *f, DataHeader &dh) {
f->seek(0);
f->seek(_G(_fileBaselineOffset) + 0x8a0);
dh._numObjects = f->readByte();
dh._numVerbs = f->readByte();
dh._numNouns = f->readByte();
dh._redRoom = f->readByte();
dh._maxItemsCarried = f->readByte();
dh._beginLocn = f->readByte();
dh._numTreasures = f->readByte();
dh._cmdLength = f->readByte();
dh._lightTurns = f->readUint16LE();
dh._treasureLocn = f->readByte();
dh._strange = f->readByte();
dh._pObjTable = f->readUint16LE();
dh._pOrigItems = f->readUint16LE();
dh._pObjLink = f->readUint16LE();
dh._pObjDescr = f->readUint16LE();
dh._pMessage = f->readUint16LE();
dh._pRoomExit = f->readUint16LE();
dh._pRoomDescr = f->readUint16LE();
dh._pNounTable = f->readUint16LE();
dh._pVerbTable = f->readUint16LE();
dh._pExplicit = f->readUint16LE();
dh._pImplicit = f->readUint16LE();
}
GameIDType detectTI994A(Common::SeekableReadStream *f, uint8_t **sf, size_t *extent) {
int offset = findCode("\x30\x30\x30\x30\x00\x30\x30\x00\x28\x28", 0);
if (offset == -1)
return UNKNOWN_GAME;
_G(_fileBaselineOffset) = offset - 0x589;
DataHeader dh;
readHeader(f, dh);
getMaxTI99Messages(dh);
getMaxTI99Items(dh);
return static_cast<GameIDType>(tryLoadingTI994A(dh, 0));
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,48 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_LOAD_TI99_4A_H
#define GLK_SCOTT_LOAD_TI99_4A_H
#include "common/file.h"
#include "glk/scott/types.h"
#include "glk/scott/definitions.h"
namespace Glk {
namespace Scott {
GameIDType detectTI994A(Common::SeekableReadStream *f, uint8_t **sf, size_t *extent);
} // End of namespace Scott
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_RESOURCE_H
#define GLK_SCOTT_RESOURCE_H
#include "common/stream.h"
#include "glk/scott/definitions.h"
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
void readHeader(uint8_t *ptr);
int parseHeader(int *h, HeaderType type, int *ni, int *na, int *nw, int *nr, int *mc, int *pr, int *tr, int *wl, int *lt, int *mn, int *trm);
uint8_t *seekToPos(uint8_t *buf, size_t offset);
int seekIfNeeded(int expectedStart, int *offset, uint8_t **ptr);
int tryLoading(GameInfo info, int dictStart, int loud);
DictionaryType getId(int *offset);
int findCode(const char *x, int base);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,169 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/scott.h"
#include "glk/scott/globals.h"
#include "glk/scott/definitions.h"
#include "glk/scott/restore_state.h"
namespace Glk {
namespace Scott {
#define MAX_UNDOS 100
void saveUndo() {
if (_G(_justUndid)) {
_G(_justUndid) = 0;
return;
}
if (_G(_lastUndo) == nullptr) {
_G(_lastUndo) = saveCurrentState();
_G(_oldestUndo) = _G(_lastUndo);
_G(_numberOfUndos) = 1;
return;
}
if (_G(_numberOfUndos) == 0)
g_scott->fatal("Number of undos == 0 but _G(_lastUndo) != nullptr!");
_G(_lastUndo)->_nextState = saveCurrentState();
SavedState *current = _G(_lastUndo)->_nextState;
current->_previousState = _G(_lastUndo);
_G(_lastUndo) = current;
if (_G(_numberOfUndos) == MAX_UNDOS) {
SavedState *oldest = _G(_oldestUndo);
_G(_oldestUndo) = _G(_oldestUndo)->_nextState;
_G(_oldestUndo)->_previousState = nullptr;
delete[] oldest->_itemLocations;
delete oldest;
} else {
_G(_numberOfUndos)++;
}
}
void restoreUndo() {
if (_G(_justStarted)) {
g_scott->output(_G(_sys)[CANT_UNDO_ON_FIRST_TURN]);
return;
}
if (_G(_lastUndo) == nullptr || _G(_lastUndo)->_previousState == nullptr) {
g_scott->output(_G(_sys)[NO_UNDO_STATES]);
return;
}
SavedState *current = _G(_lastUndo);
_G(_lastUndo) = current->_previousState;
if (_G(_lastUndo)->_previousState == nullptr)
_G(_oldestUndo) = _G(_lastUndo);
restoreState(_G(_lastUndo));
g_scott->output(_G(_sys)[MOVE_UNDONE]);
delete[] current->_itemLocations;
delete current;
_G(_numberOfUndos)--;
_G(_justUndid) = 1;
}
void ramSave() {
if (_G(_ramSave) != nullptr) {
delete[] _G(_ramSave)->_itemLocations;
delete _G(_ramSave);
}
_G(_ramSave) = saveCurrentState();
g_scott->output(_G(_sys)[STATE_SAVED]);
}
void ramRestore() {
if (_G(_ramSave) == nullptr) {
g_scott->output(_G(_sys)[NO_SAVED_STATE]);
return;
}
restoreState(_G(_ramSave));
g_scott->output(_G(_sys)[STATE_RESTORED]);
saveUndo();
}
SavedState *saveCurrentState() {
SavedState *s = new SavedState;
for (int ct = 0; ct < 16; ct++) {
s->_counters[ct] = _G(_counters)[ct];
s->_roomSaved[ct] = _G(_roomSaved)[ct];
}
s->_bitFlags = _G(_bitFlags);
s->_currentLoc = MY_LOC;
s->_currentCounter = _G(_currentCounter);
s->_savedRoom = _G(_savedRoom);
s->_lightTime = _G(_gameHeader)->_lightTime;
s->_autoInventory = _G(_autoInventory);
s->_itemLocations = new uint8_t[_G(_gameHeader)->_numItems + 1];
for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
s->_itemLocations[ct] = _G(_items)[ct]._location;
}
s->_previousState = nullptr;
s->_nextState = nullptr;
return s;
}
void recoverFromBadRestore(SavedState *state) {
g_scott->output(_G(_sys)[BAD_DATA]);
restoreState(state);
delete state;
}
void restoreState(SavedState *state) {
for (int ct = 0; ct < 16; ct++) {
_G(_counters)[ct] = state->_counters[ct];
_G(_roomSaved)[ct] = state->_roomSaved[ct];
}
_G(_bitFlags) = state->_bitFlags;
MY_LOC = state->_currentLoc;
_G(_currentCounter) = state->_currentCounter;
_G(_savedRoom) = state->_savedRoom;
_G(_gameHeader)->_lightTime = state->_lightTime;
_G(_autoInventory) = state->_autoInventory;
for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
_G(_items)[ct]._location = state->_itemLocations[ct];
}
_G(_stopTime) = 1;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,66 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_RESTORESTATE_H
#define GLK_SCOTT_RESTORESTATE_H
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
struct SavedState {
int _counters[16];
int _roomSaved[16];
uint32_t _bitFlags = 0;
int _currentLoc = 0;
int _currentCounter = 0;
int _savedRoom = 0;
int _lightTime = 0;
int _autoInventory = 0;
uint8_t *_itemLocations = nullptr;
SavedState *_previousState = nullptr;
SavedState *_nextState = nullptr;
};
void saveUndo();
void restoreUndo();
void ramSave();
void ramRestore();
SavedState *saveCurrentState();
void recoverFromBadRestore(SavedState *state);
void restoreState(SavedState *state);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,145 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/ring_buffer.h"
namespace Glk {
namespace Scott {
// The hidden definition of our circular buffer structure
struct CircularBuf {
uint8_t *_buffer;
size_t _head;
size_t _tail;
size_t _max; // of the buffer
bool _full;
};
// Return a pointer to a struct instance
cbuf_handle_t circularBufInit(uint8_t *buffer, size_t size) {
cbuf_handle_t cbuf = new CircularBuf;
cbuf->_buffer = buffer;
cbuf->_max = size;
circularBufReset(cbuf);
return cbuf;
}
void circularBufReset(cbuf_handle_t me) {
me->_head = 0;
me->_tail = 0;
me->_full = false;
}
void circularBufFree(cbuf_handle_t me) {
delete me;
}
bool circularBufFull(cbuf_handle_t me) {
return me->_full;
}
bool circularBufEmpty(cbuf_handle_t me) {
return (!me->_full && (me->_head == me->_tail));
}
size_t circularBufCapacity(cbuf_handle_t me) {
return me->_max;
}
size_t circularBufSize(cbuf_handle_t me) {
size_t size = me->_max;
if (!me->_full) {
if (me->_head >= me->_tail) {
size = (me->_head - me->_tail);
} else {
size = (me->_max + me->_head - me->_tail);
}
}
return size;
}
static void advancePointer(cbuf_handle_t me) {
if (me->_full) {
if (++(me->_tail) == me->_max) {
me->_tail = 0;
}
}
if (++(me->_head) == me->_max) {
me->_head = 0;
}
me->_full = (me->_head == me->_tail);
}
static void retreatPointer(cbuf_handle_t me) {
me->_full = false;
if (++(me->_tail) == me->_max) {
me->_tail = 0;
}
}
int circularBufPut(cbuf_handle_t me, uint8_t x, uint8_t y) {
int r = -1;
if (!circularBufFull(me)) {
me->_buffer[me->_head] = x;
advancePointer(me);
if (!circularBufFull(me)) {
me->_buffer[me->_head] = y;
advancePointer(me);
r = 0;
}
}
return r;
}
int circularBufGet(cbuf_handle_t me, int *x, int *y) {
int r = -1;
if (!circularBufEmpty(me)) {
*x = me->_buffer[me->_tail];
retreatPointer(me);
if (!circularBufEmpty(me)) {
*y = me->_buffer[me->_tail];
retreatPointer(me);
r = 0;
}
}
return r;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,80 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_RINGBUFFER_H
#define GLK_SCOTT_RINGBUFFER_H
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
// Opaque circular buffer structure
struct CircularBuf;
// Handle type, the way users interact with the API
typedef CircularBuf *cbuf_handle_t;
/// Pass in a storage buffer and size
/// Returns a circular buffer handle
cbuf_handle_t circularBufInit(uint8_t *buffer, size_t size);
/// Free a circular buffer structure.
/// Does not free data buffer; owner is responsible for that
void circularBufFree(cbuf_handle_t me);
/// Reset the circular buffer to empty, head == tail
void circularBufReset(cbuf_handle_t me);
/// Rejects new data if the buffer is full
/// Returns 0 on success, -1 if buffer is full
int circularBufPut(cbuf_handle_t me, uint8_t x, uint8_t y);
/// Retrieve a value from the buffer
/// Returns 0 on success, -1 if the buffer is empty
int circularBufGet(cbuf_handle_t me, int *x, int *y);
/// Returns true if the buffer is empty
bool circularBufEmpty(cbuf_handle_t me);
/// Returns true if the buffer is full
bool circularBufFull(cbuf_handle_t me);
/// Returns the maximum capacity of the buffer
size_t circularBufCapacity(cbuf_handle_t me);
/// Returns the current number of elements in the buffer
size_t circularBufSize(cbuf_handle_t me);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,448 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/scott.h"
#include "glk/scott/types.h"
#include "glk/scott/globals.h"
#include "glk/scott/resource.h"
#include "glk/scott/saga_draw.h"
#include "glk/scott/decompress_text.h"
#include "glk/scott/robin_of_sherwood.h"
namespace Glk {
namespace Scott {
#define WATERFALL_ANIMATION_RATE 15
void animateLightning(int stage);
void sherwoodAction(int p) {
event_t ev;
switch (p) {
case 0:
// Flash animation
_G(_animationFlag) = 1;
g_scott->glk_request_timer_events(15);
while (_G(_animationFlag) < 11) {
g_scott->glk_select(&ev);
if (ev.type == evtype_Timer) {
_G(_animationFlag)++;
animateLightning(_G(_animationFlag));
}
}
break;
case 1:
g_scott->drawImage(0); /* Herne */
g_scott->display(_G(_bottomWindow), "\n%s\n", _G(_sys)[HIT_ENTER].c_str());
g_scott->hitEnter();
_G(_items)[39]._location = 79;
g_scott->look();
break;
case 2:
// Climbing tree in forest
_G(_savedRoom) = MY_LOC;
MY_LOC = 93;
g_scott->look();
break;
default:
error("sherwoodAction: Unhandled SherwoodAction %d!", p);
break;
}
}
int isForestLocation(void) { return (MY_LOC >= 11 && MY_LOC <= 73); }
#define TREES 0
#define BUSHES 1
void drawSherwood(int loc) {
g_scott->glk_window_clear(_G(_graphics));
int subimage_index = 0;
for (int i = 0; i < loc - 11; i++) {
// BUSHES type images are made up of 5 smaller images
int skip = 5;
if (_G(_forestImages)[subimage_index] < 128)
// Those with bit 7 unset have 10 (trees only) or 11 (trees with path)
skip = 11;
subimage_index += skip;
}
int forest_type = TREES;
int subimages;
// if bit 7 of the image index is set then this image is BUSHES
if (_G(_forestImages)[subimage_index] >= 128) {
forest_type = BUSHES;
subimages = 5;
// if the last subimage value is 255, there is no path
} else if (_G(_forestImages)[subimage_index + 10] == 0xff) {
// Trees only
subimages = 10;
} else {
// Trees with path
subimages = 11;
}
int xpos = 0, ypos = 0, image_number;
for (int i = 0; i < subimages; i++) {
if (forest_type == TREES) {
if (i >= 8) {
if (i == 8) { // Undergrowth
ypos = 7;
xpos = 0;
} else if (i == 9) { // _G(_bottomWindow) path
ypos = 10;
xpos = 0;
} else { // Forward path
ypos = 7;
xpos = 12;
}
} else { // Trees (every tree image is 4 characters wide)
ypos = 0;
xpos = i * 4;
}
}
image_number = _G(_forestImages)[subimage_index++] & 127;
drawSagaPictureAtPos(image_number, xpos, ypos);
if (forest_type == BUSHES) {
xpos += _G(_images)[image_number]._width;
}
}
}
void animateWaterfall(int stage) {
rectFill(88, 16, 48, 64, _G(_whiteColour));
for (int line = 2; line < 10; line++) {
for (int col = 11; col < 17; col++) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
if ((_G(_screenchars)[col + line * 32][i] & (1 << j)) != 0) {
int ypos = line * 8 + i + stage;
if (ypos > 79)
ypos = ypos - 64;
putPixel(col * 8 + j, ypos, _G(_blueColour));
}
}
}
}
void animateWaterfallCave(int stage) {
rectFill(248, 24, 8, 64, _G(_whiteColour));
for (int line = 3; line < 11; line++) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
if ((_G(_screenchars)[31 + line * 32][i] & (1 << j)) != 0) {
int ypos = line * 8 + i + stage;
if (ypos > 87)
ypos = ypos - 64;
putPixel(248 + j, ypos, _G(_blueColour));
}
}
}
void animateLightning(int stage) {
// swich blue and bright yellow
if (_G(_palChosen) == C64B)
switchPalettes(6, 7);
else {
switchPalettes(1, 14);
switchPalettes(9, 6);
}
drawSagaPictureNumber(77);
if (stage == 11) {
g_scott->glk_request_timer_events(0);
} else if (stage == 3) {
g_scott->glk_request_timer_events(700);
} else {
g_scott->glk_request_timer_events(40);
}
}
void robinOfSherwoodLook(void) {
if (!isForestLocation()) {
if (_G(_rooms)[MY_LOC]._image == 255) {
g_scott->closeGraphicsWindow();
} else {
g_scott->drawImage(_G(_rooms)[MY_LOC]._image);
for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++)
if (_G(_items)[ct]._image) {
if ((_G(_items)[ct]._flag & 127) == MY_LOC && _G(_items)[ct]._location == MY_LOC) {
g_scott->drawImage(_G(_items)[ct]._image);
}
}
}
}
if (MY_LOC == 82) // Dummy room where exit from Up a tree goes
MY_LOC = _G(_savedRoom);
if (MY_LOC == 93) // Up a tree
for (int i = 0; i < _G(_gameHeader)->_numItems; i++)
if (_G(_items)[i]._location == 93)
_G(_items)[i]._location = _G(_savedRoom);
if (MY_LOC == 7 && _G(_items)[62]._location == 7) // Left bedroom, open treasure chest
g_scott->drawImage(70);
if (isForestLocation()) {
g_scott->openGraphicsWindow();
drawSherwood(MY_LOC);
if (_G(_items)[36]._location == MY_LOC) {
//"Gregory the tax collector with his horse and cart"
g_scott->drawImage(15); // Horse and cart
g_scott->drawImage(3); // Sacks of grain
}
if (_G(_items)[60]._location == MY_LOC || _G(_items)[77]._location == MY_LOC) {
// "A serf driving a horse and cart"
g_scott->drawImage(15); // Horse and cart
g_scott->drawImage(12); // Hay
}
if (MY_LOC == 73) {
// Outlaws camp
g_scott->drawImage(36); // Campfire
}
}
if (MY_LOC == 86 || MY_LOC == 79) {
g_scott->glk_request_timer_events(WATERFALL_ANIMATION_RATE);
}
}
void updateRobinOfSherwoodAnimations(void) {
_G(_animationFlag)++;
if (_G(_animationFlag) > 63)
_G(_animationFlag) = 0;
if (MY_LOC == 86 || MY_LOC == 79 || MY_LOC == 84) {
/* If we're in room 84, the stone circle, we just */
/* want the timer to not switch off */
if (MY_LOC == 86) {
animateWaterfall(_G(_animationFlag));
} else if (MY_LOC == 79) {
animateWaterfallCave(_G(_animationFlag));
}
} else {
g_scott->glk_request_timer_events(0);
}
}
GameIDType loadExtraSherwoodData(void) {
int offset = 0x3d99 + _G(_fileBaselineOffset);
uint8_t *ptr;
/* Load the room images */
ptr = seekToPos(_G(_entireFile), offset);
if (ptr == 0)
return UNKNOWN_GAME;
int ct;
Room *rp = &_G(_rooms)[0];
for (ct = 0; ct <= _G(_gameHeader)->_numRooms; ct++) {
rp->_image = *(ptr++);
rp++;
if (ct == 10) {
for (int i = 0; i < 63; i++) {
rp++;
ct++;
}
}
}
ct = 0;
rp = &_G(_rooms)[0];
//int actual_room_number = 0;
ptr = seekToPos(_G(_entireFile), 0x5b7e + _G(_fileBaselineOffset));
if (ptr == 0)
return UNKNOWN_GAME;
do {
rp->_text = decompressText(ptr, ct);
rp->_text.toLowercase();
ct++;
//actual_room_number++;
if (ct == 11) {
for (int i = 0; i < 61; i++) {
rp++;
rp->_text = "in Sherwood Forest";
//actual_room_number++;
}
}
rp++;
} while (ct < 33);
for (int i = I_DONT_UNDERSTAND; i <= THATS_BEYOND_MY_POWER; i++)
_G(_sys)[i] = _G(_systemMessages)[4 - I_DONT_UNDERSTAND + i];
for (int i = YOU_SEE; i <= HIT_ENTER; i++)
_G(_sys)[i] = _G(_systemMessages)[15 - YOU_SEE + i];
_G(_sys)[OK] = _G(_systemMessages)[2];
_G(_sys)[TAKEN] = _G(_systemMessages)[2];
_G(_sys)[DROPPED] = _G(_systemMessages)[2];
_G(_sys)[PLAY_AGAIN] = _G(_systemMessages)[3];
_G(_sys)[YOURE_CARRYING_TOO_MUCH] = _G(_systemMessages)[21];
_G(_sys)[YOU_CANT_GO_THAT_WAY] = _G(_systemMessages)[12];
_G(_sys)[YOU_ARE] = _G(_systemMessages)[13];
_G(_sys)[EXITS_DELIMITER] = " ";
_G(_sys)[MESSAGE_DELIMITER] = ". ";
ptr = seekToPos(_G(_entireFile), 0x3b6e + _G(_fileBaselineOffset));
if (ptr == 0)
return UNKNOWN_GAME;
int cells = 555;
_G(_forestImages) = new uint8_t[cells];
for (int i = 0; i < cells; i++)
_G(_forestImages)[i] = *(ptr++);
return ROBIN_OF_SHERWOOD;
}
GameIDType loadExtraSherwoodData64(void) {
// white_colour = 1;
// blue = 6;
int offset = 0x1ffd + _G(_fileBaselineOffset);
uint8_t *ptr;
/* Load the room images */
ptr = seekToPos(_G(_entireFile), offset);
if (ptr == 0)
return UNKNOWN_GAME;
int ct;
Room *rp = &_G(_rooms)[0];
for (ct = 0; ct <= _G(_gameHeader)->_numRooms; ct++) {
rp->_image = *(ptr++);
rp++;
if (ct == 10) {
for (int i = 0; i < 63; i++) {
rp++;
ct++;
}
}
}
ct = 0;
rp = &_G(_rooms)[0];
//int actual_room_number = 0;
offset = 0x402e + _G(_fileBaselineOffset);
ptr = seekToPos(_G(_entireFile), offset);
if (ptr == 0)
return UNKNOWN_GAME;
do {
rp->_text = decompressText(ptr, ct);
rp->_text.toLowercase();
ct++;
//actual_room_number++;
if (ct == 11) {
for (int i = 0; i < 61; i++) {
rp++;
rp->_text = "in Sherwood Forest";
//actual_room_number++;
}
}
rp++;
} while (ct < 33);
SysMessageType messagekey[] = {NORTH,
SOUTH,
EAST,
WEST,
UP,
DOWN,
EXITS,
YOU_SEE,
YOU_ARE,
HIT_ENTER,
YOU_CANT_GO_THAT_WAY,
OK,
WHAT_NOW,
HUH,
YOU_HAVE_IT,
TAKEN,
DROPPED,
YOU_HAVENT_GOT_IT,
INVENTORY,
YOU_DONT_SEE_IT,
THATS_BEYOND_MY_POWER,
DIRECTION,
YOURE_CARRYING_TOO_MUCH,
PLAY_AGAIN,
RESUME_A_SAVED_GAME,
YOU_CANT_DO_THAT_YET,
I_DONT_UNDERSTAND,
NOTHING};
for (int i = 0; i < 26; i++) {
_G(_sys)[messagekey[i]] = _G(_systemMessages)[i];
}
_G(_sys)[HIT_ENTER] = _G(_systemMessages)[30];
_G(_sys)[WHAT] = _G(_systemMessages)[13];
_G(_sys)[EXITS_DELIMITER] = " ";
_G(_sys)[MESSAGE_DELIMITER] = ". ";
offset = 0x2300 + _G(_fileBaselineOffset);
ptr = seekToPos(_G(_entireFile), offset);
if (ptr == 0)
return UNKNOWN_GAME;
int cells = 555;
_G(_forestImages) = new uint8_t[cells];
for (int i = 0; i < cells; i++) {
_G(_forestImages)[i] = *(ptr++);
}
return ROBIN_OF_SHERWOOD_C64;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,51 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_ROBIN_OF_SHERWOOD_H
#define GLK_SCOTT_ROBIN_OF_SHERWOOD_H
#include "glk/scott/definitions.h"
namespace Glk {
namespace Scott {
void updateRobinOfSherwoodAnimations();
void robinOfSherwoodLook();
GameIDType loadExtraSherwoodData();
GameIDType loadExtraSherwoodData64();
int isForestLocation();
void sherwoodAction(int p);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,727 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/scott.h"
#include "glk/scott/definitions.h"
#include "glk/scott/resource.h"
#include "glk/scott/globals.h"
#include "glk/scott/saga_draw.h"
namespace Glk {
namespace Scott {
#define INVALID_COLOR 16
void rot90(uint8_t character[]) {
int32_t i, j;
uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
if ((character[j] & (1 << i)) != 0)
work2[7 - i] += 1 << j;
for (i = 0; i < 8; i++)
character[i] = work2[i];
}
void rot180(uint8_t character[]) {
int32_t i, j;
uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
if ((character[i] & (1 << j)) != 0)
work2[7 - i] += 1 << (7 - j);
for (i = 0; i < 8; i++)
character[i] = work2[i];
}
void rot270(uint8_t character[]) {
int32_t i, j;
uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
if ((character[j] & (1 << i)) != 0)
work2[i] += 1 << (7 - j);
for (i = 0; i < 8; i++)
character[i] = work2[i];
}
void flip(uint8_t character[]) {
int32_t i, j;
uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
if ((character[i] & (1 << j)) != 0)
work2[i] += 1 << (7 - j);
for (i = 0; i < 8; i++)
character[i] = work2[i];
}
void background(int32_t x, int32_t y, int32_t color) {
/* Draw the background */
rectFill(x * 8, y * 8, 8, 8, color);
}
void plotsprite(int32_t character, int32_t x, int32_t y, int32_t fg, int32_t bg) {
int32_t i, j;
background(x, y, bg);
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++)
if ((_G(_screenchars)[character][i] & (1 << j)) != 0)
putPixel(x * 8 + j, y * 8 + i, fg);
}
}
void transform(int32_t character, int32_t flipMode, int32_t ptr) {
if (character > 255)
return;
uint8_t work[8];
int32_t i;
#ifdef DRAWDEBUG
debug("Plotting char: %d with flip: %02x (%s) at %d: %d,%d\n",
character, flip_mode, flipdescription[(flip_mode & 48) >> 4],
ptr, ptr % 0x20, ptr / 0x20);
#endif
// first copy the character into work
for (i = 0; i < 8; i++)
work[i] = _G(_sprite)[character][i];
// Now flip it
if ((flipMode & 0x30) == 0x10) {
rot90(work);
}
if ((flipMode & 0x30) == 0x20) {
rot180(work);
}
if ((flipMode & 0x30) == 0x30) {
rot270(work);
}
if ((flipMode & 0x40) != 0) {
flip(work);
}
flip(work);
// Now mask it onto the previous character
for (i = 0; i < 8; i++) {
if ((flipMode & 0x0c) == 12)
_G(_screenchars)[ptr][i] ^= work[i];
else if ((flipMode & 0x0c) == 8)
_G(_screenchars)[ptr][i] &= work[i];
else if ((flipMode & 0x0c) == 4)
_G(_screenchars)[ptr][i] |= work[i];
else
_G(_screenchars)[ptr][i] = work[i];
}
}
uint8_t *drawSagaPictureFromData(uint8_t *dataptr, int xSize, int ySize, int xOff, int yOff) {
int32_t offset = 0, cont = 0;
int32_t i, x, y, mask_mode;
uint8_t data, data2, old = 0;
int32_t ink[0x22][14], paper[0x22][14];
// uint8_t *origptr = dataptr;
int version = _G(_game)->_pictureFormatVersion;
offset = 0;
int32_t character = 0;
int32_t count;
do {
count = 1;
/* first handle mode */
data = *dataptr++;
if (data < 0x80) {
if (character > 127 && version > 2) {
data += 128;
}
character = data;
#ifdef DRAWDEBUG
debug("******* SOLO CHARACTER: %04x\n", character);
#endif
transform(character, 0, offset);
offset++;
if (offset > 767)
break;
} else {
// first check for a count
if ((data & 2) == 2) {
count = (*dataptr++) + 1;
}
// Next get character and plot (count) times
character = *dataptr++;
// Plot the initial character
if ((data & 1) == 1 && character < 128)
character += 128;
for (i = 0; i < count; i++)
transform(character, (data & 0x0c) ? (data & 0xf3) : data, offset + i);
// Now check for overlays
if ((data & 0xc) != 0) {
// We have overlays so grab each member of the stream and work out what
// to do with it
mask_mode = (data & 0xc);
data2 = *dataptr++;
old = data;
do {
cont = 0;
if (data2 < 0x80) {
if (version == 4 && (old & 1) == 1)
data2 += 128;
#ifdef DRAWDEBUG
debug("Plotting %d directly (overlay) at %d\n", data2, offset);
#endif
for (i = 0; i < count; i++)
transform(data2, old & 0x0c, offset + i);
} else {
character = *dataptr++;
if ((data2 & 1) == 1)
character += 128;
#ifdef DRAWDEBUG
debug("Plotting %d with flip %02x (%s) at %d %d\n", character, (data2 | mask_mode), flipdescription[((data2 | mask_mode) & 48) >> 4],
offset, count);
#endif
for (i = 0; i < count; i++)
transform(character, (data2 & 0xf3) | mask_mode, offset + i);
if ((data2 & 0x0c) != 0) {
mask_mode = (data2 & 0x0c);
old = data2;
cont = 1;
data2 = *dataptr++;
}
}
} while (cont != 0);
}
offset += count;
}
} while (offset < xSize * ySize);
y = 0;
x = 0;
uint8_t colour = 0;
// Note version 3 count is inverse it is repeat previous colour
// Whilst version0-2 count is repeat next character
while (y < ySize) {
if (dataptr > _G(_entireFile) && static_cast<size_t>(dataptr - _G(_entireFile)) > _G(_fileLength))
return dataptr - 1;
data = *dataptr++;
if ((data & 0x80)) {
count = (data & 0x7f) + 1;
if (version >= 3) {
count--;
} else {
colour = *dataptr++;
}
} else {
count = 1;
colour = data;
}
while (count > 0) {
// Now split up depending on which version we're using
// For version 3+
if (_G(_drawToBuffer))
_G(_buffer)[(yOff + y) * 32 + (xOff + x)][8] = colour;
else {
if (version > 2) {
if (x > 33)
return nullptr;
// ink is 0-2, screen is 3-5, 6 in bright flag
ink[x][y] = colour & 0x07;
paper[x][y] = (colour & 0x38) >> 3;
if ((colour & 0x40) == 0x40) {
paper[x][y] += 8;
ink[x][y] += 8;
}
} else {
if (x > 33)
return nullptr;
paper[x][y] = colour & 0x07;
ink[x][y] = ((colour & 0x70) >> 4);
if ((colour & 0x08) == 0x08 || version < 2) {
paper[x][y] += 8;
ink[x][y] += 8;
}
}
}
x++;
if (x == xSize) {
x = 0;
y++;
}
count--;
}
}
offset = 0;
int32_t xoff2;
for (y = 0; y < ySize; y++)
for (x = 0; x < xSize; x++) {
xoff2 = xOff;
if (version > 0 && version < 3)
xoff2 = xOff - 4;
if (_G(_drawToBuffer)) {
for (i = 0; i < 8; i++)
_G(_buffer)[(y + yOff) * 32 + x + xoff2][i] = _G(_screenchars)[offset][i];
} else {
plotsprite(offset, x + xoff2, y + yOff, remap(ink[x][y]), remap(paper[x][y]));
}
#ifdef DRAWDEBUG
debug("(gfx#:plotting %d,%d:paper=%s,ink=%s)\n", x + xoff2, y + yoff,
colortext(remap(paper[x][y])), colortext(remap(ink[x][y])));
#endif
offset++;
if (offset > 766)
break;
}
return dataptr;
}
void drawSagaPictureNumber(int pictureNumber) {
int numgraphics = _G(_game)->_numberOfPictures;
if (pictureNumber >= numgraphics) {
error("drawSagaPictureNumber: Invalid image number % d !Last image: % d", pictureNumber, numgraphics - 1);
return;
}
Image img = _G(_images)[pictureNumber];
if (img._imageData == nullptr)
return;
drawSagaPictureFromData(img._imageData, img._width, img._height, img._xOff, img._yOff);
}
void drawSagaPictureAtPos(int pictureNumber, int x, int y) {
Image img = _G(_images)[pictureNumber];
drawSagaPictureFromData(img._imageData, img._width, img._height, x, y);
}
void drawSagaPictureFromBuffer() {
for (int line = 0; line < 12; line++) {
for (int col = 0; col < 32; col++) {
uint8_t colour = _G(_buffer)[col + line * 32][8];
int paper = (colour >> 3) & 0x7;
paper += 8 * ((colour & 0x40) == 0x40);
paper = remap(paper);
int ink = (colour & 0x7);
ink += 8 * ((colour & 0x40) == 0x40);
ink = remap(ink);
background(col, line, paper);
for (int i = 0; i < 8; i++) {
if (_G(_buffer)[col + line * 32][i] == 0)
continue;
if (_G(_buffer)[col + line * 32][i] == 255) {
glui32 glk_color = (_G(_pal)[ink][0] << 16) | (_G(_pal)[ink][1] << 8) | _G(_pal)[ink][2];
g_scott->glk_window_fill_rect(_G(_graphics), glk_color, col * 8 * _G(_pixelSize) + _G(_xOffset),
(line * 8 + i) * _G(_pixelSize), 8 * _G(_pixelSize), _G(_pixelSize));
continue;
}
for (int j = 0; j < 8; j++)
if ((_G(_buffer)[col + line * 32][i] & (1 << j)) != 0) {
int ypos = line * 8 + i;
putPixel(col * 8 + j, ypos, ink);
}
}
}
}
}
void sagaSetup(size_t imgOffset) {
int32_t i, y;
Common::Array<uint16_t> imageOffsets(_G(_game)->_numberOfPictures);
if (_G(_palChosen) == NO_PALETTE) {
_G(_palChosen) = _G(_game)->_palette;
}
if (_G(_palChosen) == NO_PALETTE) {
error("sagaSetup: unknown palette");
}
definePalette();
int version = _G(_game)->_pictureFormatVersion;
int32_t CHAR_START = _G(_game)->_startOfCharacters + _G(_fileBaselineOffset);
int32_t OFFSET_TABLE_START = _G(_game)->_startOfImageData + _G(_fileBaselineOffset);
if (_G(_game)->_startOfImageData == FOLLOWS) {
OFFSET_TABLE_START = CHAR_START + 0x800;
}
int32_t DATA_OFFSET = _G(_game)->_imageAddressOffset + _G(_fileBaselineOffset);
if (imgOffset)
DATA_OFFSET = imgOffset;
uint8_t *pos;
int numgraphics = _G(_game)->_numberOfPictures;
pos = seekToPos(_G(_entireFile), CHAR_START);
#ifdef DRAWDEBUG
debug("Grabbing Character details\n");
debug("Character Offset: %04x\n", CHAR_START - _G(_fileBaselineOffset));
#endif
for (i = 0; i < 256; i++) {
for (y = 0; y < 8; y++) {
_G(_sprite)[i][y] = *(pos++);
}
}
_G(_images).resize(numgraphics);
Image *img = &_G(_images)[0];
pos = seekToPos(_G(_entireFile), OFFSET_TABLE_START);
pos = seekToPos(_G(_entireFile), OFFSET_TABLE_START);
for (i = 0; i < numgraphics; i++) {
if (_G(_game)->_pictureFormatVersion == 0) {
uint16_t address;
if (i < 11) {
address = _G(_game)->_startOfImageData + (i * 2);
} else if (i < 28) {
address = _G(_hulkItemImageOffsets) + (i - 10) * 2;
} else if (i < 34) {
address = _G(_hulkLookImageOffsets) + (i - 28) * 2;
} else {
address = _G(_hulkSpecialImageOffsets) + (i - 34) * 2;
}
address += _G(_fileBaselineOffset);
address = _G(_entireFile)[address] + _G(_entireFile)[address + 1] * 0x100;
imageOffsets[i] = address + _G(_hulkImageOffset);
} else {
imageOffsets[i] = *(pos++);
imageOffsets[i] += *(pos++) * 0x100;
}
}
for (int picture_number = 0; picture_number < numgraphics; picture_number++) {
pos = seekToPos(_G(_entireFile), imageOffsets[picture_number] + DATA_OFFSET);
if (pos == 0)
return;
img->_width = *(pos++);
if (img->_width > 32)
img->_width = 32;
img->_height = *(pos++);
if (img->_height > 12)
img->_height = 12;
if (version > 0) {
img->_xOff = *(pos++);
if (img->_xOff > 32)
img->_xOff = 4;
img->_yOff = *(pos++);
if (img->_yOff > 12)
img->_yOff = 0;
} else {
if (picture_number > 9 && picture_number < 28) {
img->_xOff = _G(_entireFile)[_G(_hulkCoordinates) + picture_number - 10 + _G(_fileBaselineOffset)];
img->_yOff = _G(_entireFile)[_G(_hulkCoordinates) + 18 + picture_number - 10 + _G(_fileBaselineOffset)];
} else {
img->_xOff = img->_yOff = 0;
}
}
img->_imageData = pos;
img++;
}
}
void putPixel(glsi32 x, glsi32 y, int32_t color) {
int yOffset = 0;
glui32 glk_color = ((_G(_pal)[color][0] << 16)) | ((_G(_pal)[color][1] << 8)) | (_G(_pal)[color][2]);
g_scott->glk_window_fill_rect(_G(_graphics), glk_color, x * _G(_pixelSize) + _G(_xOffset),
y * _G(_pixelSize) + yOffset, _G(_pixelSize), _G(_pixelSize));
}
void rectFill(int32_t x, int32_t y, int32_t width, int32_t height, int32_t color) {
int yOffset = 0;
int bufferpos = (y / 8) * 32 + (x / 8);
if (bufferpos >= 0xD80)
return;
_G(_buffer)[bufferpos][8] = _G(_buffer)[bufferpos][8] | (color << 3);
glui32 glk_color = ((_G(_pal)[color][0] << 16)) | ((_G(_pal)[color][1] << 8)) | (_G(_pal)[color][2]);
g_scott->glk_window_fill_rect(_G(_graphics), glk_color, x * _G(_pixelSize) + _G(_xOffset),
y * _G(_pixelSize) + yOffset, width * _G(_pixelSize), height * _G(_pixelSize));
}
void switchPalettes(int pal1, int pal2) {
uint8_t temp[3];
temp[0] = _G(_pal)[pal1][0];
temp[1] = _G(_pal)[pal1][1];
temp[2] = _G(_pal)[pal1][2];
_G(_pal)[pal1][0] = _G(_pal)[pal2][0];
_G(_pal)[pal1][1] = _G(_pal)[pal2][1];
_G(_pal)[pal1][2] = _G(_pal)[pal2][2];
_G(_pal)[pal2][0] = temp[0];
_G(_pal)[pal2][1] = temp[1];
_G(_pal)[pal2][2] = temp[2];
}
void setColor(int32_t index, RGB *colour) {
_G(_pal)[index][0] = (*colour)[0];
_G(_pal)[index][1] = (*colour)[1];
_G(_pal)[index][2] = (*colour)[2];
}
void definePalette() {
/* set up the palette */
if (_G(_palChosen) == VGA) {
RGB black = {0, 0, 0};
RGB blue = {0, 0, 255};
RGB red = {255, 0, 0};
RGB magenta = {255, 0, 255};
RGB green = {0, 255, 0};
RGB cyan = {0, 255, 255};
RGB yellow = {255, 255, 0};
RGB white = {255, 255, 255};
RGB brblack = {0, 0, 0};
RGB brblue = {0, 0, 255};
RGB brred = {255, 0, 0};
RGB brmagenta = {255, 0, 255};
RGB brgreen = {0, 255, 0};
RGB brcyan = {0, 255, 255};
RGB bryellow = {255, 255, 0};
RGB brwhite = {255, 255, 255};
setColor(0, &black);
setColor(1, &blue);
setColor(2, &red);
setColor(3, &magenta);
setColor(4, &green);
setColor(5, &cyan);
setColor(6, &yellow);
setColor(7, &white);
setColor(8, &brblack);
setColor(9, &brblue);
setColor(10, &brred);
setColor(11, &brmagenta);
setColor(12, &brgreen);
setColor(13, &brcyan);
setColor(14, &bryellow);
setColor(15, &brwhite);
} else if (_G(_palChosen) == ZX) {
/* corrected Sinclair ZX palette (pretty dull though) */
RGB black = {0, 0, 0};
RGB blue = {0, 0, 154};
RGB red = {154, 0, 0};
RGB magenta = {154, 0, 154};
RGB green = {0, 154, 0};
RGB cyan = {0, 154, 154};
RGB yellow = {154, 154, 0};
RGB white = {154, 154, 154};
RGB brblack = {0, 0, 0};
RGB brblue = {0, 0, 170};
RGB brred = {186, 0, 0};
RGB brmagenta = {206, 0, 206};
RGB brgreen = {0, 206, 0};
RGB brcyan = {0, 223, 223};
RGB bryellow = {239, 239, 0};
RGB brwhite = {255, 255, 255};
setColor(0, &black);
setColor(1, &blue);
setColor(2, &red);
setColor(3, &magenta);
setColor(4, &green);
setColor(5, &cyan);
setColor(6, &yellow);
setColor(7, &white);
setColor(8, &brblack);
setColor(9, &brblue);
setColor(10, &brred);
setColor(11, &brmagenta);
setColor(12, &brgreen);
setColor(13, &brcyan);
setColor(14, &bryellow);
setColor(15, &brwhite);
_G(_whiteColour) = 15;
_G(_blueColour) = 9;
_G(_diceColour) = 0xff0000;
} else if (_G(_palChosen) == ZXOPT) {
/* optimized but not realistic Sinclair ZX palette (SPIN emu) */
RGB black = {0, 0, 0};
RGB blue = {0, 0, 202};
RGB red = {202, 0, 0};
RGB magenta = {202, 0, 202};
RGB green = {0, 202, 0};
RGB cyan = {0, 202, 202};
RGB yellow = {202, 202, 0};
RGB white = {202, 202, 202};
/*
old David Lodge palette:
RGB black = { 0, 0, 0 };
RGB blue = { 0, 0, 214 };
RGB red = { 214, 0, 0 };
RGB magenta = { 214, 0, 214 };
RGB green = { 0, 214, 0 };
RGB cyan = { 0, 214, 214 };
RGB yellow = { 214, 214, 0 };
RGB white = { 214, 214, 214 };
*/
RGB brblack = {0, 0, 0};
RGB brblue = {0, 0, 255};
RGB brred = {255, 0, 20};
RGB brmagenta = {255, 0, 255};
RGB brgreen = {0, 255, 0};
RGB brcyan = {0, 255, 255};
RGB bryellow = {255, 255, 0};
RGB brwhite = {255, 255, 255};
setColor(0, &black);
setColor(1, &blue);
setColor(2, &red);
setColor(3, &magenta);
setColor(4, &green);
setColor(5, &cyan);
setColor(6, &yellow);
setColor(7, &white);
setColor(8, &brblack);
setColor(9, &brblue);
setColor(10, &brred);
setColor(11, &brmagenta);
setColor(12, &brgreen);
setColor(13, &brcyan);
setColor(14, &bryellow);
setColor(15, &brwhite);
_G(_whiteColour) = 15;
_G(_blueColour) = 9;
_G(_diceColour) = 0xff0000;
} else if ((_G(_palChosen) == C64A) || (_G(_palChosen) == C64B)) {
/* and now: C64 palette (pepto/VICE) */
RGB black = {0, 0, 0};
RGB white = {255, 255, 255};
RGB red = {191, 97, 72};
RGB cyan = {153, 230, 249};
RGB purple = {177, 89, 185};
RGB green = {121, 213, 112};
RGB blue = {95, 72, 233};
RGB yellow = {247, 255, 108};
RGB orange = {186, 134, 32};
RGB brown = {116, 105, 0};
RGB lred = {180, 105, 164};
RGB dgrey = {69, 69, 69};
RGB grey = {167, 167, 167};
RGB lgreen = {154, 210, 134};
RGB lblue = {162, 143, 255};
RGB lgrey = {150, 150, 150};
setColor(0, &black);
setColor(1, &white);
setColor(2, &red);
setColor(3, &cyan);
setColor(4, &purple);
setColor(5, &green);
setColor(6, &blue);
setColor(7, &yellow);
setColor(8, &orange);
setColor(9, &brown);
setColor(10, &lred);
setColor(11, &dgrey);
setColor(12, &grey);
setColor(13, &lgreen);
setColor(14, &lblue);
setColor(15, &lgrey);
_G(_whiteColour) = 1;
_G(_blueColour) = 6;
_G(_diceColour) = 0x5f48e9;
}
}
int32_t remap(int32_t color) {
int32_t mapcol;
if ((_G(_palChosen) == ZX) || (_G(_palChosen) == ZXOPT)) {
/* nothing to remap here; shows that the gfx were created on a ZX */
mapcol = (((color >= 0) && (color <= 15)) ? color : INVALID_COLOR);
} else if (_G(_palChosen) == C64A) {
/* remap A determined from Golden Baton, applies to S1/S3/S13 too (8col) */
int32_t c64remap[] = {0, 6, 2, 4, 5, 3, 7, 1, 8, 1, 1, 1, 7, 12, 8, 7};
mapcol = (((color >= 0) && (color <= 15)) ? c64remap[color] : INVALID_COLOR);
} else if (_G(_palChosen) == C64B) {
/* remap B determined from Spiderman (16col) */
int32_t c64remap[] = {0, 6, 9, 4, 5, 14, 8, 12, 0, 6, 2, 4, 5, 3, 7, 1};
mapcol = (((color >= 0) && (color <= 15)) ? c64remap[color] : INVALID_COLOR);
} else
mapcol = (((color >= 0) && (color <= 15)) ? color : INVALID_COLOR);
return (mapcol);
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,71 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_SAGADRAW_H
#define GLK_SCOTT_SAGADRAW_H
#include "glk/glk_types.h"
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
struct Image {
uint8_t *_imageData;
uint8_t _xOff;
uint8_t _yOff;
uint8_t _width;
uint8_t _height;
};
typedef uint8_t RGB[3];
typedef RGB PALETTE[16];
uint8_t *drawSagaPictureFromData(uint8_t *dataptr, int xSize, int ySize, int xOff, int yOff);
void drawSagaPictureNumber(int pictureNumber);
void drawSagaPictureAtPos(int pictureNumber, int x, int y);
void drawSagaPictureFromBuffer();
void flip(uint8_t character[]);
void sagaSetup(size_t imgOffset);
void putPixel(glsi32 x, glsi32 y, int32_t color);
void rectFill(int32_t x, int32_t y, int32_t width, int32_t height, int32_t color);
void switchPalettes(int pal1, int pal2);
void definePalette();
int32_t remap(int32_t color);
} // End of namespace Scott
} // End of namespace Glk
#endif

2179
engines/glk/scott/scott.cpp Normal file

File diff suppressed because it is too large Load Diff

269
engines/glk/scott/scott.h Normal file
View File

@@ -0,0 +1,269 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_H
#define GLK_SCOTT_H
/*
* Controlling block
*/
#include "common/scummsys.h"
#include "glk/glk_api.h"
#include "glk/scott/definitions.h"
#include "glk/scott/globals.h"
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
struct Command;
#define LIGHT_SOURCE 9 // Always 9 how odd
#define CARRIED 255 // Carried
#define DESTROYED 0 // Destroyed
#define DARKBIT 15
#define LIGHTOUTBIT 16 // Light gone out
enum GameOption {
YOUARE = 1, ///< You are not I am
SCOTTLIGHT = 2, ///< Authentic Scott Adams light messages
DEBUGGING = 4, ///< Info from database load
TRS80_STYLE = 8, ///< Display in style used on TRS-80
PREHISTORIC_LAMP = 16, ///< Destroy the lamp (very old databases)
SPECTRUM_STYLE = 32, ///< Display in style used on ZX Spectrum
TI994A_STYLE = 64, ///< Display in style used on TI-99/4A
NO_DELAYS = 128, ///< Skip all pauses
FORCE_PALETTE_ZX = 256, ///< Force ZX Spectrum image palette
FORCE_PALETTE_C64 = 512, ///< Force CBM 64 image palette
FORCE_INVENTORY = 1024, ///< Inventory in upper window always on
FORCE_INVENTORY_OFF = 2048 ///< Inventory in upper window always off
};
#define GLK_BUFFER_ROCK 1
#define GLK_STATUS_ROCK 1010
#define GLK_GRAPHICS_ROCK 1020
#define TRS80_LINE "\n<------------------------------------------------------------>\n"
#define MY_LOC (_G(_gameHeader)->_playerRoom)
#define CURRENT_GAME (_G(_game->_gameID))
struct Header {
int _unknown;
int _numItems;
int _numActions;
int _numWords; ///< Smaller of verb/noun is padded to same size
int _numRooms;
int _maxCarry;
int _playerRoom;
int _treasures;
int _wordLength;
int _lightTime;
int _numMessages;
int _treasureRoom;
Header() : _unknown(0), _numItems(0), _numActions(0), _numWords(0), _numRooms(0),
_maxCarry(0), _playerRoom(0), _treasures(0), _wordLength(0), _lightTime(0),
_numMessages(0), _treasureRoom(0) {}
};
struct Action {
int _vocab;
int _condition[5];
int _action[2];
Action() : _vocab(0) {
Common::fill(&_condition[0], &_condition[5], 0);
Common::fill(&_action[0], &_action[2], 0);
}
};
struct Room {
Common::String _text;
int _exits[6];
byte _image;
Room() : _image(255) {
Common::fill(&_exits[0], &_exits[6], 0);
}
};
struct Item {
Common::String _text;
byte _location;
byte _initialLoc;
Common::String _autoGet;
byte _flag;
byte _image;
Item() : _location(0), _initialLoc(0), _flag(0), _image(0) {}
};
struct Tail {
int _version;
int _adventureNumber;
int _unknown;
Tail() : _version(0), _adventureNumber(0), _unknown(0) {}
};
/**
* Scott Adams game interpreter
*/
class Scott : public GlkAPI {
private:
Globals _globals;
char _nounText[16];
//int _width = 0; ///< Terminal width
int _topHeight = 0; ///< Height of top window
int _topWidth = 0;
bool _splitScreen = true;
int _saveSlot = -1; ///< Save slot when loading savegame from launcher
int _printLookToTranscript = 0;
int _pauseNextRoomDescription = 0;
strid_t _roomDescriptionStream = nullptr;
private:
/**
* Initialization code
*/
void initialize();
void clearScreen(void);
int matchUpItem(int noun, int loc);
Common::String readString(Common::SeekableReadStream *f);
void loadDatabase(Common::SeekableReadStream *f, bool loud);
int whichWord(const char *word, const Common::StringArray &list);
ActionResultType performLine(int ct);
ExplicitResultType performActions(int vb, int no);
void readInts(Common::SeekableReadStream *f, size_t count, ...);
void writeToRoomDescriptionStream(const char *fmt, ...);
void flushRoomDescription(char *buf);
void printWindowDelimiter();
void listExits();
void listExitsSpectrumStyle();
void listInventoryInUpperWindow();
int itemEndsWithPeriod(int item);
winid_t findGlkWindowWithRock(glui32 rock);
glui32 optimalPictureSize(uint *width, uint *height);
void openTopWindow();
void cleanupAndExit();
void drawBlack();
void drawRoomImage();
void restartGame();
void transcriptOn();
void transcriptOff();
int yesOrNo();
void lookWithPause();
void printTakenOrDropped(int index);
void printTitleScreenBuffer();
void printTitleScreenGrid();
public:
void drawImage(int image);
void output(const Common::String &a);
void output(const Common::U32String &a);
void outputNumber(int a);
void display(winid_t w, const char *fmt, ...);
template<class... TParam> void display(winid_t w, const Common::U32String &fmt, TParam... param);
void fatal(const char *x);
void hitEnter();
void updates(event_t ev);
const char *mapSynonym(int noun);
int performExtraCommand(int extraStopTime);
void look(void);
void openGraphicsWindow();
void closeGraphicsWindow();
void listInventory();
void updateSettings();
uint getRandomNumber(uint max);
bool randomPercent(uint n);
int countCarried(void);
void playerIsDead();
void doneIt();
void putItemAInRoomB(int itemA, int roomB);
int printScore();
void swapItemLocations(int itemA, int itemB);
void moveItemAToLocOfItemB(int itemA, int itemB);
void goToStoredLoc();
void swapLocAndRoomFlag(int index);
void swapCounters(int index);
void printNoun();
void delay(double seconds);
void printMessage(int index);
public:
/**
* Constructor
*/
Scott(OSystem *syst, const GlkGameDescription &gameDesc);
/**
* Returns the running interpreter type
*/
InterpreterType getInterpreterType() const override { return INTERPRETER_SCOTT; }
/**
* Execute the game
*/
void runGame() override;
/**
* Load a savegame from the passed Quetzal file chunk stream
*/
Common::Error readSaveData(Common::SeekableReadStream *rs) override;
/**
* Save the game. The passed write stream represents access to the UMem chunk
* in the Quetzal save file that will be created
*/
Common::Error writeGameData(Common::WriteStream *ws) override;
private:
void display_u32_internal(winid_t w, const Common::U32String *fmt, ...);
};
template<class... TParam>
inline void Scott::display(winid_t w, const Common::U32String &fmt, TParam... param) {
display_u32_internal(w, &fmt, Common::forward<TParam>(param)...);
}
extern Scott *g_scott;
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,859 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/scott.h"
#include "glk/scott/saga_draw.h"
#include "glk/scott/resource.h"
#include "glk/scott/decompress_text.h"
#include "glk/scott/seas_of_blood.h"
namespace Glk {
namespace Scott {
#define VICTORY 0
#define LOSS 1
#define DRAW 2
#define FLEE 3
#define ERROR 99
int getEnemyStats(int *strike, int *stamina, int *boatFlag);
void battleLoop(int enemy, int strike, int stamina, int boatFlag);
void swapStaminaAndCrewStrength(void);
void bloodBattle(void);
void adventureSheet(void) {
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(_G(_bottomWindow)));
g_scott->glk_set_style(style_Preformatted);
g_scott->output("\nADVENTURE SHEET\n\n");
g_scott->output("SKILL :");
g_scott->outputNumber(9);
g_scott->output(" STAMINA :");
g_scott->outputNumber(_G(_counters)[3]);
g_scott->output("\nLOG :");
g_scott->outputNumber(_G(_counters)[6]);
if (_G(_counters)[6] < 10)
g_scott->output(" PROVISIONS :");
else
g_scott->output(" PROVISIONS :"); // one less space!
g_scott->outputNumber(_G(_counters)[5]);
g_scott->output("\nCREW STRIKE:");
g_scott->outputNumber(9);
g_scott->output(" CREW STRENGTH:");
g_scott->outputNumber(_G(_counters)[7]);
g_scott->output("\n\n * * * * * * * * * * * * * * * * * * * * *\n\n");
g_scott->listInventory();
g_scott->output("\n");
g_scott->glk_set_style(style_Normal);
}
void bloodAction(int p) {
switch (p) {
case 0:
break;
case 1:
// Update LOG
_G(_counters)[6]++;
break;
case 2:
// Battle
g_scott->look();
g_scott->output("You are attacked \n");
g_scott->output("<HIT ENTER> \n");
g_scott->hitEnter();
bloodBattle();
break;
default:
error("bloodAction: Unhandled special action %d", p);
break;
}
}
void mirrorLeftHalf(void) {
for (int line = 0; line < 12; line++) {
for (int col = 32; col > 16; col--) {
_G(_buffer)[line * 32 + col - 1][8] = _G(_buffer)[line * 32 + (32 - col)][8];
for (int pixrow = 0; pixrow < 8; pixrow++)
_G(_buffer)[line * 32 + col - 1][pixrow] = _G(_buffer)[line * 32 + (32 - col)][pixrow];
flip(_G(_buffer)[line * 32 + col - 1]);
}
}
}
void replaceColour(uint8_t before, uint8_t after) {
// I don't think any of the data has bit 7 set,
// so masking it is probably unnecessary, but this is what
// the original code does.
uint8_t beforeink = before & 7;
uint8_t afterink = after & 7;
uint8_t inkmask = 0x07;
uint8_t beforepaper = beforeink << 3;
uint8_t afterpaper = afterink << 3;
uint8_t papermask = 0x38;
for (int j = 0; j < 384; j++) {
if ((_G(_buffer)[j][8] & inkmask) == beforeink) {
_G(_buffer)[j][8] = (_G(_buffer)[j][8] & ~inkmask) | afterink;
}
if ((_G(_buffer)[j][8] & papermask) == beforepaper) {
_G(_buffer)[j][8] = (_G(_buffer)[j][8] & ~papermask) | afterpaper;
}
}
}
void drawColour(uint8_t x, uint8_t y, uint8_t colour, uint8_t length) {
for (int i = 0; i < length; i++) {
_G(_buffer)[y * 32 + x + i][8] = colour;
}
}
void makeLight(void) {
for (int i = 0; i < 384; i++) {
_G(_buffer)[i][8] = _G(_buffer)[i][8] | 0x40;
}
}
void flipImage(void) {
uint8_t mirror[384][9];
for (int line = 0; line < 12; line++) {
for (int col = 32; col > 0; col--) {
for (int pixrow = 0; pixrow < 9; pixrow++)
mirror[line * 32 + col - 1][pixrow] = _G(_buffer)[line * 32 + (32 - col)][pixrow];
flip(mirror[line * 32 + col - 1]);
}
}
memcpy(_G(_buffer), mirror, 384 * 9);
}
void drawObjectImage(uint8_t x, uint8_t y) {
for (int i = 0; i < _G(_gameHeader)->_numItems; i++) {
if (_G(_items)[i]._flag != MY_LOC)
continue;
if (_G(_items)[i]._location != MY_LOC)
continue;
drawSagaPictureAtPos(_G(_items)[i]._image, x, y);
_G(_shouldDrawObjectImages) = 0;
}
}
void drawBlood(int loc) {
memset(_G(_buffer), 0, 384 * 9);
uint8_t *ptr = _G(_bloodImageData);
for (int i = 0; i < loc; i++) {
while (*(ptr) != 0xff)
ptr++;
ptr++;
}
while (ptr < _G(_bloodImageData) + 2010) {
switch (*ptr) {
case 0xff:
if (loc == 13) {
_G(_buffer)[8 * 32 + 18][8] = _G(_buffer)[8 * 32 + 18][8] & ~0x40;
_G(_buffer)[8 * 32 + 17][8] = _G(_buffer)[8 * 32 + 17][8] & ~0x40;
_G(_buffer)[8 * 32 + 9][8] = _G(_buffer)[8 * 32 + 9][8] & ~0x40;
_G(_buffer)[8 * 32 + 10][8] = _G(_buffer)[8 * 32 + 10][8] & ~0x40;
}
return;
case 0xfe:
mirrorLeftHalf();
break;
case 0xfD:
replaceColour(*(ptr + 1), *(ptr + 2));
ptr += 2;
break;
case 0xfc: // Draw colour: x, y, attribute, length
drawColour(*(ptr + 1), *(ptr + 2), *(ptr + 3), *(ptr + 4));
ptr = ptr + 4;
break;
case 0xfb: // Make all screen colours bright
makeLight();
break;
case 0xfa: // Flip entire image horizontally
flipImage();
break;
case 0xf9: // Draw object image (if present) at x, y
drawObjectImage(*(ptr + 1), *(ptr + 2));
ptr = ptr + 2;
break;
default: // else draw image *ptr at x, y
drawSagaPictureAtPos(*ptr, *(ptr + 1), *(ptr + 2));
ptr = ptr + 2;
}
ptr++;
}
}
void seasOfBloodRoomImage(void) {
_G(_shouldDrawObjectImages) = 1;
drawBlood(MY_LOC);
for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++)
if (_G(_items)[ct]._image && _G(_shouldDrawObjectImages)) {
if ((_G(_items)[ct]._flag & 127) == MY_LOC && _G(_items)[ct]._location == MY_LOC) {
g_scott->drawImage(_G(_items)[ct]._image);
}
}
drawSagaPictureFromBuffer();
}
//static void SOBPrint(winid_t w, const char *fmt, ...)
//#ifdef __GNUC__
// __attribute__((__format__(__printf__, 2, 3)))
//#endif
// ;
static void SOBPrint(winid_t w, const char *fmt, ...) {
va_list ap;
char msg[2048];
int size = sizeof msg;
va_start(ap, fmt);
vsnprintf(msg, size, fmt, ap);
va_end(ap);
g_scott->glk_put_string_stream(g_scott->glk_window_get_stream(w), msg);
}
glui32 optimalDicePixelSize(glui32 *width, glui32 *height) {
int idealWidth = 8;
int idealHeight = 8;
*width = idealWidth;
*height = idealHeight;
int multiplier = 1;
uint graphwidth, graphheight;
g_scott->glk_window_get_size(_G(_leftDiceWin), &graphwidth, &graphheight);
multiplier = graphheight / idealHeight;
if ((glui32)(idealWidth * multiplier) > graphwidth)
multiplier = graphwidth / idealWidth;
if (multiplier < 2)
multiplier = 2;
multiplier = multiplier / 2;
*width = idealWidth * multiplier;
*height = idealHeight * multiplier;
return multiplier;
}
static void drawBorder(winid_t win) {
uint width, height;
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(win));
g_scott->glk_window_get_size(win, &width, &height);
height--;
width -= 2;
g_scott->glk_window_move_cursor(win, 0, 0);
g_scott->glk_put_char_uni(0x250F); // Top left corner
for (glui32 i = 1; i < width; i++)
g_scott->glk_put_char_uni(0x2501); // Top
g_scott->glk_put_char_uni(0x2513); // Top right corner
for (glui32 i = 1; i < height; i++) {
g_scott->glk_window_move_cursor(win, 0, i);
g_scott->glk_put_char_uni(0x2503);
g_scott->glk_window_move_cursor(win, width, i);
g_scott->glk_put_char_uni(0x2503);
}
g_scott->glk_window_move_cursor(win, 0, height);
g_scott->glk_put_char_uni(0x2517);
for (glui32 i = 1; i < width; i++)
g_scott->glk_put_char_uni(0x2501);
g_scott->glk_put_char_uni(0x251B);
}
static void redrawStaticText(winid_t win, int boatFlag) {
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(win));
g_scott->glk_window_move_cursor(win, 2, 4);
if (boatFlag) {
g_scott->glk_put_string("STRIKE :\n");
g_scott->glk_window_move_cursor(win, 2, 5);
g_scott->glk_put_string("CRW STR :");
} else {
g_scott->glk_put_string("SKILL :\n");
g_scott->glk_window_move_cursor(win, 2, 5);
g_scott->glk_put_string("STAMINA :");
}
if (win == _G(_battleRight)) {
uint width;
g_scott->glk_window_get_size(_G(_battleRight), &width, 0);
g_scott->glk_window_move_cursor(_G(_battleRight), width - 6, 1);
g_scott->glk_put_string("YOU");
}
}
static void redrawBattleScreen(int boatFlag) {
uint graphwidth, graphheight;
glui32 optimal_width, optimal_height;
g_scott->glk_window_get_size(_G(_leftDiceWin), &graphwidth, &graphheight);
_G(_dicePixelSize) = optimalDicePixelSize(&optimal_width, &optimal_height);
_G(_diceXOffset) = (graphwidth - optimal_width) / 2;
_G(_diceYOffset) = (graphheight - optimal_height - _G(_dicePixelSize)) / 2;
drawBorder(_G(_topWindow));
drawBorder(_G(_battleRight));
redrawStaticText(_G(_topWindow), boatFlag);
redrawStaticText(_G(_battleRight), boatFlag);
}
static void setupBattleScreen(int boatFlag) {
winid_t parent = g_scott->glk_window_get_parent(_G(_topWindow));
g_scott->glk_window_set_arrangement(parent, winmethod_Above | winmethod_Fixed, 7, _G(_topWindow));
g_scott->glk_window_clear(_G(_topWindow));
g_scott->glk_window_clear(_G(_bottomWindow));
_G(_battleRight) = g_scott->glk_window_open(_G(_topWindow), winmethod_Right | winmethod_Proportional,
50, wintype_TextGrid, 0);
_G(_leftDiceWin) = g_scott->glk_window_open(_G(_topWindow), winmethod_Right | winmethod_Proportional,
30, wintype_Graphics, 0);
_G(_rightDiceWin) = g_scott->glk_window_open(_G(_battleRight), winmethod_Left | winmethod_Proportional, 30,
wintype_Graphics, 0);
// Set the graphics window background to match the top window background, best as we can, and clear the window.
if (g_scott->glk_style_measure(_G(_topWindow), style_Normal, stylehint_BackColor,
&_G(_backgroundColour))) {
g_scott->glk_window_set_background_color(_G(_leftDiceWin), _G(_backgroundColour));
g_scott->glk_window_set_background_color(_G(_rightDiceWin), _G(_backgroundColour));
g_scott->glk_window_clear(_G(_leftDiceWin));
g_scott->glk_window_clear(_G(_rightDiceWin));
}
if (_G(_palChosen) == C64B)
_G(_diceColour) = 0x5f48e9;
else
_G(_diceColour) = 0xff0000;
redrawBattleScreen(boatFlag);
}
void bloodBattle(void) {
int enemy, strike, stamina, boatFlag;
enemy = getEnemyStats(&strike, &stamina, &boatFlag); // Determine their stats
if (enemy == 0) {
error("Seas of blood battle: No enemy in location?\n");
return;
}
setupBattleScreen(boatFlag);
battleLoop(enemy, strike, stamina, boatFlag); // Into the battle loops
if (boatFlag)
swapStaminaAndCrewStrength(); // Switch back stamina - crew strength
g_scott->glk_window_close(_G(_leftDiceWin), nullptr);
g_scott->glk_window_close(_G(_rightDiceWin), nullptr);
g_scott->glk_window_close(_G(_battleRight), nullptr);
g_scott->closeGraphicsWindow();
g_scott->openGraphicsWindow();
seasOfBloodRoomImage();
}
int getEnemyStats(int *strike, int *stamina, int *boatFlag) {
int enemy, i = 0;
while (i < 124) {
enemy = _G(_enemyTable)[i];
if (_G(_items)[enemy]._location == MY_LOC) {
i++;
*strike = _G(_enemyTable)[i++];
*stamina = _G(_enemyTable)[i++];
*boatFlag = _G(_enemyTable)[i];
if (*boatFlag) {
swapStaminaAndCrewStrength(); // Switch stamina - crew strength
}
return enemy;
}
i = i + 4; // Skip to next entry
}
return 0;
}
void drawRect(winid_t win, int32_t x, int32_t y, int32_t width, int32_t height,
int32_t color) {
g_scott->glk_window_fill_rect(win, color, x * _G(_dicePixelSize) + _G(_diceXOffset),
y * _G(_dicePixelSize) + _G(_diceYOffset),
width * _G(_dicePixelSize), height * _G(_dicePixelSize));
}
void drawGraphicalDice(winid_t win, int number) {
// The eye-less dice backgrounds consist of two rectangles on top of each
// other
drawRect(win, 1, 2, 7, 5, _G(_diceColour));
drawRect(win, 2, 1, 5, 7, _G(_diceColour));
switch (number + 1) {
case 1:
drawRect(win, 4, 4, 1, 1, _G(_backgroundColour));
break;
case 2:
drawRect(win, 2, 6, 1, 1, _G(_backgroundColour));
drawRect(win, 6, 2, 1, 1, _G(_backgroundColour));
break;
case 3:
drawRect(win, 2, 6, 1, 1, _G(_backgroundColour));
drawRect(win, 4, 4, 1, 1, _G(_backgroundColour));
drawRect(win, 6, 2, 1, 1, _G(_backgroundColour));
break;
case 4:
drawRect(win, 2, 6, 1, 1, _G(_backgroundColour));
drawRect(win, 6, 2, 1, 1, _G(_backgroundColour));
drawRect(win, 2, 2, 1, 1, _G(_backgroundColour));
drawRect(win, 6, 6, 1, 1, _G(_backgroundColour));
break;
case 5:
drawRect(win, 2, 6, 1, 1, _G(_backgroundColour));
drawRect(win, 6, 2, 1, 1, _G(_backgroundColour));
drawRect(win, 2, 2, 1, 1, _G(_backgroundColour));
drawRect(win, 6, 6, 1, 1, _G(_backgroundColour));
drawRect(win, 4, 4, 1, 1, _G(_backgroundColour));
break;
case 6:
drawRect(win, 2, 6, 1, 1, _G(_backgroundColour));
drawRect(win, 6, 2, 1, 1, _G(_backgroundColour));
drawRect(win, 2, 2, 1, 1, _G(_backgroundColour));
drawRect(win, 2, 4, 1, 1, _G(_backgroundColour));
drawRect(win, 6, 4, 1, 1, _G(_backgroundColour));
drawRect(win, 6, 6, 1, 1, _G(_backgroundColour));
break;
default:
break;
}
}
void updateDice(int ourTurn, int leftDice, int rightDice) {
leftDice--;
rightDice--;
g_scott->glk_window_clear(_G(_leftDiceWin));
g_scott->glk_window_clear(_G(_rightDiceWin));
_G(_diceXOffset) = _G(_diceXOffset) - _G(_dicePixelSize);
drawGraphicalDice(_G(_leftDiceWin), leftDice);
_G(_diceXOffset) = _G(_diceXOffset) + _G(_dicePixelSize);
drawGraphicalDice(_G(_rightDiceWin), rightDice);
winid_t win = ourTurn ? _G(_battleRight) : _G(_topWindow);
g_scott->glk_window_move_cursor(win, 2, 1);
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(win));
g_scott->glk_put_char_uni(0x2680 + leftDice);
g_scott->glk_put_char('+');
g_scott->glk_put_char_uni(0x2680 + rightDice);
g_scott->glk_window_move_cursor(win, 2, 2);
SOBPrint(win, "%d %d", leftDice + 1, rightDice + 1);
}
void printSum(int ourTurn, int sum, int strike) {
winid_t win = ourTurn ? _G(_battleRight) : _G(_topWindow);
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(win));
g_scott->glk_window_move_cursor(win, 6, 1);
SOBPrint(win, "+ %d = %d", strike, sum);
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(_G(_battleRight)));
g_scott->glk_window_move_cursor(_G(_battleRight), 6, 1);
g_scott->glk_put_string("+ 9 = ");
}
void updateResult(int ourTurn, int strike, int stamina, int boatFlag) {
winid_t win = ourTurn ? _G(_battleRight) : _G(_topWindow);
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(win));
g_scott->glk_window_move_cursor(win, 2, 4);
if (boatFlag) {
SOBPrint(win, "STRIKE : %d\n", strike);
g_scott->glk_window_move_cursor(win, 2, 5);
SOBPrint(win, "CRW STR : %d", stamina);
} else {
SOBPrint(win, "SKILL : %d\n", strike);
g_scott->glk_window_move_cursor(win, 2, 5);
SOBPrint(win, "STAMINA : %d", stamina);
}
}
void clearResult(void) {
winid_t win = _G(_topWindow);
uint width;
for (int j = 0; j < 2; j++) {
g_scott->glk_window_get_size(win, &width, 0);
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(win));
g_scott->glk_window_move_cursor(win, 11, 1);
for (int i = 0; i < 10; i++)
g_scott->glk_put_string(" ");
drawBorder(win);
win = _G(_battleRight);
}
}
void clearStamina(void) {
winid_t win = _G(_topWindow);
uint width;
for (int j = 0; j < 2; j++) {
g_scott->glk_window_get_size(win, &width, 0);
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(win));
g_scott->glk_window_move_cursor(win, 11, 5);
for (int i = 0; i < (int)width - 13; i++)
g_scott->glk_put_string(" ");
drawBorder(win);
win = _G(_battleRight);
}
}
static void rearrangeBattleDisplay(int strike, int stamina, int boatFlag) {
g_scott->updateSettings();
g_scott->glk_cancel_char_event(_G(_topWindow));
g_scott->closeGraphicsWindow();
g_scott->glk_window_close(_G(_battleRight), nullptr);
g_scott->glk_window_close(_G(_leftDiceWin), nullptr);
g_scott->glk_window_close(_G(_rightDiceWin), nullptr);
seasOfBloodRoomImage();
setupBattleScreen(boatFlag);
updateResult(0, strike, stamina, boatFlag);
updateResult(1, 9, _G(_counters)[3], boatFlag);
drawBorder(_G(_topWindow));
drawBorder(_G(_battleRight));
redrawStaticText(_G(_topWindow), boatFlag);
redrawStaticText(_G(_battleRight), boatFlag);
g_scott->glk_request_char_event(_G(_topWindow));
}
int rollDice(int strike, int stamina, int boatFlag) {
clearResult();
redrawStaticText(_G(_battleRight), boatFlag);
g_scott->glk_request_timer_events(60);
int rolls = 0;
int ourTurn = 0;
int leftDice = 1 + g_scott->getRandomNumber(100) % 6;
int rightDice = 1 + g_scott->getRandomNumber(100) % 6;
int ourResult;
int theirResult = 0;
int theirDiceStopped = 0;
event_t event;
int enemyRolls = 20 + g_scott->getRandomNumber(100) % 10;
g_scott->glk_cancel_char_event(_G(_topWindow));
g_scott->glk_request_char_event(_G(_topWindow));
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(_G(_bottomWindow)));
g_scott->glk_put_string("Their throw");
int delay = 60;
while (!g_vm->shouldQuit()) {
g_scott->glk_select(&event);
if (event.type == evtype_Timer) {
if (theirDiceStopped) {
g_scott->glk_request_timer_events(60);
theirDiceStopped = 0;
printSum(ourTurn, theirResult, strike);
ourTurn = 1;
g_scott->glk_window_clear(_G(_bottomWindow));
g_scott->glk_cancel_char_event(_G(_topWindow));
g_scott->glk_request_char_event(_G(_topWindow));
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(_G(_bottomWindow)));
g_scott->glk_put_string("Your throw\n");
g_scott->glk_put_string("<ENTER> to stop dice");
if (!boatFlag)
g_scott->glk_put_string(" <X> to run");
} else if (ourTurn == 0) {
g_scott->glk_request_timer_events(delay);
}
rolls++;
if (rolls % 2)
leftDice = 1 + g_scott->getRandomNumber(100) % 6;
else
rightDice = 1 + g_scott->getRandomNumber(100) % 6;
updateDice(ourTurn, leftDice, rightDice);
if (ourTurn == 0 && rolls == enemyRolls) {
g_scott->glk_window_clear(_G(_bottomWindow));
theirResult = leftDice + rightDice + strike;
SOBPrint(_G(_bottomWindow), "Their result: %d + %d + %d = %d\n", leftDice,
rightDice, strike, theirResult);
g_scott->glk_request_timer_events(1000);
theirDiceStopped = 1;
}
}
if (event.type == evtype_CharInput) {
if (ourTurn && (event.val1 == keycode_Return)) {
updateDice(ourTurn, leftDice, rightDice);
ourResult = leftDice + rightDice + 9;
printSum(ourTurn, ourResult, 9);
if (theirResult > ourResult) {
return LOSS;
} else if (ourResult > theirResult) {
return VICTORY;
} else {
return DRAW;
}
} else if (MY_LOC != 1 && (event.val1 == 'X' || event.val1 == 'x')) {
MY_LOC = _G(_savedRoom);
return FLEE;
} else {
g_scott->glk_request_char_event(_G(_topWindow));
}
}
if (event.type == evtype_Arrange) {
rearrangeBattleDisplay(strike, stamina, boatFlag);
}
}
return ERROR;
}
void battleHitEnter(int strike, int stamina, int boatFlag) {
g_scott->glk_request_char_event(_G(_bottomWindow));
event_t ev;
int result = 0;
do {
g_scott->glk_select(&ev);
if (ev.type == evtype_CharInput) {
if (ev.val1 == keycode_Return) {
result = 1;
} else {
g_scott->glk_request_char_event(_G(_bottomWindow));
}
}
if (ev.type == evtype_Arrange) {
rearrangeBattleDisplay(strike, stamina, boatFlag);
}
} while (result == 0 && !g_vm->shouldQuit());
}
void battleLoop(int enemy, int strike, int stamina, int boatFlag) {
updateResult(0, strike, stamina, boatFlag);
updateResult(1, 9, _G(_counters)[3], boatFlag);
do {
int result = rollDice(strike, stamina, boatFlag);
g_scott->glk_cancel_char_event(_G(_topWindow));
g_scott->glk_window_clear(_G(_bottomWindow));
clearStamina();
g_scott->glk_stream_set_current(g_scott->glk_window_get_stream(_G(_bottomWindow)));
if (result == LOSS) {
_G(_counters)[3] -= 2;
if (_G(_counters)[3] <= 0) {
SOBPrint(_G(_bottomWindow), "%s\n",
boatFlag ? "THE BANSHEE HAS BEEN SUNK!"
: "YOU HAVE BEEN KILLED!");
_G(_counters)[3] = 0;
_G(_bitFlags) |= (1 << 6);
_G(_counters)[7] = 0;
} else {
SOBPrint(_G(_bottomWindow), "%s", _G(_battleMessages)[1 + g_scott->getRandomNumber(100) % 5 + 16 * boatFlag]);
}
} else if (result == VICTORY) {
stamina -= 2;
if (stamina <= 0) {
g_scott->glk_put_string("YOU HAVE WON!\n");
_G(_bitFlags) &= ~(1 << 6);
stamina = 0;
} else {
SOBPrint(_G(_bottomWindow), "%s", _G(_battleMessages)[6 + g_scott->getRandomNumber(100) % 5 + 16 * boatFlag]);
}
} else if (result == FLEE) {
_G(_bitFlags) |= (1 << 6);
MY_LOC = _G(_savedRoom);
return;
} else {
SOBPrint(_G(_bottomWindow), "%s", _G(_battleMessages)[11 + g_scott->getRandomNumber(100) % 5 + 16 * boatFlag]);
}
g_scott->glk_put_string("\n\n");
if (_G(_counters)[3] > 0 && stamina > 0) {
g_scott->glk_put_string("<ENTER> to roll dice");
if (!boatFlag)
g_scott->glk_put_string(" <X> to run");
}
updateResult(0, strike, stamina, boatFlag);
updateResult(1, 9, _G(_counters)[3], boatFlag);
battleHitEnter(strike, stamina, boatFlag);
g_scott->glk_window_clear(_G(_bottomWindow));
} while (stamina > 0 && _G(_counters)[3] > 0 && !g_vm->shouldQuit());
}
void swapStaminaAndCrewStrength(void) {
uint8_t temp = _G(_counters)[7]; // Crew strength
_G(_counters)[7] = _G(_counters)[3]; // Stamina
_G(_counters)[3] = temp;
}
int loadExtraSeasOfBloodData(void) {
_G(_drawToBuffer) = 1;
int offset;
offset = 0x47b7 + _G(_fileBaselineOffset);
uint8_t *ptr = seekToPos(_G(_entireFile), offset);
int ct;
for (ct = 0; ct < 124; ct++) {
_G(_enemyTable)[ct] = *(ptr++);
if (_G(_enemyTable)[ct] == 0xff)
break;
}
ptr = seekToPos(_G(_entireFile), 0x71DA + _G(_fileBaselineOffset));
for (int i = 0; i < 32; i++) {
_G(_battleMessages)[i] = decompressText(ptr, i);
}
offset = 0x7af5 - 16357 + _G(_fileBaselineOffset);
int data_length = 2010;
_G(_bloodImageData) = new uint8_t[data_length];
ptr = seekToPos(_G(_entireFile), offset);
for (int i = 0; i < data_length; i++)
_G(_bloodImageData)[i] = *(ptr++);
for (int i = I_DONT_UNDERSTAND; i <= THATS_BEYOND_MY_POWER; i++)
_G(_sys)[i] = _G(_systemMessages)[4 - I_DONT_UNDERSTAND + i];
for (int i = YOU_ARE; i <= HIT_ENTER; i++)
_G(_sys)[i] = _G(_systemMessages)[13 - YOU_ARE + i];
_G(_sys)[OK] = _G(_systemMessages)[2];
_G(_sys)[PLAY_AGAIN] = _G(_systemMessages)[3];
_G(_sys)[YOURE_CARRYING_TOO_MUCH] = _G(_systemMessages)[27];
_G(_items)[125]._text = "A loose plank";
_G(_items)[125]._autoGet = "PLAN";
return 0;
}
int loadExtraSeasOfBlood64Data(void) {
_G(_drawToBuffer) = 1;
int offset;
offset = 0x3fee + _G(_fileBaselineOffset);
uint8_t *ptr;
ptr = seekToPos(_G(_entireFile), offset);
int ct;
for (ct = 0; ct < 124; ct++) {
_G(_enemyTable)[ct] = *(ptr++);
if (_G(_enemyTable)[ct] == 0xff)
break;
}
offset = 0x82f6 + _G(_fileBaselineOffset);
ptr = seekToPos(_G(_entireFile), offset);
for (int i = 0; i < 32; i++) {
_G(_battleMessages)[i] = decompressText(ptr, i);
}
offset = 0x5299 + _G(_fileBaselineOffset);
int data_length = 2010;
_G(_bloodImageData) = new uint8_t[data_length];
ptr = seekToPos(_G(_entireFile), offset);
for (int i = 0; i < data_length; i++) {
_G(_bloodImageData)[i] = *(ptr++);
}
SysMessageType messagekey[] = {
NORTH,
SOUTH,
EAST,
WEST,
UP,
DOWN,
EXITS,
YOU_SEE,
YOU_ARE,
YOU_CANT_GO_THAT_WAY,
OK,
WHAT_NOW,
HUH,
YOU_HAVE_IT,
YOU_HAVENT_GOT_IT,
DROPPED,
TAKEN,
INVENTORY,
YOU_DONT_SEE_IT,
THATS_BEYOND_MY_POWER,
DIRECTION,
YOURE_CARRYING_TOO_MUCH,
PLAY_AGAIN,
RESUME_A_SAVED_GAME,
YOU_CANT_DO_THAT_YET,
I_DONT_UNDERSTAND,
NOTHING};
for (int i = 0; i < 27; i++) {
_G(_sys)[messagekey[i]] = _G(_systemMessages)[i];
}
return 0;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,50 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_SEAS_OF_BLOOD_H
#define GLK_SCOTT_SEAS_OF_BLOOD_H
namespace Glk {
namespace Scott {
int loadExtraSeasOfBloodData();
int loadExtraSeasOfBlood64Data();
void seasOfBloodRoomImage();
void adventureSheet();
void bloodAction(int p);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,501 @@
#include "ti99_4a_terp.h"
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/scott.h"
#include "glk/scott/types.h"
#include "glk/scott/globals.h"
#include "glk/scott/ti99_4a_terp.h"
namespace Glk {
namespace Scott {
ActionResultType performTI99Line(uint8_t *actionLine) {
if (actionLine == nullptr)
return ACT_FAILURE;
uint8_t *ptr = actionLine;
int runCode = 0;
int index = 0;
ActionResultType result = ACT_FAILURE;
int opcode, param;
int tryIndex;
int tryArr[32];
tryIndex = 0;
while (runCode == 0) {
opcode = *(ptr++);
switch (opcode) {
case 183: /* is p in inventory? */
if (_G(_items)[*(ptr++)]._location != CARRIED) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 184: /* is p in room? */
if (_G(_items)[*(ptr++)]._location != MY_LOC) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 185: /* is p available? */
if (_G(_items)[*ptr]._location != CARRIED && _G(_items)[*ptr]._location != MY_LOC) {
runCode = 1;
result = ACT_FAILURE;
}
ptr++;
break;
case 186: /* is p here? */
if (_G(_items)[*(ptr++)]._location == MY_LOC) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 187: /* is p NOT in inventory? */
if (_G(_items)[*(ptr++)]._location == CARRIED) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 188: /* is p NOT available? */
if (_G(_items)[*ptr]._location == CARRIED || _G(_items)[*ptr]._location == MY_LOC) {
runCode = 1;
result = ACT_FAILURE;
}
ptr++;
break;
case 189: /* is p in play? */
if (_G(_items)[*(ptr++)]._location == 0) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 190: /* Is object p NOT in play? */
if (_G(_items)[*(ptr++)]._location != 0) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 191: /* Is player is in room p? */
if (MY_LOC != *(ptr++)) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 192: /* Is player NOT in room p? */
if (MY_LOC == *(ptr++)) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 193: /* Is bitflag p clear? */
if ((_G(_bitFlags) & (1 << *(ptr++))) == 0) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 194: /* Is bitflag p set? */
if (_G(_bitFlags) & (1 << *(ptr++))) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 195: /* Does the player carry anything? */
if (g_scott->countCarried() == 0) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 196: /* Does the player carry nothing? */
if (g_scott->countCarried()) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 197: /* Is _G(_currentCounter) <= p? */
if (_G(_currentCounter) > *(ptr++)) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 198: /* Is _G(_currentCounter) > p? */
if (_G(_currentCounter) <= *(ptr++)) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 199: /* Is _G(_currentCounter) == p? */
if (_G(_currentCounter) != *(ptr++)) {
runCode = 1;
result = ACT_FAILURE;
}
break;
case 200: /* Is item p still in initial room? */
if (_G(_items)[*ptr]._location != _G(_items)[*ptr]._initialLoc) {
runCode = 1;
result = ACT_FAILURE;
}
ptr++;
break;
case 201: /* Has item p been moved? */
if (_G(_items)[*ptr]._location == _G(_items)[*ptr]._initialLoc) {
runCode = 1;
result = ACT_FAILURE;
}
ptr++;
break;
case 212: /* clear screen */
g_scott->glk_window_clear(_G(_bottomWindow));
break;
case 214: /* inv */
_G(_autoInventory) = 1;
break;
case 215: /* !inv */
_G(_autoInventory) = 0;
break;
case 216:
case 217:
break;
case 218:
if (tryIndex >= 32) {
g_scott->fatal("ERROR Hit upper limit on try method.");
}
tryArr[tryIndex++] = ptr - actionLine + *ptr;
ptr++;
break;
case 219: /* get item */
if (g_scott->countCarried() >= _G(_gameHeader)->_maxCarry) {
g_scott->output(_G(_sys)[YOURE_CARRYING_TOO_MUCH]);
runCode = 1;
result = ACT_FAILURE;
break;
} else {
_G(_items)[*ptr]._location = CARRIED;
}
ptr++;
break;
case 220: /* drop item */
_G(_items)[*(ptr++)]._location = MY_LOC;
_G(_shouldLookInTranscript) = 1;
break;
case 221: /* go to room */
MY_LOC = *(ptr++);
_G(_shouldLookInTranscript) = 1;
g_scott->look();
break;
case 222: /* move item p to room 0 */
_G(_items)[*(ptr++)]._location = 0;
break;
case 223: /* darkness */
_G(_bitFlags) |= 1 << DARKBIT;
break;
case 224: /* light */
_G(_bitFlags) &= ~(1 << DARKBIT);
break;
case 225: /* set flag p */
_G(_bitFlags) |= (1 << *(ptr++));
break;
case 226: /* clear flag p */
_G(_bitFlags) &= ~(1 << *(ptr++));
break;
case 227: /* set flag 0 */
_G(_bitFlags) |= (1 << 0);
break;
case 228: /* clear flag 0 */
_G(_bitFlags) &= ~(1 << 0);
break;
case 229: /* die */
g_scott->playerIsDead();
g_scott->doneIt();
result = ACT_GAMEOVER;
break;
case 230: /* move item p2 to room p */
param = *(ptr++);
g_scott->putItemAInRoomB(*(ptr++), param);
break;
case 231: /* quit */
g_scott->doneIt();
return ACT_GAMEOVER;
case 232: /* print score */
if (g_scott->printScore() == 1)
return ACT_GAMEOVER;
_G(_stopTime) = 2;
break;
case 233: /* list contents of inventory */
g_scott->listInventory();
_G(_stopTime) = 2;
break;
case 234: /* refill lightsource */
_G(_gameHeader)->_lightTime = _G(_lightRefill);
_G(_items)[LIGHT_SOURCE]._location = CARRIED;
_G(_bitFlags) &= ~(1 << LIGHTOUTBIT);
break;
case 235: /* save */
g_scott->saveGame();
_G(_stopTime) = 2;
break;
case 236: /* swap items p and p2 around */
param = *(ptr++);
g_scott->swapItemLocations(param, *(ptr++));
break;
case 237: /* move item p to the inventory */
_G(_items)[*(ptr++)]._location = CARRIED;
break;
case 238: /* make item p same room as item p2 */
param = *(ptr++);
g_scott->moveItemAToLocOfItemB(param, *(ptr++));
break;
case 239: /* nop */
break;
case 240: /* look at room */
g_scott->look();
_G(_shouldLookInTranscript) = 1;
break;
case 241: /* unknown */
break;
case 242: /* add 1 to current counter */
_G(_currentCounter)++;
break;
case 243: /* sub 1 from current counter */
if (_G(_currentCounter) >= 1)
_G(_currentCounter)--;
break;
case 244: /* print current counter */
g_scott->outputNumber(_G(_currentCounter));
g_scott->output(" ");
break;
case 245: /* set current counter to p */
_G(_currentCounter) = *(ptr++);
break;
case 246: /* add to current counter */
_G(_currentCounter) += *(ptr++);
break;
case 247: /* sub from current counter */
_G(_currentCounter) -= *(ptr++);
if (_G(_currentCounter) < -1)
_G(_currentCounter) = -1;
break;
case 248: /* go to stored location */
g_scott->goToStoredLoc();
break;
case 249: /* swap room and counter */
g_scott->swapLocAndRoomFlag(*(ptr++));
break;
case 250: /* swap current counter */
g_scott->swapCounters(*(ptr++));
break;
case 251: /* print noun */
g_scott->printNoun();
break;
case 252: /* print noun + newline */
g_scott->printNoun();
g_scott->output("\n");
break;
case 253: /* print newline */
g_scott->output("\n");
break;
case 254: /* delay */
g_scott->delay(1);
break;
case 255: /* end of code block. */
result = ACT_SUCCESS;
runCode = 1;
tryIndex = 0; /* drop out of all try blocks! */
break;
default:
if (opcode <= 182 && opcode <= _G(_gameHeader)->_numMessages + 1) {
g_scott->printMessage(opcode);
} else {
index = ptr - actionLine;
error("Unknown action %d [Param begins %d %d]", opcode, actionLine[index], actionLine[index + 1]);
break;
}
break;
}
/* we are on the 0xff opcode, or have fallen through */
if (runCode == 1 && tryIndex > 0) {
if (opcode == 0xff) {
runCode = 1;
} else {
/* dropped out of TRY block */
/* or at end of TRY block */
index = tryArr[tryIndex - 1];
tryIndex -= 1;
tryArr[tryIndex] = 0;
runCode = 0;
ptr = actionLine + index;
}
}
}
return result;
}
void runImplicitTI99Actions() {
int probability;
uint8_t *ptr;
int loopFlag;
ptr = _G(_ti99ImplicitActions);
loopFlag = 0;
/* bail if no auto acts in the game. */
if (*ptr == 0x0)
loopFlag = 1;
while (loopFlag == 0) {
/*
p + 0 = chance of happening
p + 1 = size of code chunk
p + 2 = start of code
*/
probability = ptr[0];
if (g_scott->randomPercent(probability))
performTI99Line(ptr + 2);
if (ptr[1] == 0 || static_cast<size_t>(ptr - _G(_ti99ImplicitActions)) >= _G(_ti99ImplicitExtent))
loopFlag = 1;
/* skip code chunk */
ptr += 1 + ptr[1];
}
}
ExplicitResultType runExplicitTI99Actions(int verbNum, int nounNum) {
uint8_t *p;
ExplicitResultType flag = ER_NO_RESULT;
int match = 0;
ActionResultType runcode;
p = _G(_verbActionOffsets)[verbNum];
/* process all code blocks for this verb
until success or end. */
while (flag == ER_NO_RESULT) {
/* we match VERB NOUN or VERB ANY */
if (p != nullptr && (p[0] == nounNum || p[0] == 0)) {
match = 1;
runcode = performTI99Line(p + 2);
if (runcode == ACT_SUCCESS) {
return ER_SUCCESS;
} else { /* failure */
if (p[1] == 0)
flag = ER_RAN_ALL_LINES;
else
p += 1 + p[1];
}
} else {
if (p == nullptr || p[1] == 0)
flag = ER_RAN_ALL_LINES_NO_MATCH;
else
p += 1 + p[1];
}
}
if (match)
flag = ER_RAN_ALL_LINES;
return flag;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,47 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_TI99_4A_TERP_H
#define GLK_SCOTT_TI99_4A_TERP_H
#include "glk/scott/definitions.h"
namespace Glk {
namespace Scott {
void runImplicitTI99Actions();
ExplicitResultType runExplicitTI99Actions(int verbNum, int nounNum);
} // End of namespace Scott
} // End of namespace Glk
#endif

57
engines/glk/scott/types.h Normal file
View File

@@ -0,0 +1,57 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#ifndef GLK_SCOTT_TYPES_H
#define GLK_SCOTT_TYPES_H
#include "common/scummsys.h"
namespace Glk {
namespace Scott {
#if !defined(SIZE_MAX)
#define SIZE_MAX 0xFFFFFFFF
#endif
typedef uint8 uint8_t;
typedef uint16 uint16_t;
typedef uint32 uint32_t;
typedef int8 int8_t;
typedef int16 int16_t;
typedef int32 int32_t;
typedef int64 int64_t;
typedef unsigned long long size_t;
} // End of namespace Scott
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Copyright (c) 2002 - 2023 Magnus Lind.
*
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
*/
#ifndef GLK_SCOTT_6502_EMU_H
#define GLK_SCOTT_6502_EMU_H
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
struct CpuCtx {
uint32_t _cycles;
uint16_t _pc;
uint8_t *_mem;
uint8_t _sp;
uint8_t _flags;
uint8_t _a;
uint8_t _x;
uint8_t _y;
};
int nextInst(CpuCtx *r);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,235 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Copyright (c) 2002 - 2023 Magnus Lind.
*
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
*/
#include "common/util.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
int findSys(const byte *buf, int target) {
int outstart = -1;
int state = 1;
int i = 0;
/* skip link and line number */
buf += 4;
/* iAN: workaround for hidden sysline (1001 cruncher, CFB, etc)*/
if (buf[0] == 0) {
for (i = 5; i < 32; i++)
if (buf[i] == 0x9e && (((buf[i + 1] & 0x30) == 0x30) || ((buf[i + 2] & 0x30) == 0x30)))
break;
}
/* exit loop at line end */
while (i < 1000 && buf[i] != '\0') {
byte *sysEnd;
int c = buf[i];
switch (state) {
/* look for and consume sys token */
case 1:
if ((target == -1 && (c == 0x9e)) || c == target) {
state = 2;
}
break;
/* skip spaces and left parenthesis, if any */
case 2:
if (strchr(" (", c) != nullptr)
break;
// fallthrough
/* convert string number to int */
case 3:
outstart = (int)strtol((const char *)(buf + i), (char **)&sysEnd, 10);
if ((buf + i) == sysEnd) {
/* we got nothing */
outstart = -1;
} else {
c = *sysEnd;
if ((c >= 0xaa) && (c <= 0xae)) {
i = (int)strtol((char *)(sysEnd + 1), (char **)&sysEnd, 10);
switch (c) {
case 0xaa:
outstart += i;
break;
case 0xab:
outstart -= i;
break;
case 0xac:
outstart *= i;
break;
case 0xad:
if (i > 0)
outstart /= i;
break;
case 0xae:
c = outstart;
while (--i)
outstart *= c;
break;
}
} else if (c == 'E') {
i = (int)strtol((char *)(sysEnd + 1), (char **)&sysEnd, 10);
i++;
while (--i)
outstart *= 10;
}
}
state = 4;
break;
case 4:
break;
}
++i;
}
return outstart;
}
static void loadPrgData(byte mem[65536], uint8_t *data, size_t dataLength, LoadInfo *info) {
int len = MIN(65536 - info->_start, static_cast<int>(dataLength));
memcpy(mem + info->_start, data, (size_t)len);
info->_end = info->_start + len;
info->_basicVarStart = -1;
info->_run = -1;
if (info->_basicTxtStart >= info->_start && info->_basicTxtStart < info->_end) {
info->_basicVarStart = info->_end;
}
}
void loadData(uint8_t *data, size_t dataLength, byte mem[65536], LoadInfo *info) {
int load = data[0] + data[1] * 0x100;
info->_start = load;
loadPrgData(mem, data + 2, dataLength - 2, info);
}
int strToInt(const char *str, int *value) {
int status = 0;
do {
char *strEnd;
long lval;
/* base 0 is auto detect */
int base = 0;
if (*str == '\0') {
/* no string to parse */
status = 1;
break;
}
if (*str == '$') {
/* a $ prefix specifies base 16 */
++str;
base = 16;
}
lval = strtol(str, &strEnd, base);
if (*strEnd != '\0') {
/* there is garbage in the string */
status = 1;
break;
}
if (value != nullptr) {
/* all is well, set the out parameter */
*value = static_cast<int>(lval);
}
} while (0);
return status;
}
bool u32eq(const unsigned char *addr, uint32_t val)
{
return addr[3] == (val >> 24) &&
addr[2] == ((val >> 16) & 0xff) &&
addr[1] == ((val >> 8) & 0xff) &&
addr[0] == (val & 0xff);
}
bool u32eqmasked(const unsigned char *addr, uint32_t mask, uint32_t val)
{
uint32_t val1 = addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24);
return (val1 & mask) == val;
}
bool u32eqxored(const unsigned char *addr, uint32_t xormask, uint32_t val)
{
uint32_t val1 = addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24);
return (val1 ^ xormask) == val;
}
bool u16eqmasked(const unsigned char *addr, uint16_t mask, uint16_t val)
{
uint16_t val1 = addr[0] | (addr[1] << 8);
return (val1 & mask) == val;
}
bool u16eq(const unsigned char *addr, uint16_t val)
{
return addr[1] == (val >> 8) &&
addr[0] == (val & 0xff);
}
bool u16noteq(const unsigned char *addr, uint16_t val)
{
return addr[1] != (val >> 8) ||
addr[0] != (val & 0xff);
}
bool u16gteq(const unsigned char *addr, uint16_t val)
{
uint16_t val2 = addr[0] | (addr[1] << 8);
return val2 >= val;
}
bool u16lteq(const unsigned char *addr, uint16_t val)
{
uint16_t val2 = addr[0] | (addr[1] << 8);
return val2 <= val;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,78 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Copyright (c) 2002 - 2023 Magnus Lind.
*
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
*/
#ifndef GLK_SCOTT_EXO_UTIL_H
#define GLK_SCOTT_EXO_UTIL_H
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
struct LoadInfo {
int _basicTxtStart; /* in */
int _basicVarStart; /* out */
int _run; /* out */
int _start; /* out */
int _end; /* out */
};
int findSys(const byte *buf, int target);
void loadData(uint8_t *data, size_t dataLength, byte mem[65536], LoadInfo *info);
int strToInt(const char *str, int *value);
bool u32eq(const unsigned char *addr, uint32_t val);
bool u16eq(const unsigned char *addr, uint16_t val);
bool u16gteq(const unsigned char *addr, uint16_t val);
bool u16lteq(const unsigned char *addr, uint16_t val);
bool u16noteq(const unsigned char *addr, uint16_t val);
bool u32eqmasked(const unsigned char *addr, uint32_t mask, uint32_t val);
bool u32eqxored(const unsigned char *addr, uint32_t ormask, uint32_t val);
bool u16eqmasked(const unsigned char *addr, uint16_t mask, uint16_t val);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,53 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnActionPacker(UnpStr *unp) {
byte *mem;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x811, 0x018538A9) &&
u32eq(mem + 0x81d, 0xCEF7D0E8) &&
u32eq(mem + 0x82d, 0x0F9D0837) &&
u32eq(mem + 0x84b, 0x03D00120)) {
unp->_depAdr = 0x110;
unp->_forced = 0x811;
unp->_strMem = READ_LE_UINT16(&mem[0x848]);
unp->_fEndAf = 0x120;
unp->_retAdr = READ_LE_UINT16(&mem[0x863]);
unp->_idFlag = 1;
return;
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,94 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnByteBoiler(UnpStr *unp) {
byte *mem;
int q, p;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x813, 0xE800F09D) &&
u32eq(mem + 0x818, 0x014E4CF7)) {
p = READ_LE_UINT16(&mem[0x811]);
if (u32eq(mem + p + 1, 0x02D0FAA5)) {
unp->_depAdr = 0x14e;
unp->_forced = 0x80b;
unp->_retAdr = READ_LE_UINT16(&mem[p + 0x5c]);
unp->_endAdr = READ_LE_UINT16(&mem[p + 0x0e]);
unp->_endAdr++;
unp->_fStrAf = 0xfe;
unp->_idFlag = 1;
return;
}
}
}
/* CPX hack */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x80b, 0xA97800A2) &&
u32eq(mem + 0x815, 0x4C01E6D0)) {
q = READ_LE_UINT16(&mem[0x819]);
if (u32eq(mem + q + 3, 0xE800F09D) &&
u32eq(mem + q + 8, 0x014E4CF7)) {
p = READ_LE_UINT16(&mem[q + 1]);
if (u32eq(mem + p + 1, 0x02D0FAA5)) {
unp->_depAdr = 0x14e;
unp->_forced = 0x80b;
unp->_retAdr = READ_LE_UINT16(&mem[p + 0x5c]);
unp->_endAdr = READ_LE_UINT16(&mem[p + 0x0e]);
unp->_endAdr++;
unp->_fStrAf = 0xfe;
unp->_idFlag = 1;
return;
}
}
}
}
/* SCS hack */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x813, 0xE800F09D) &&
u32eq(mem + 0x818, 0x01bf4CF7)) {
p = READ_LE_UINT16(&mem[0x811]);
if (u32eq(mem + p + 1, 0x02D0FAA5) &&
u32eq(mem + p + 0xdd, 0x014e4c01)) {
unp->_depAdr = 0x14e;
unp->_forced = 0x80b;
unp->_retAdr = READ_LE_UINT16(&mem[p + 0x5c]);
unp->_endAdr = READ_LE_UINT16(&mem[p + 0x0e]);
unp->_endAdr++;
unp->_fStrAf = 0xfe;
unp->_idFlag = 1;
return;
}
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,135 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnCaution(UnpStr *unp) {
byte *mem;
if (unp->_idFlag)
return;
mem = unp->_mem;
/* quickpacker 1.0 sysless */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x801, 0xE67800A2) &&
u32eq(mem + 0x805, 0x07EDBD01) &&
u32eq(mem + 0x80d, 0x00284CF8) &&
u32eq(mem + 0x844, 0xAC00334C)) {
unp->_forced = 0x801;
unp->_depAdr = 0x28;
unp->_retAdr = READ_LE_UINT16(&mem[0x86b]);
unp->_endAdr = READ_LE_UINT16(&mem[0x85a]);
unp->_fStrAf = mem[0x863];
unp->_strAdC = EA_ADDFF | 0xffff;
unp->_idFlag = 1;
return;
}
}
/* quickpacker 2.x + sys */
if (unp->_depAdr == 0) {
if (u32eqmasked(mem + 0x80b, 0xf0ffffff, 0x60A200A0) &&
u32eq(mem + 0x80f, 0x0801BD78) &&
u32eq(mem + 0x813, 0xD0CA0095) &&
u32eq(mem + 0x81e, 0xD0C80291) &&
u32eq(mem + 0x817, 0x001A4CF8)) {
unp->_forced = 0x80b;
unp->_depAdr = 0x01a;
if (mem[0x80e] == 0x69) {
unp->_retAdr = READ_LE_UINT16(&mem[0x842]);
unp->_endAdr = READ_LE_UINT16(&mem[0x850]);
unp->_endAdr += 0x100;
unp->_fStrAf = 0x4f;
unp->_strAdC = 0xffff | EA_USE_Y;
unp->_idFlag = 1;
return;
} else if (mem[0x80e] == 0x6c) {
unp->_retAdr = READ_LE_UINT16(&mem[0x844]);
unp->_endAdr = READ_LE_UINT16(&mem[0x84e]);
unp->_endAdr++;
unp->_fStrAf = 0x4d;
unp->_idFlag = 1;
return;
}
}
}
/* strangely enough, sysless v2.0 depacker is at $0002 */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x83d, 0xAA004A20) &&
u32eq(mem + 0x801, 0xA27800A0) &&
u32eq(mem + 0x805, 0x080FBD55) &&
u32eq(mem + 0x809, 0xD0CA0095) &&
u32eq(mem + 0x80d, 0x00024CF8)) {
unp->_forced = 0x801;
unp->_depAdr = 0x2;
unp->_retAdr = READ_LE_UINT16(&mem[0x83b]);
unp->_endAdr = READ_LE_UINT16(&mem[0x845]);
unp->_endAdr++;
unp->_fStrAf = mem[0x849];
// unp->_StrAdC=0xffff;
unp->_idFlag = 1;
return;
}
}
/* same goes for v2.5 sysless, seems almost another packer */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x83b, 0xAA005520) &&
u32eq(mem + 0x801, 0x60A200A0) &&
u32eq(mem + 0x805, 0x0801BD78) &&
u32eq(mem + 0x809, 0xD0CA0095) &&
u32eq(mem + 0x80d, 0x00104CF8)) {
unp->_forced = 0x801;
unp->_depAdr = 0x10;
unp->_retAdr = READ_LE_UINT16(&mem[0x839]);
unp->_endAdr = READ_LE_UINT16(&mem[0x847]);
unp->_endAdr += 0x100;
unp->_fStrAf = 0x46;
unp->_strAdC = 0xffff | EA_USE_Y;
unp->_idFlag = 1;
return;
}
}
/* hardpacker */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x80d, 0x8534A978) &&
u32eq(mem + 0x811, 0xB9B3A001) &&
u32eq(mem + 0x815, 0x4C99081F) &&
u32eq(mem + 0x819, 0xF7D08803) &&
u32eq(mem + 0x81d, 0xB9034D4C)) {
unp->_forced = 0x80d;
unp->_depAdr = 0x34d;
unp->_retAdr = READ_LE_UINT16(&mem[0x87f]);
unp->_endAdr = READ_LE_UINT16(&mem[0x88d]);
unp->_fStrAf = 0x3ba;
unp->_strAdC = EA_ADDFF | 0xffff;
unp->_idFlag = 1;
return;
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,208 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnCCS(UnpStr *unp) {
byte *mem;
int p;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x817, 0xB901E678) &&
u32eq(mem + 0x81b, 0xFD990831) &&
u32eq(mem + 0x8ff, 0xFEE60290) &&
u32eq(mem + 0x90f, 0x02903985)) {
if (unp->_info->_run == -1)
unp->_forced = 0x817;
unp->_depAdr = 0x0ff;
unp->_fEndAf = 0x2d;
unp->_endAdC = 0xffff;
unp->_retAdr = READ_LE_UINT16(&mem[0x8ed]);
if (unp->_retAdr == 0xa659) {
mem[0x8ec] = 0x2c;
unp->_retAdr = READ_LE_UINT16(&mem[0x8f0]);
}
unp->_idFlag = 1;
return;
}
}
/* derived from supercomp/eqseq */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x80b, 0x8C7800A0) &&
u32eq(mem + 0x812, 0x0099082F) &&
u32eq(mem + 0x846, 0x0DADF2D0) &&
u32eq(mem + 0x8c0, 0xF001124C)) {
if (unp->_info->_run == -1)
unp->_forced = 0x80b;
unp->_depAdr = 0x100;
unp->_endAdr = 0xae;
unp->_retAdr = READ_LE_UINT16(&mem[0x8f1]);
if (unp->_retAdr == 0xa659) {
mem[0x8f0] = 0x2c;
unp->_retAdr = READ_LE_UINT16(&mem[0x8f4]);
}
unp->_idFlag = 1;
return;
}
}
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x814, 0xB901E678) &&
u32eq(mem + 0x818, 0xFD990829) &&
u32eq(mem + 0x8a1, 0xFDA6FDB1) &&
u32eq(mem + 0x8a5, 0xFEC602D0)) {
if (unp->_info->_run == -1)
unp->_forced = 0x814;
unp->_depAdr = 0x0ff;
unp->_fEndBf = 0x39;
unp->_idFlag = 1;
return;
}
}
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x818, 0x2CB901E6) &&
u32eq(mem + 0x81c, 0x00FB9908) &&
u32eq(mem + 0x850, 0xFBB1C84A) &&
u32eq(mem + 0x854, 0xB1C81185)) {
if (unp->_info->_run == -1)
unp->_forced = 0x812;
unp->_depAdr = 0x0ff;
unp->_endAdr = 0xae;
unp->_idFlag = 1;
return;
}
}
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x818, 0x2CB901E6) &&
u32eq(mem + 0x81c, 0x00FB9908) &&
u32eq(mem + 0x851, 0xFBB1C812) &&
u32eq(mem + 0x855, 0xB1C81185)) {
if (unp->_info->_run == -1)
unp->_forced = 0x812;
unp->_depAdr = 0x0ff;
unp->_endAdr = 0xae;
unp->_idFlag = 1;
return;
}
}
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x82c, 0x018538A9) &&
u32eq(mem + 0x831, 0xFD990842) &&
u32eq(mem + 0x83e, 0x00FF4CF1) &&
u32eq(mem + 0x8a5, 0x50C651C6)) {
if (unp->_info->_run == -1)
unp->_forced = 0x822;
unp->_depAdr = 0x0ff;
unp->_fEndBf = 0x39;
unp->_retAdr = READ_LE_UINT16(&mem[0x8ea]);
unp->_idFlag = 1;
return;
}
}
if (unp->_depAdr == 0) {
if (u16eq(mem + 0x81a, 0x00A0) &&
(u32eq(mem + 0x820, 0xFB990837) ||
u32eq(mem + 0x824, 0xFB990837)) &&
u32eq(mem + 0x83b, 0xFD91FBB1) &&
u32eq(mem + 0x8bc, 0xEE00FC99)) {
if (unp->_info->_run == -1)
unp->_forced = 0x81a;
unp->_depAdr = 0x0ff;
unp->_fEndAf = 0x39;
unp->_endAdC = 0xffff;
unp->_retAdr = READ_LE_UINT16(&mem[0x8b3]);
unp->_idFlag = 1;
return;
}
}
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x812, 0xE67800A0) &&
u32eq(mem + 0x816, 0x0823B901) &&
u32eq(mem + 0x81a, 0xC800FD99) &&
u32eq(mem + 0x81e, 0xFF4CF7D0) &&
u32eq(mem + 0x885, 0xFDA6FDB1)) {
if (unp->_info->_run == -1)
unp->_forced = 0x812;
unp->_depAdr = 0x0ff;
// $2d is unreliable, Executer uses line number at $0803/4,
// which is read at $0039/3a by basic, as end address,
// then can set arbitrarily $2d/$ae pointers after unpack.
// unp->_fEndAf=0x2d;
unp->_endAdr = READ_LE_UINT16(&mem[0x803]);
unp->_endAdr++;
if (u32eq(mem + 0x87f, 0x4CA65920))
mem[0x87f] = 0x2c;
unp->_retAdr = READ_LE_UINT16(&mem[0x883]);
unp->_idFlag = 1;
return;
}
}
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x812, 0xE67800A0) &&
u32eq(mem + 0x816, 0x084CB901) &&
u32eq(mem + 0x81a, 0xA900FB99) &&
u32eq(mem + 0x848, 0x00FF4CE2)) {
if (unp->_info->_run == -1)
unp->_forced = 0x812;
unp->_depAdr = 0x0ff;
unp->_fEndAf = 0x2d;
unp->_endAdC = 0xffff;
unp->_idFlag = 1;
return;
}
}
/* Triad Hack */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x838, 0xB9080099) &&
u32eq(mem + 0x83f, 0xD0880816) &&
u32eq(mem + 0x8ff, 0xFEE60290) &&
u32eq(mem + 0x90f, 0x02903985)) {
if (unp->_info->_run == -1) {
for (p = 0x80b; p < 0x820; p++) {
if ((mem[p] & 0xa0) == 0xa0) {
unp->_forced = p;
break;
}
}
}
unp->_depAdr = 0x0ff;
unp->_fEndAf = 0x2d;
unp->_endAdC = 0xffff;
unp->_retAdr = READ_LE_UINT16(&mem[0x8ed]);
if (unp->_retAdr == 0xa659) {
mem[0x8ec] = 0x2c;
unp->_retAdr = READ_LE_UINT16(&mem[0x8f0]);
}
unp->_idFlag = 1;
return;
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,374 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnCruel(UnpStr *unp) {
byte *mem;
int q, p, strtmp = 0;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
if (mem[0x810] == 0xb9 &&
((*(unsigned int *)(mem + 0x813) & 0xfffffeff) == 0xC800FA99) &&
u16eq(mem + 0x818, 0x4CF7)) {
if (mem[0x814] == 0xFA) {
p = READ_LE_UINT16(&mem[0x811]); // mem[0x811] | mem[0x812] << 8;
if (u32eq(mem + p + 9, 0xC8071C99)) {
unp->_endAdr = READ_LE_UINT16(&mem[p + 2]); // mem[p + 2] | mem[p + 3] << 8;
unp->_depAdr = 0x100;
if (unp->_info->_run == -1)
unp->_forced = 0x80b;
unp->_fStrAf = 0xfc;
q = READ_LE_UINT16(&mem[p + 7]); // mem[p + 7] | mem[p + 8] << 8;
if ((mem[q + 0x8e] == 0xc6) && (mem[q + 0x8f] == 0x01) &&
(mem[q + 0x93] == 0xe6) && (mem[q + 0x94] == 0x01)) {
mem[q + 0x90] = 0x2c;
}
/* retadr is not always at the same addr, but at least can't
be anything < $07e8
*/
// unp->_retAdr=0x7e8;
q = READ_LE_UINT16(&mem[p + 7]); // mem[p + 7] | mem[p + 8] << 8;
if (mem[q + 0x3c] == 0x4c) {
/* v2.2/dynamix, v2.5/cross, v2.5/crest */
strtmp = *(unsigned short int *)(mem + q + 0x3d);
} else if (mem[q + 0x4a] == 0x4c) {
strtmp = *(unsigned short int *)(mem + q + 0x4b);
} else if (mem[q + 0x3f] == 0x4c) {
/* v2.2/oneway+scs, also hacked as cruel 2mhz 1.0 */
strtmp = *(unsigned short int *)(mem + q + 0x40);
} else {
/* todo: determine real retadr, for now a default seems ok */
strtmp = 0;
}
if (strtmp) {
if (strtmp >= unp->_retAdr) {
unp->_retAdr = strtmp;
} else { /* now search it... variable code here */
strtmp += p - *(unsigned short int *)(mem + 0x814);
for (q = strtmp; q < unp->_info->_end; q++) {
if ((mem[q] == 0xa9) || (mem[q] == 0x85)) {
q++;
continue;
}
if (mem[q] == 0x8d) {
q += 2;
continue;
}
if (mem[q] == 0x4c) {
unp->_retAdr = *(unsigned short int *)(mem + q + 1);
;
break;
}
}
}
}
}
} else if (mem[0x814] == 0xFB) {
/* not Cruel2 but MSCRUNCH by Marco/Taboo
v1.0 works only with some old AR cart (unless patched ;)
v1.5 is infact more common
*/
p = READ_LE_UINT16(&mem[0x811]); // mem[0x811] | mem[0x812] << 8;
if (u32eq(mem + p + 7, 0xC8071C99)) {
unp->_endAdr = READ_LE_UINT16(&mem[p + 3]); // mem[p + 3] | mem[p + 4] << 8;
unp->_depAdr = 0x100;
unp->_forced = 0x80b;
unp->_fStrAf = 0xfe;
if ((mem[p + 0x93] == 0x4c) && (mem[p + 0xa1] == 0x4c)) {
unp->_retAdr = READ_LE_UINT16(&mem[p + 0xa2]); // mem[p + 0xa2] | mem[p + 0xa3] << 8;
} else if ((mem[p + 0x8c] == 0x4c) && (mem[p + 0x94] == 0x4c)) {
unp->_retAdr = READ_LE_UINT16(&mem[p + 0x95]); // mem[p + 0x95] | mem[p + 0x96] << 8;
} else if ((mem[p + 0x20] == 0x4c) && (mem[p + 0x28] == 0x4c)) {
unp->_retAdr = READ_LE_UINT16(&mem[p + 0x29]); // mem[p + 0x29] | mem[p + 0x2a] << 8;
}
}
}
}
if (unp->_depAdr) {
unp->_idFlag = 1;
return;
}
}
/* MSCRUNCH 1.5 hack by Anubis */
if (unp->_depAdr == 0) {
if (mem[0x819] == 0x4c) {
p = READ_LE_UINT16(&mem[0x81a]); //mem[0x81a] | mem[0x81b] << 8;
if ((mem[p] == 0xa9) && (mem[p + 0x0f] == 0x30) &&
u32eq(mem + p + 0x13, 0xCA04009D) &&
u32eq(mem + p + 0x38, 0x01084C01)) {
q = READ_LE_UINT16(&mem[p + 0x1f]); // mem[p + 0x1f] | mem[p + 0x20] << 8;
if (u32eq(mem + q + 7, 0xC8071C99)) {
unp->_endAdr = READ_LE_UINT16(&mem[q + 3]); // mem[q + 3] | mem[q + 4] << 8;
unp->_depAdr = 0x100;
if (unp->_info->_run == -1)
unp->_forced = 0x819;
unp->_fStrAf = 0xfe;
unp->_retAdr = READ_LE_UINT16(&mem[q + 0xa2]); // mem[q + 0xa2] | mem[q + 0xa3] << 8;
}
}
}
}
/* fast cruel 4.x */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x80b, 0xE67800A0) &&
u32eq(mem + 0x813, 0xC8034099) &&
(u32eq(mem + 0x818, 0x03404cF7) ||
u32eq(mem + 0x818, 0x03b34cF7) ||
u32eq(mem + 0x818, 0x03db4cF7))) {
p = READ_LE_UINT16(&mem[0x811]); // mem[0x811] | mem[0x812] << 8;
if (u32eq(mem + p, 0xa75801c6)) {
p += 0x45;
q = READ_LE_UINT16(&mem[p]); // mem[p] | mem[p + 1] << 8;
unp->_endAdr = READ_LE_UINT16(&mem[q + 2]); // mem[q + 2] | mem[q + 3] << 8;
unp->_depAdr = 0x340;
unp->_forced = 0x80b;
unp->_fStrAf = 0xfc;
unp->_idFlag = 1;
return;
}
}
}
/* Cruel 2.0 / (BB) packer header */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x837, 0x9D0845BD) &&
u32eq(mem + 0x84f, 0xE808039D) &&
u32eq(mem + 0x83b, 0xC9E803B7)) {
unp->_depAdr = READ_LE_UINT16(&mem[0x843]); // mem[0x843] | mem[0x844] << 8;
if (unp->_info->_run == -1)
unp->_forced = 0x80d;
unp->_retAdr = READ_LE_UINT16(&mem[0x868]); // mem[0x868] | mem[0x869] << 8;
unp->_endAdr = unp->_info->_end - 0x90;
unp->_strMem = 0x801;
unp->_idFlag = 1;
return;
}
if (u32eq(mem + 0x845, 0x03E04CF2) &&
u32eq(mem + 0x852, 0x9D0893BD) &&
u32eq(mem + 0x856, 0xD0E80803)) {
unp->_depAdr = READ_LE_UINT16(&mem[0x847]); // mem[0x847] | mem[0x848] << 8;
if (unp->_info->_run == -1)
unp->_forced = 0x80d;
unp->_retAdr = READ_LE_UINT16(&mem[0x869]); // mem[0x869] | mem[0x86a] << 8;
unp->_endAdr = unp->_info->_end - 0x90;
unp->_strMem = 0x801;
unp->_idFlag = 1;
return;
}
if (u32eq(mem + 0x841, 0x03B74CF5) &&
u32eq(mem + 0x84c, 0x9D089BBD) &&
u32eq(mem + 0x850, 0xD0E8080B)) {
unp->_depAdr = READ_LE_UINT16(&mem[0x843]); // mem[0x843] | mem[0x844] << 8;
if (unp->_info->_run == -1) {
unp->_forced = 0x811;
} else {
mem[0x808] = '5'; /* just to be safe, change sys for next layer */
mem[0x809] = '9'; /* this hdr leaves it as sys2065 */
}
unp->_retAdr = READ_LE_UINT16(&mem[0x868]); // mem[0x868] | mem[0x869] << 8; /* fixed $080b */
unp->_endAdr = unp->_info->_end - 0x90;
unp->_strMem = 0x801;
unp->_idFlag = 1;
return;
}
/* this is a totally useless header, cheers TCOM! */
if (u32eq(mem + 0x80b, 0x1BB900A0) &&
u32eq(mem + 0x80f, 0x03B79908) &&
u32eq(mem + 0x823, 0x039D0840)) {
unp->_depAdr = READ_LE_UINT16(&mem[0x819]); // mem[0x819] | mem[0x81a] << 8;
if (unp->_info->_run == -1)
unp->_forced = 0x80b;
unp->_retAdr = READ_LE_UINT16(&mem[0x83e]); // mem[0x83e] | mem[0x83f] << 8;
unp->_endAdr = unp->_info->_end - 0x3d;
unp->_strMem = 0x801;
unp->_idFlag = 1;
return;
}
}
/* Cruel 2.0 / (BB) packer sysless */
if (unp->_depAdr == 0) {
if (u32eqmasked(mem + 0x80b, 0x0000ffff, 0x000000A0) &&
u32eq(mem + 0x817, 0xC800CB99) &&
u32eq(mem + 0x81b, 0x004CF7D0) && mem[0x81f] == 1) {
p = READ_LE_UINT16(&mem[0x815]); // mem[0x815] | mem[0x816] << 8;
p += 0x31;
if ((mem[p + 4] == 0xb9) &&
u32eq(mem + p + 7, 0xC8072099)) {
unp->_forced = 0x80b;
unp->_depAdr = 0x100;
unp->_endAdr = READ_LE_UINT16(&mem[p]); // mem[p] | mem[p + 1] << 8;
unp->_fStrAf = 0xfc;
/* patch: some version contain a zp cleaner sub at $01a2 */
if (u32eq(mem + p + 0xa6, 0x00A9CBA2) &&
u32eq(mem + p + 0xaa, 0xD0E80095)) {
mem[p + 0xa6] = 0x60;
}
/* patch: some version expects $01==#$34 already set from the header */
if (u32eq(mem + 0x811, 0xb9eaeaea)) {
mem[0x811] = 0xe6;
mem[0x812] = 0x01;
}
q = READ_LE_UINT16(&mem[p + 5]); // mem[p + 5] | mem[p + 6] << 8;
unp->_retAdr = 0x7e8;
if (mem[q + 0x6c] == 0x4c)
unp->_retAdr = READ_LE_UINT16(&mem[q + 0x6d]); // mem[q + 0x6d] | mem[q + 0x6e] << 8;
unp->_idFlag = 1;
return;
}
}
}
/* Cruel 2.1 / STA */
if (unp->_depAdr == 0) {
if (mem[0x80b] == 0xa0 && u32eq(mem + 0x817, 0xC800CB99) &&
u32eq(mem + 0x81b, 0x004CF7D0) && mem[0x81f] == 1) {
p = READ_LE_UINT16(&mem[0x815]); // mem[0x815] | mem[0x816] << 8;
p += 0x31;
if (mem[p + 6] == 0xb9 &&
u32eq(mem + p + 9, 0xC8072099)) {
unp->_forced = 0x80b;
unp->_depAdr = 0x100;
unp->_endAdr = READ_LE_UINT16(&mem[p]); // mem[p] | mem[p + 1] << 8;
unp->_fStrAf = 0xfc;
q = READ_LE_UINT16(&mem[p + 7]); // mem[p + 7] | mem[p + 8] << 8;
unp->_retAdr = 0x7e8;
if (mem[q + 0x6c] == 0x4c)
unp->_retAdr = READ_LE_UINT16(&mem[q + 0x6d]); // mem[q + 0x6d] | mem[q + 0x6e] << 8;
unp->_idFlag = 1;
return;
}
}
}
/* unknown cruel, jmp $00e9, found in Illusion/Random warez */
if (unp->_depAdr == 0) {
if (mem[0x810] == 0xb9 && u32eq(mem + 0x813, 0xC800e999) &&
u32eq(mem + 0x818, 0x00e94CF7)) {
p = READ_LE_UINT16(&mem[0x811]); // mem[0x811] | mem[0x812] << 8;
q = p - 0xed;
if (u32eq(mem + p, 0x13F01284) &&
u32eq(mem + q, 0xA9C8C8C8)) {
unp->_depAdr = 0xe9;
unp->_endAdr = READ_LE_UINT16(&mem[p + 0x13]); // mem[p + 0x13] | mem[p + 0x14] << 8;
unp->_retAdr = READ_LE_UINT16(&mem[q + 0x38]); // mem[q + 0x38] | mem[q + 0x39] << 8;
if (unp->_info->_run == -1)
unp->_forced = 0x80b;
unp->_fStrAf = 0xfc;
unp->_idFlag = 1;
return;
}
}
}
if (unp->_depAdr == 0) {
if (mem[0x810] == 0xb9 && u32eq(mem + 0x813, 0xC800ed99) &&
u32eq(mem + 0x818, 0x01004CF7)) {
p = READ_LE_UINT16(&mem[0x811]); // mem[0x811] | mem[0x812] << 8;
q = p - 0xed;
if (u32eq(mem + p, 0x01C60888) &&
u32eq(mem + q, 0xA9C8C8C8)) {
unp->_depAdr = 0x100;
unp->_endAdr = READ_LE_UINT16(&mem[p + 0x0f]); // mem[p + 0x0f] | mem[p + 0x10] << 8;
unp->_retAdr = READ_LE_UINT16(&mem[q + 0x38]); // mem[q + 0x38] | mem[q + 0x39] << 8;
if (unp->_info->_run == -1)
unp->_forced = 0x80b;
unp->_fStrAf = 0xfc;
unp->_idFlag = 1;
return;
}
}
}
/* cruel 1.2 / unknown 2059 */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x80b, 0xE67800A0) &&
u32eq(mem + 0x80f, 0x0803B901) &&
u32eq(mem + 0x813, 0xC800E399) &&
u32eq(mem + 0x817, 0x004CF7D0) &&
u32eq(mem + 0x90b, 0xC068FEC6)) {
unp->_depAdr = 0x100;
unp->_forced = 0x80b;
unp->_retAdr = READ_LE_UINT16(&mem[0x91c]); // mem[0x91c] | mem[0x91d] << 8;
unp->_endAdr = 0x2d;
unp->_fStrAf = 0xfc;
unp->_idFlag = 1;
return;
}
/* this was found in Agile and S451 cracks, Galleon's "Cruel+Search"
it's actually the real v1.0
*/
if (u32eq(mem + 0x80b, 0xE67800A0) &&
u32eq(mem + 0x80f, 0x0803B901) &&
u32eq(mem + 0x813, 0xC800E399) &&
u32eq(mem + 0x8c5, 0x011D4C04) &&
u32eq(mem + 0x90b, 0xB1486018)) {
unp->_depAdr = 0x100;
unp->_forced = 0x80b;
unp->_retAdr = READ_LE_UINT16(&mem[0x92d]); // mem[0x92d] | mem[0x92e] << 8;
unp->_endAdr = 0x2d;
unp->_fStrAf = 0xfc;
unp->_idFlag = 1;
return;
}
if (u32eq(mem + 0x80b, 0xE67800A0) &&
u32eq(mem + 0x80f, 0x0803B901) &&
u32eq(mem + 0x813, 0xC800E399) &&
u32eq(mem + 0x8b7, 0x011D4C04) &&
u32eq(mem + 0x8fc, 0xB1486018)) {
unp->_depAdr = 0x100;
unp->_forced = 0x80b;
unp->_retAdr = READ_LE_UINT16(&mem[0x91e]); // mem[0x91e] | mem[0x91f] << 8;
unp->_endAdr = 0x2d;
unp->_fStrAf = 0xfc;
unp->_idFlag = 1;
return;
}
}
/* TKC "proggy crueler 2.3" (and 2.5) */
if (unp->_depAdr == 0) {
if (mem[0x810] == 0xb9 && mem[0x819] == 0xa9 &&
u32eq(mem + 0x813, 0xC800fa99) &&
u32eq(mem + 0x822, 0x4CAF86AE)) {
p = READ_LE_UINT16(&mem[0x811]); // mem[0x811] | mem[0x812] << 8;
q = p - 0x100;
if (u32eq(mem + p + 0x0c, 0x20F7D0C8) &&
u32eq(mem + q, 0xA9C8C8C8)) {
unp->_depAdr = 0x100;
unp->_endAdr = READ_LE_UINT16(&mem[p + 0x02]); // mem[p + 0x02] | mem[p + 0x03] << 8;
unp->_retAdr = READ_LE_UINT16(&mem[q + 0x3f]); // mem[q + 0x3f] | mem[q + 0x40] << 8;
if (unp->_info->_run == -1)
unp->_forced = 0x80b;
unp->_fStrAf = 0xfc;
unp->_idFlag = 1;
return;
}
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,185 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnECA(UnpStr *unp) {
byte *mem;
int q, p;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
// for(p=0x810;p<0x830;p+=0x4)
for (p = 0x80d; p < 0x830; p += 0x1) {
if (u32eq(mem + p + 0x08, (unsigned int)(0x2D9D0032 + p)) &&
u32eq(mem + p + 0x3a, 0x2a2a2a2a) &&
u32eq(mem + p + 0x0c, 0xF710CA00)) {
if (((*(unsigned int *)(mem + p + 0x00) & 0xf4fff000) == 0x8434A000) &&
u32eq(mem + p + 0x04, 0xBD05A201)) {
unp->_forced = p + 1;
} else if (((*(unsigned int *)(mem + p + 0x00) & 0xffffff00) == 0x04A27800) &&
u32eq(mem + p + 0x04, 0xBDE80186)) {
unp->_forced = p + 1;
} else if (((*(unsigned int *)(mem + p - 0x03) & 0xffffff00) == 0x04A27800) &&
u32eq(mem + p + 0x04, 0xBDE80186)) {
unp->_forced = p - 2;
} else if (u32eq(mem + p - 0x03, 0x8D00a978)) {
unp->_forced = p - 2;
}
}
if (!unp->_forced) {
if (u32eq(mem + p + 0x3a, 0x2a2a2a2a) &&
u32eq(mem + p + 0x02, 0x8534A978) &&
mem[p - 3] == 0xa0) {
unp->_forced = p - 3;
if (mem[p + 0x0d6] == 0x20 && mem[p + 0x0d7] == 0xe0 &&
mem[p + 0x0d8] == 0x03 && mem[p + 0x1da] == 0x5b &&
mem[p + 0x1e7] == 0x59) {
/* antiprotection :D */
mem[p + 0x0d6] = 0x4c;
mem[p + 0x0d7] = 0xae;
mem[p + 0x0d8] = 0xa7;
}
}
}
if (!unp->_forced) { /* FDT */
if (u32eq(mem + p + 0x3a, 0x2a2a2a2a) &&
u32eq(mem + p + 0x03, 0x8604A278) &&
u32eq(mem + p + 0x0a, 0x2D950842)) {
unp->_forced = p + 3;
}
}
if (!unp->_forced) {
/* decibel hacks */
if (u32eq(mem + p + 0x3a, 0x2a2a2a2a) &&
u32eq(mem + p + 0x00, 0x9D085EBD) &&
u32eq(mem + p - 0x06, 0x018534A9)) {
unp->_forced = p - 0x6;
}
}
if (unp->_forced) {
for (q = 0xd6; q < 0xde; q++) {
if (mem[p + q] == 0x20) {
if (u16eq(mem + p + q + 1, 0xa659) ||
u16eq(mem + p + q + 1, 0xff81) ||
u16eq(mem + p + q + 1, 0xe3bf) ||
u16eq(mem + p + q + 1, 0xe5a0) ||
u16eq(mem + p + q + 1, 0xe518)) {
mem[p + q] = 0x2c;
q += 2;
continue;
} else {
unp->_retAdr = READ_LE_UINT16(&mem[p + q + 1]); // mem[p + q + 1] | mem[p + q + 2] << 8;
break;
}
}
if (mem[p + q] == 0x4c) {
unp->_retAdr = READ_LE_UINT16(&mem[p + q + 1]); // mem[p + q + 1] | mem[p + q + 2] << 8;
break;
}
}
unp->_depAdr = READ_LE_UINT16(&mem[p + 0x30]); // mem[p + 0x30] | mem[p + 0x31] << 8;
// some use $2d, some $ae
for (q = 0xed; q < 0x108; q++) {
if (u32eq(mem + p + q, 0xA518F7D0)) {
unp->_endAdr = mem[p + q + 4];
// if(unp->_DebugP)
// printf("EndAdr from $%02x\n",unp->_endAdr);
break;
}
}
/*
if anything it's unpacked to $d000-efff, it will be copied
to $e000-ffff as last action in unpacker before starting.
0196 20 DA 01 JSR $01DA ; some have this jsr nopped, reloc doesn't
happen 0199 A9 37 LDA #$37 019b 85 01 STA $01 019d 58 CLI
019e 20 00 0D JSR $0D00 ; retaddr can be either here or following
01a1 4C AE A7 JMP $A7AE
01da B9 00 EF LDA $EF00,Y
01dd 99 00 FF STA $FF00,Y
01e0 C8 INY
01e1 D0 F7 BNE $01DA
01e3 CE DC 01 DEC $01DC
01e6 CE DF 01 DEC $01DF
01e9 AD DF 01 LDA $01DF
01ec C9 DF CMP #$DF ;<< not fixed, found as lower as $44 for
example 01ee D0 EA BNE $01DA 01f0 60 RTS Because of this,
$d000-dfff will be a copy of $e000-efff. So if $2d points to >= $d000,
SOMETIMES it's better save up to $ffff or: mem[$2d]|(mem[$2e]+$10)<<8
Still it's not a rule and I don't know exactly when.
17/06/09: Implemented but still experimental, so better check
extensively. use -v to know when it does the adjustments. 28/10/09:
whoops, was clearing ONLY $d000-dfff =)
*/
unp->_strMem = READ_LE_UINT16(&mem[p + 0x32]); // mem[p + 0x32] | mem[p + 0x33] << 8;
for (q = 0xcd; q < 0xd0; q++) {
if (u32eqmasked(mem + p + q, 0xffff00ff, 0xa9010020)) {
unp->_ecaFlg = READ_LE_UINT16(&mem[p + q + 1]); // mem[p + q + 1] | mem[p + q + 2] << 8;
for (q = 0x110; q < 0x11f; q++) {
if (u32eq(mem + p + q, 0x99EF00B9) &&
mem[p + q + 0x12] == 0xc9) {
unp->_ecaFlg |= (mem[p + q + 0x13] - 0xf) << 24;
break;
}
}
break;
}
}
/* radwar hack has a BRK here, fffe/f used as IRQ/BRK vector */
if (mem[0x8e1] == 0) {
mem[0x8e1] = 0x6c;
mem[0x8e2] = 0xfe;
mem[0x8e3] = 0xff;
}
break;
}
}
if (unp->_depAdr) {
unp->_idFlag = 1;
return;
}
}
/* old packer, many old 1985 warez used this */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x81b, 0x018534A9) &&
u32eq(mem + 0x822, 0xAFC600A0) &&
u32eq(mem + 0x826, 0xB1082DCE) &&
u32eq(mem + 0x85b, 0x2A2A2A2A)) {
unp->_forced = 0x81b;
unp->_depAdr = 0x100;
unp->_strMem = READ_LE_UINT16(&mem[0x853]); // mem[0x853] | mem[0x854] << 8;
unp->_endAdr = mem[0x895];
unp->_retAdr = READ_LE_UINT16(&mem[0x885]); // mem[0x885] | mem[0x886] << 8;
unp->_idFlag = 1;
return;
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,177 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnExomizer(UnpStr *unp) {
byte *mem;
int q, p;
if (unp->_idFlag)
return;
mem = unp->_mem;
/* exomizer 3.x */
if (unp->_depAdr == 0) {
for (p = unp->_info->_end - 4; p > unp->_info->_start; p--) {
if (u32eq(mem + p, 0x100A8069) &&
u32eq(mem + p + 4, 0xD0FD060F) &&
mem[p - 6] == 0x4c && mem[p - 4] == 0x01) {
p -= 5;
q = 2;
if (mem[p - q] == 0x8a)
q++;
/* low byte of EndAdr, it's a lda $ff00,y */
if ((mem[p - q - 1] == mem[p - q - 3]) &&
(mem[p - q - 2] == mem[p - q])) { /* a0 xx a0 xx -> exomizer 3.0/3.01 */
unp->_exoFnd = 0x30;
} else { /* d0 c1 a0 xx -> exomizer 3.0.2, force +1 in start/end */
unp->_exoFnd = 0x32;
}
unp->_exoFnd |= (mem[p - q] << 8);
break;
}
}
if (unp->_exoFnd) {
unp->_depAdr = 0x100 | mem[p];
for (; p < unp->_info->_end; p++) {
if (u32eq(mem + p, 0x7d010020))
break;
}
for (; p < unp->_info->_end; p++) {
if (mem[p] == 0x4c) {
unp->_retAdr = 0;
if ((unp->_retAdr = READ_LE_UINT16(&mem[p + 1])) >= 0x200) {
break;
} else { /* it's a jmp $01xx, goto next */
p++;
p++;
}
}
}
if (unp->_info->_run == -1) {
p = unp->_info->_start;
q = p + 0x10;
for (; p < q; p++) {
if ((mem[p] == 0xba) && (mem[p + 1] == 0xbd)) {
unp->_forced = p;
break;
}
}
for (q = p - 1; q >= unp->_info->_start; q--) {
if (mem[q] == 0xe6)
unp->_forced = q;
if (mem[q] == 0xa0)
unp->_forced = q;
if (mem[q] == 0x78)
unp->_forced = q;
}
}
}
}
/* exomizer 1.x/2.x */
if (unp->_depAdr == 0) {
for (p = unp->_info->_end - 4; p > unp->_info->_start; p--) {
if (((u32eq(mem + p, 0x4CF7D088) &&
u32eq(mem + p - 0x0d, 0xD034C0C8)) ||
(u32eq(mem + p, 0x4CA7A438) &&
u32eq(mem + p - 0x0c, 0x799FA5AE)) ||
(u32eq(mem + p, 0x4CECD08A) &&
u32eq(mem + p - 0x13, 0xCECA0EB0)) ||
(u32eq(mem + p, 0x4C00A0D3) &&
u32eq(mem + p - 0x04, 0xD034C0C8)) ||
(u32eq(mem + p, 0x4C00A0D2) &&
u32eq(mem + p - 0x04, 0xD034C0C8))) &&
mem[p + 5] == 1) {
p += 4;
unp->_exoFnd = 1;
break;
} else if (((u32eq(mem + p, 0x8C00A0d2) &&
u32eq(mem + p - 0x04, 0xD034C0C8)) ||
(u32eq(mem + p, 0x8C00A0d3) &&
u32eq(mem + p - 0x04, 0xD034C0C8)) ||
(u32eq(mem + p, 0x8C00A0cf) &&
u32eq(mem + p - 0x04, 0xD034C0C8))) &&
mem[p + 6] == 0x4c && mem[p + 8] == 1) {
p += 7;
unp->_exoFnd = 1;
break;
}
}
if (unp->_exoFnd) {
unp->_depAdr = 0x100 | mem[p];
if (unp->_depAdr >= 0x134 && unp->_depAdr <= 0x14a /*0x13e*/) {
for (p = unp->_info->_end - 4; p > unp->_info->_start;
p--) { /* 02 04 04 30 20 10 80 00 */
if (u32eq(mem + p, 0x30040402))
break;
}
} else {
// exception for exo v1.x, otherwise add 8 to the counter and
// scan backward from here
if (unp->_depAdr != 0x143)
p += 0x08;
else
p -= 0xb8;
}
for (; p > unp->_info->_start; p--) {
// incredibly there can be a program starting at $4c00 :P
if ((mem[p] == 0x4c) && (mem[p - 1] != 0x4c) && (mem[p - 2] != 0x4c)) {
unp->_retAdr = 0;
if ((unp->_retAdr = READ_LE_UINT16(&mem[p + 1])) >= 0x200) {
break;
}
}
}
if (unp->_info->_run == -1) {
p = unp->_info->_start;
q = p + 0x10;
for (; p < q; p++) {
if ((mem[p] == 0xba) && (mem[p + 1] == 0xbd)) {
unp->_forced = p;
break;
}
}
for (q = p - 1; q >= unp->_info->_start; q--) {
if (mem[q] == 0xe6)
unp->_forced = q;
if (mem[q] == 0xa0)
unp->_forced = q;
if (mem[q] == 0x78)
unp->_forced = q;
}
}
}
}
if (unp->_depAdr != 0) {
unp->_idFlag = 1;
return;
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,247 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnExpert(UnpStr *unp) {
byte *mem;
int q, p;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
for (q = 0x81b; q < 0x81d; q++) {
if (u32eq(mem + q + 0x00, 0x852FA978) &&
u32eq(mem + q + 0x04, 0x8534A900) &&
u32eq(mem + q + 0x14, 0x03860286)) {
for (p = 0x900; p < 0xfff0; p++) {
if (u32eq(mem + p + 1, 0x00084C9A) &&
u32eq(mem + p - 4, 0xA2058604)) {
if (unp->_info->_run == -1) {
unp->_forced = q;
unp->_info->_run = q;
}
q = 0x100 + mem[p] + 1;
if (q != 0x100) {
unp->_depAdr = q;
}
}
}
break;
}
}
if (unp->_depAdr) {
unp->_rtiFrc = 1;
if (u32eq(mem + 0x835, 0x6E8D48A9)) {
p = 0;
if (u32eq(mem + 0x92c, 0x4902B100)) {
if (!unp->_idOnly) {
p = 0x876;
mem[p] = 0x00; /* 1st anti hack */
p = mem[0x930];
}
} else if (u32eq(mem + 0x92f, 0x4902B100)) {
if (!unp->_idOnly) {
p = 0x873;
mem[p] = 0xa9; /* 1st anti hack */
mem[p + 1] = 0x02;
p = mem[0x933];
}
}
if (p && !unp->_idOnly) {
p |= (p << 24) | (p << 16) | (p << 8);
for (q = 0x980; q < 0xfff0; q++) {
if (((mem[q] ^ (p & 0xff)) == 0xac) &&
((mem[q + 3] ^ (p & 0xff)) == 0xc0) &&
u32eqxored(mem + q + 7, (unsigned int)p, 0xC001F2AC)) {
mem[q + 0x06] = (p & 0xff); /* 2nd anti hack */
mem[q + 0x0d] = (p & 0xff);
break;
}
}
}
}
}
}
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x81b, 0x2FA9D878) &&
u32eq(mem + 0x82d, 0x0873BDB0)) {
for (p = 0x900; p < 0xfff0; p++) {
if (u32eq(mem + p, 0xA2F3D0CA) &&
mem[p + 0x05] == 0x4c) {
q = READ_LE_UINT16(&mem[p + 0x06]); // mem[p + 0x06] | mem[p + 0x07] << 8;
if (q != 0x100) {
unp->_depAdr = q;
break;
}
}
}
if (unp->_depAdr) {
unp->_rtiFrc = 1;
unp->_forced = 0x81b;
}
}
}
/* 2.9 Expert User Club version, found in
BloodMoney/HTL & SWIV/Inceria
*/
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x81b, 0x8C00A078) &&
u32eq(mem + 0x831, 0x05860485) &&
u32eq(mem + 0x998, 0x00084C9A)) {
p = mem[0x919];
q = p << 24 | p << 16 | p << 8 | p;
for (p = 0x900; p < 0xfff0; p++) {
if (((*(unsigned int *)(mem + p) ^ (unsigned int)q) == 0xA2F3D0CA) &&
((mem[p + 0x05] ^ (q & 0xff)) == 0x4c)) {
q = (mem[p + 0x06] ^ (q & 0xff)) | (mem[p + 0x07] ^ (q & 0xff)) << 8;
if (q != 0x100) {
unp->_depAdr = q;
break;
}
}
}
if (unp->_depAdr) {
unp->_rtiFrc = 1;
unp->_forced = 0x81b;
}
}
}
/* sys2070 A.S.S. */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x817, 0x00852FA9) &&
u32eq(mem + 0x823, 0x05860485) &&
u32eq(mem + 0x9a0, 0x00084C9A)) {
p = mem[0x923];
q = p << 24 | p << 16 | p << 8 | p;
for (p = 0x900; p < 0xfff0; p++) {
if (((*(unsigned int *)(mem + p) ^ (unsigned int)q) == 0xA2F3D0CA) &&
((mem[p + 0x05] ^ (q & 0xff)) == 0x4c)) {
q = (mem[p + 0x06] ^ (q & 0xff)) | (mem[p + 0x07] ^ (q & 0xff)) << 8;
if (q != 0x100) {
unp->_depAdr = q;
break;
}
}
}
if (unp->_depAdr) {
unp->_rtiFrc = 1;
unp->_forced = 0x81b;
}
}
}
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x81b, 0x7FA978D8) ||
u32eq(mem + 0x81b, 0x7FA9D878) ||
u32eq(mem + 0x816, 0x7FA978D8)) {
for (p = 0x900; p < 0xfff0; p++) {
if (u32eq(mem + p, 0xA2F3D0CA) &&
mem[p + 0x05] == 0x4c) {
q = READ_LE_UINT16(&mem[p + 0x06]); // mem[p + 0x06] | mem[p + 0x07] << 8;
if (q != 0x100) {
unp->_depAdr = q;
break;
}
}
}
if (unp->_depAdr) {
unp->_rtiFrc = 1;
if (u32eq(mem + 0x816, 0x7FA978D8)) {
q = 0x816;
if (!unp->_idOnly) {
for (p = 0x900; p < 0xfff0; p++) {
if (u32eq(mem + p, 0xE0A9F0A2) &&
u32eq(mem + p + 4, 0xE807135D) &&
mem[p + 0x8] == 0xd0) {
mem[p + 0x1] = 0x00;
mem[p + 0x3] = 0x98;
memset(mem + p + 4, 0xea, 6);
break;
}
}
}
} else {
q = 0x81b;
if (!unp->_idOnly) {
for (p = 0x900; p < 0xfff0; p++) {
if (u32eq(mem + p, 0xCA08015D) &&
u32eq(mem + p + 4, 0xF8D003E0) &&
mem[p + 0xa] == 0xd0) {
p += 0xa;
mem[p] = 0x24;
break;
}
}
}
}
if (unp->_info->_run == -1) {
unp->_forced = q;
unp->_info->_run = q;
}
}
}
}
if (unp->_depAdr == 0) {
q = 0x81b;
if (u32eq(mem + q + 0x00, 0x852FA978) &&
u32eq(mem + q + 0x04, 0x8534A900) &&
u32eq(mem + q + 0x14, 0x03860286) &&
u32eq(mem + q + 0x4f, 0xA200594C) &&
u32eq(mem + q + 0xad, 0x2000124C)) {
unp->_forced = q;
unp->_info->_run = q;
unp->_depAdr = 0x12;
unp->_rtiFrc = 1;
}
}
/* expert 2.11 (sys2074) & unknown sys2061 */
if (unp->_depAdr == 0) {
for (q = 0x80d; q < 0x820; q++) {
if (u32eq(mem + q + 0x00, 0x852FA978) &&
u32eq(mem + q + 0x04, 0x8534A900) &&
u32eq(mem + q + 0x13, 0x03840284) &&
u32eq(mem + q + 0x4f, 0x084C003A) &&
u32eq(mem + q + 0xad, 0x00AA2048)) {
unp->_forced = q;
unp->_info->_run = q;
unp->_depAdr = 0x100 + mem[q + 0x17a] + 1;
break;
}
}
if (unp->_depAdr != 0) {
unp->_rtiFrc = 1;
}
}
if (unp->_depAdr != 0) {
unp->_idFlag = 1;
return;
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,125 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnMasterCompressor(UnpStr *unp) {
byte *mem;
int p;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
for (p = 0x80d; p < 0x880; p++) {
if (u32eqmasked(mem + p + 0x005, 0x00ffffff, 0x00BDD2A2) &&
u32eq(mem + p + 0x00a, 0xE000F99D) &&
u32eq(mem + p + 0x017, 0xCAEDD0CA) &&
u32eq(mem + p + 0x031, 0x84C82E86) &&
u32eqmasked(mem + p + 0x035, 0x0000ffff, 0x00004C2D) &&
u32eq(mem + p + 0x134, 0xDBD0FFE6)) {
if (/*mem[p]==0x78&&*/ mem[p + 1] == 0xa9 &&
u32eq(mem + p + 0x003, 0xD2A20185)) {
unp->_depAdr = READ_LE_UINT16(&mem[p + 0x37]);
unp->_forced = p + 1;
if (mem[p + 0x12b] == 0x020) // jsr $0400, unuseful fx
mem[p + 0x12b] = 0x2c;
} else if (u32eq(mem + p, 0xD024E0E8)) {
/* HTL version */
unp->_depAdr = READ_LE_UINT16(&mem[p + 0x37]);
unp->_forced = 0x840;
}
if (unp->_depAdr) {
unp->_retAdr = READ_LE_UINT16(&mem[p + 0x13e]);
unp->_endAdr = 0x2d;
unp->_fStrBf = unp->_endAdr;
unp->_idFlag = 1;
return;
}
}
}
}
if (unp->_depAdr == 0) {
for (p = 0x80d; p < 0x880; p++) {
if (u32eqmasked(mem + p + 0x005, 0x00ffffff, 0x00BDD2A2) &&
u32eq(mem + p + 0x00a, 0xE000F99D) &&
u32eq(mem + p + 0x017, 0xCAEDD0CA) &&
u32eq(mem + p + 0x031, 0x84C82E86) &&
u32eqmasked(mem + p + 0x035, 0x0000ffff, 0x00004C2D) &&
u32eq(mem + p + 0x12d, 0xe2D0FFE6)) {
if (mem[p + 1] == 0xa9 &&
u32eq(mem + p + 0x003, 0xD2A20185)) {
unp->_depAdr = READ_LE_UINT16(&mem[p + 0x37]);
unp->_forced = p + 1;
}
if (unp->_depAdr) {
if (mem[p + 0x136] == 0x4c)
unp->_retAdr = READ_LE_UINT16(&mem[p + 0x137]);
else if (mem[p + 0x13d] == 0x4c)
unp->_retAdr = READ_LE_UINT16(&mem[p + 0x13e]);
unp->_endAdr = 0x2d;
unp->_fStrBf = unp->_endAdr;
unp->_idFlag = 1;
return;
}
}
}
}
if (unp->_depAdr == 0) {
p = 0x812;
if (u32eq(mem + p + 0x000, 0xE67800A0) &&
u32eq(mem + p + 0x004, 0x0841B901) &&
u32eq(mem + p + 0x008, 0xB900FA99) &&
u32eq(mem + p + 0x00c, 0x34990910)) {
unp->_depAdr = 0x100;
unp->_forced = p;
unp->_retAdr = READ_LE_UINT16(&mem[0x943]); // mem[0x943] | mem[0x944] << 8;
unp->_endAdr = 0x2d;
unp->_fStrBf = unp->_endAdr;
unp->_idFlag = 1;
return;
}
}
/* Fred/Channel4 hack */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x811, 0xA9A98078) &&
u32eq(mem + 0x815, 0x85EE8034) &&
u32eq(mem + 0x819, 0x802DA201) &&
u32eq(mem + 0x882, 0x01004C2D)) {
unp->_depAdr = 0x100;
unp->_forced = 0x811;
unp->_retAdr = READ_LE_UINT16(&mem[0x98b]); // mem[0x98b] | mem[0x98c] << 8;
if (unp->_retAdr < 0x800)
unp->_rtAFrc = 1;
unp->_endAdr = 0x2d;
unp->_idFlag = 1;
return;
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,82 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnMegabyte(UnpStr *unp) {
byte *mem;
int p;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
p = 0;
if (mem[0x816] == 0x4c)
p = READ_LE_UINT16(&mem[0x817]); // mem[0x817] | mem[0x818] << 8;
else if (unp->_info->_run == 0x810 && mem[0x814] == 0x4c &&
u32eqmasked(mem + 0x810, 0xffff00ff, 0x018500A9))
p = READ_LE_UINT16(&mem[0x815]); // mem[0x815] | mem[0x816] << 8;
if (p) {
if (mem[p + 0] == 0x78 && mem[p + 1] == 0xa2 &&
mem[p + 3] == 0xa0 &&
u32eq(mem + p + 0x05, 0x15841486) &&
u32eq(mem + p + 0x1d, 0x03804CF7)) {
unp->_depAdr = 0x380;
unp->_endAdr = READ_LE_UINT16(&mem[p + 0x55]); // mem[p + 0x55] | mem[p + 0x56] << 8;
unp->_endAdr++;
unp->_strMem = 0x801;
unp->_retAdr = 0x801; /* usually it just runs */
unp->_idFlag = 1;
return;
}
}
}
if (unp->_depAdr == 0) {
p = 0;
if (mem[0x81a] == 0x4c &&
u32eqmasked(mem + 0x816, 0xffff00ff, 0x018500A9))
p = READ_LE_UINT16(&mem[0x81b]); // mem[0x81b] | mem[0x81c] << 8;
if (p) {
if (mem[p + 0] == 0x78 && mem[p + 1] == 0xa2 &&
mem[p + 3] == 0xa0 &&
u32eq(mem + p + 0x05, 0x15841486) &&
u32eq(mem + p + 0x1d, 0x03844CF7)) {
unp->_depAdr = 0x384;
unp->_forced = 0x816;
unp->_endAdr = READ_LE_UINT16(&mem[p + 0x59]); // mem[p + 0x59] | mem[p + 0x5a] << 8;
unp->_endAdr++;
unp->_strMem = 0x801;
unp->_retAdr = 0x801; /* usually it just runs */
unp->_idFlag = 1;
return;
}
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,194 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnPuCrunch(UnpStr *unp) {
byte *mem;
int q, p;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
if (mem[0x80d] == 0x78 &&
u32eq(mem + 0x813, 0x34A20185) &&
u32eq(mem + 0x817, 0x9D0842BD) &&
u32eq(mem + 0x81b, 0xD0CA01FF) &&
u32eq(mem + 0x83d, 0x4CEDD088)) {
for (p = 0x912; p < 0x938; p++) {
if (u32eq(mem + p, 0x2D85FAA5) &&
u32eq(mem + p + 4, 0x2E85FBA5)) {
unp->_endAdr = 0xfa;
unp->_strMem = READ_LE_UINT16(&mem[0x879]); // mem[0x879] | mem[0x87a] << 8;
unp->_depAdr = READ_LE_UINT16(&mem[0x841]); // mem[0x841] | mem[0x842] << 8;
unp->_retAdr = READ_LE_UINT16(&mem[p + 0xa]); // mem[p + 0xa] | mem[p + 0xb] << 8;
unp->_forced = 0x80d;
break;
}
}
} else if (mem[0x80d] == 0x78 &&
u32eq(mem + 0x81a, 0x10CA4B95) &&
u32eq(mem + 0x81e, 0xBD3BA2F8) &&
u32eq(mem + 0x847, 0x4CEDD088)) {
for (p = 0x912; p < 0x938; p++) {
if (u32eq(mem + p, 0x2D85FAA5) &&
u32eq(mem + p + 4, 0x2E85FBA5)) {
unp->_endAdr = 0xfa;
unp->_strMem = READ_LE_UINT16(&mem[p + 0x88a]); // mem[0x88a] | mem[0x88b] << 8;
unp->_depAdr = READ_LE_UINT16(&mem[p + 0x84b]); // mem[0x84b] | mem[0x84c] << 8;
unp->_retAdr = READ_LE_UINT16(&mem[p + 0xa]); // mem[p + 0xa] | mem[p + 0xb] << 8;
unp->_forced = 0x80d;
break;
}
}
} else if (mem[0x80d] == 0x78 &&
u32eq(mem + 0x811, 0x85AAA901) &&
u32eq(mem + 0x81d, 0xF69D083C) &&
u32eq(mem + 0x861, 0xC501C320) &&
u32eq(mem + 0x839, 0x01164CED)) {
unp->_endAdr = 0xfa;
unp->_strMem = READ_LE_UINT16(&mem[0x840]); // mem[0x840] | mem[0x841] << 8;
unp->_depAdr = 0x116;
unp->_retAdr = READ_LE_UINT16(&mem[0x8df]); // mem[0x8df] | mem[0x8e0] << 8;
unp->_forced = 0x80d;
} else if (mem[0x80d] == 0x78 &&
u32eq(mem + 0x811, 0x85AAA901) &&
u32eq(mem + 0x81d, 0xF69D083C) &&
u32eq(mem + 0x861, 0xC501C820) &&
u32eq(mem + 0x839, 0x01164CED)) {
unp->_endAdr = 0xfa;
unp->_strMem = READ_LE_UINT16(&mem[0x840]); // mem[0x840] | mem[0x841] << 8;
unp->_depAdr = 0x116;
if (mem[0x8de] == 0xa9) {
unp->_retAdr = READ_LE_UINT16(&mem[0x8e1]); // mem[0x8e1] | mem[0x8e2] << 8;
if ((unp->_retAdr == 0xa871) && (mem[0x8e0] == 0x20) &&
(mem[0x8e3] == 0x4c)) {
mem[0x8e0] = 0x2c;
unp->_retAdr = READ_LE_UINT16(&mem[0x8e4]); // mem[0x8e4] | mem[0x8e5] << 8;
}
} else {
unp->_retAdr = READ_LE_UINT16(&mem[0x8df]); // mem[0x8df] | mem[0x8e0] << 8;
}
unp->_forced = 0x80d;
} else {
/* unknown old/hacked pucrunch ? */
for (p = 0x80d; p < 0x820; p++) {
if (mem[p] == 0x78) {
q = p;
for (; p < 0x824; p++) {
if (u32eqmasked(mem + p, 0xf0ffffff, 0xF0BD53A2) &&
u32eq(mem + p + 4, 0x01FF9D08) &&
u32eq(mem + p + 8, 0xA2F7D0CA)) {
unp->_forced = q;
q = mem[p + 3] & 0xf; /* can be $f0 or $f2, q&0x0f as offset */
p = READ_LE_UINT16(&mem[p + 0xe]); // mem[p + 0xe] | mem[p + 0xf] << 8;
if (mem[p - 2] == 0x4c && mem[p + 0xa0 + q] == 0x85) {
unp->_depAdr = READ_LE_UINT16(&mem[p - 1]); // mem[p - 1] | mem[p] << 8;
unp->_strMem = READ_LE_UINT16(&mem[p + 4]); // mem[p + 4] | mem[p + 5] << 8;
unp->_endAdr = 0xfa;
p += 0xa2;
q = p + 8;
for (; p < q; p++) {
if (u32eq(mem + p, 0x2D85FAA5) &&
mem[p + 9] == 0x4c) {
unp->_retAdr = READ_LE_UINT16(&mem[p + 0xa]); // mem[p + 0xa] | mem[p + 0xb] << 8;
break;
}
}
}
}
}
}
}
}
/* various old/hacked pucrunch */
/* common pattern, variable pos from 0x79 to 0xd1
90 ?? C8 20 ?? 0? 85 ?? C9 ?0 90 0B A2 0? 20 ?? 0? 85 ?? 20 ?? 0? A8 20 ??
0? AA BD ?? 0? E0 20 90 0? 8A (A2 03) not always 20 ?? 02 A6
?? E8 20 F9
*/
if (unp->_depAdr == 0) {
unp->_idFlag = 0;
for (q = 0x70; q < 0xff; q++) {
if (u32eqmasked(mem + 0x801 + q, 0xFFFF00FF, 0x20C80090) &&
u32eqmasked(mem + 0x801 + q + 8, 0xFFFF0FFF,
0x0B9000C9) &&
u32eqmasked(mem + 0x801 + q + 12, 0x00FFF0FF,
0x002000A2) &&
u32eqmasked(mem + 0x801 + q + 30, 0xF0FFFFFf,
0x009020E0)) {
unp->_idFlag = 385;
break;
}
}
if (unp->_idFlag) {
for (p = 0x801 + q + 34; p < 0x9ff; p++) {
if (u32eq(mem + p, 0x00F920E8)) {
for (; p < 0x9ff; p++) {
if (mem[p] == 0x4c) {
unp->_retAdr = READ_LE_UINT16(&mem[p + 1]);
if (unp->_retAdr > 0x257)
break;
}
}
break;
}
}
for (p = 0; p < 0x40; p++) {
if (unp->_info->_run == -1)
if (unp->_forced == 0) {
if (mem[0x801 + p] == 0x78) {
unp->_forced = 0x801 + p;
unp->_info->_run = unp->_forced;
}
}
if (u32eq(mem + 0x801 + p, 0xCA00F69D) &&
mem[0x801 + p + 0x1b] == 0x4c) {
q = 0x801 + p + 0x1c;
unp->_depAdr = READ_LE_UINT16(&mem[q]);
q = 0x801 + p - 2;
p = READ_LE_UINT16(&mem[q]);
if ((mem[p + 3] == 0x8d) && (mem[p + 6] == 0xe6)) {
unp->_strMem = READ_LE_UINT16(&mem[p + 4]);
}
break;
}
}
unp->_endAdr = 0xfa; // some hacks DON'T xfer fa/b to 2d/e
unp->_idFlag = 1;
}
}
if (unp->_depAdr) {
unp->_idFlag = 1;
return;
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,72 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "glk/scott/unp64/unp64.h"
namespace Glk {
namespace Scott {
void scnECA(UnpStr *unp);
void scnExpert(UnpStr *unp);
void scnCruel(UnpStr *unp);
void scnPuCrunch(UnpStr *unp);
void scnByteBoiler(UnpStr *unp);
void scnMasterCompressor(UnpStr *unp);
void scnTCScrunch(UnpStr *unp);
void scnTBCMultiComp(UnpStr *unp);
void scnXTC(UnpStr *unp);
void scnCCS(UnpStr *unp);
void scnMegabyte(UnpStr *unp);
void scnSection8(UnpStr *unp);
void scnCaution(UnpStr *unp);
void scnActionPacker(UnpStr *unp);
void scnExomizer(UnpStr *unp);
Scnptr g_scanFunc[] = {
scnECA,
scnExpert,
scnCruel,
scnPuCrunch,
scnByteBoiler,
scnMasterCompressor,
scnTCScrunch,
scnTBCMultiComp,
scnXTC,
scnCCS,
scnMegabyte,
scnSection8,
scnCaution,
scnActionPacker,
scnExomizer
};
void scanners(UnpStr* unp) {
int x, y;
y = sizeof(g_scanFunc) / sizeof(*g_scanFunc);
for (x = 0; x < y; x++) {
(g_scanFunc[x])(unp);
if (unp->_idFlag)
break;
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,103 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnSection8(UnpStr *unp) {
byte *mem;
int p;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
for (p = 0x810; p <= 0x828; p++) {
if (u32eq(mem + p, (unsigned int)(0x00BD00A2 + (((p & 0xff) + 0x11) << 24))) &&
u32eq(mem + p + 0x04, 0x01009D08) &&
u32eq(mem + p + 0x10, 0x34A97801) &&
u32eq(mem + p + 0x6a, 0xB1017820) &&
u32eq(mem + p + 0x78, 0x017F20AE)) {
unp->_depAdr = 0x100;
break;
}
}
if (unp->_depAdr) {
if (unp->_info->_run == -1)
unp->_forced = p;
unp->_strMem = mem[p + 0x47] | mem[p + 0x4b] << 8;
unp->_retAdr = READ_LE_UINT16(&mem[p + 0x87]); // mem[p + 0x87] | mem[p + 0x88] << 8;
if (unp->_retAdr == 0xf7) {
unp->_retAdr = 0xa7ae;
mem[p + 0x87] = 0xae;
mem[p + 0x88] = 0xa7;
}
unp->_endAdr = 0xae;
unp->_idFlag = 1;
return;
}
}
/* Crackman variant? */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x827, 0x38BD00A2) &&
u32eq(mem + 0x82b, 0x01009D08) &&
u32eq(mem + 0x837, 0x34A97801) &&
u32eq(mem + 0x891, 0xB1018420) &&
u32eq(mem + 0x89f, 0x018b20AE)) {
unp->_depAdr = 0x100;
if (unp->_info->_run == -1)
unp->_forced = 0x827;
unp->_strMem = mem[0x86e] | mem[0x872] << 8;
if (u16eq(mem + 0x8b7, 0xff5b)) {
mem[0x8b6] = 0x2c;
unp->_retAdr = READ_LE_UINT16(&mem[0x8ba]); // mem[0x8ba] | mem[0x8bb] << 8;
} else {
unp->_retAdr = READ_LE_UINT16(&mem[0x8b7]); // mem[0x8b7] | mem[0x8b8] << 8;
}
unp->_endAdr = 0xae;
unp->_idFlag = 1;
return;
}
}
/* PET||SLAN variant? */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x812, 0x20BD00A2) &&
u32eq(mem + 0x816, 0x033c9D08) &&
u32eq(mem + 0x863, 0xB103B420) &&
u32eq(mem + 0x86c, 0x03BB20AE)) {
unp->_depAdr = 0x33c;
if (unp->_info->_run == -1)
unp->_forced = 0x812;
unp->_strMem = mem[0x856] | mem[0x85a] << 8;
unp->_retAdr = READ_LE_UINT16(&mem[0x896]); // mem[0x896] | mem[0x897] << 8;
unp->_endAdr = 0xae;
unp->_idFlag = 1;
return;
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,166 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnTBCMultiComp(UnpStr *unp) {
byte *mem;
int p = 0, q = 0, strtmp;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
if (u32eqmasked(mem + 0x82c, 0xfffffffd, 0x9ACA0184) &&
u32eq(mem + 0x830, 0xA001004C) &&
u32eq(mem + 0x834, 0x84FD8400) &&
u32eq(mem + 0x8a2, 0x01494C01)) {
/*normal 2080*/
if (mem[0x84a] == 0x81) {
if (u32eq(mem + 0x820, 0x32BDE9A2)) {
unp->_forced = 0x820;
unp->_retAdr = READ_LE_UINT16(&mem[0x8b2]); // mem[0x8b2] | mem[0x8b3] << 8;
if (unp->_retAdr == 0x1e1) {
if (u32eq(mem + 0x916, 0x4CA87120)) {
p = *(unsigned short int *)(mem + 0x91a);
if (p == 0xa7ae) {
unp->_retAdr = p;
mem[0x8b2] = 0xae;
mem[0x8b3] = 0xa7;
} else {
mem[0x916] = 0x2c;
unp->_retAdr = p;
}
} else if ((mem[0x916] == 0x4C) || (mem[0x916] == 0x20)) {
unp->_retAdr = READ_LE_UINT16(&mem[0x917]); // mem[0x917] | mem[0x918] << 8;
} else if (mem[0x919] == 0x4c) {
unp->_retAdr = READ_LE_UINT16(&mem[0x91a]); // mem[0x91a] | mem[0x91b] << 8;
}
}
if ((unp->_retAdr == 0) && (mem[0x8b1] == 0)) {
unp->_retAdr = 0xa7ae;
mem[0x8b1] = 0x4c;
mem[0x8b2] = 0xae;
mem[0x8b3] = 0xa7;
}
p = 0x8eb;
}
}
/*firelord 2076*/
else if (mem[0x84a] == 0x7b) {
if (u32eq(mem + 0x81d, 0x32BDE9A2)) {
unp->_forced = 0x81d;
unp->_retAdr = READ_LE_UINT16(&mem[0x8ac]); // mem[0x8ac] | mem[0x8ad] << 8;
p = 0x8eb;
}
}
if (unp->_forced) {
unp->_depAdr = 0x100;
unp->_strMem = READ_LE_UINT16(&mem[p + 1]); // mem[p + 1] | mem[p + 2] << 8;
q = p;
q += mem[p];
unp->_endAdr = 0;
for (; q > p; q -= 4) {
strtmp = READ_LE_UINT16(&mem[q - 1]); //(mem[q - 1] | mem[q] << 8);
if (strtmp == 0)
strtmp = 0x10000;
if (strtmp > unp->_endAdr)
unp->_endAdr = strtmp;
}
unp->_idFlag = 1;
return;
}
}
}
/* TBC Multicompactor ? very similar but larger code */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x822, 0x9D083DBD) &&
u32eq(mem + 0x826, 0xD0CA0333) &&
u32eq(mem + 0x832, 0xF7D0CA00) &&
u32eq(mem + 0x836, 0xCA018678) &&
u32eq(mem + 0x946, 0xADC5AFA5)) {
if (unp->_info->_run == -1) {
for (p = 0x81e; p < 0x821; p++) {
if (mem[p] == 0xa2) {
unp->_forced = p;
break;
}
}
}
unp->_depAdr = 0x334;
unp->_retAdr = READ_LE_UINT16(&mem[0x92a]); // mem[0x92a] | mem[0x92b] << 8;
p = 0x94d;
unp->_strMem = READ_LE_UINT16(&mem[p + 1]); // mem[p + 1] | mem[p + 2] << 8;
q = p;
q += mem[p];
unp->_endAdr = 0;
for (; q > p; q -= 4) {
strtmp = READ_LE_UINT16(&mem[q - 1]); //(mem[q - 1] | mem[q] << 8);
if (strtmp == 0)
strtmp = 0x10000;
if (strtmp > unp->_endAdr)
unp->_endAdr = strtmp;
}
unp->_idFlag = 1;
return;
}
}
/*"AUTOMATIC BREAK SYSTEM" found in Manowar Cracks*/
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x835, 0x9D0845BD) &&
u32eq(mem + 0x839, 0xD0CA00ff) &&
u32eq(mem + 0x83e, 0xCA018678) &&
u32eq(mem + 0x8e1, 0xADC5AFA5)) {
if (unp->_info->_run == -1) {
for (p = 0x830; p < 0x834; p++) {
if (mem[p] == 0xa2) {
unp->_forced = p;
break;
}
}
}
unp->_depAdr = 0x100;
unp->_retAdr = READ_LE_UINT16(&mem[0x8c5]); // mem[0x8c5] | mem[0x8c6] << 8;
p = 0x8fe;
unp->_strMem = READ_LE_UINT16(&mem[p + 1]); // mem[p + 1] | mem[p + 2] << 8;
q = p;
q += mem[p];
unp->_endAdr = 0;
for (; q > p; q -= 4) {
strtmp = READ_LE_UINT16(&mem[q - 1]); //(mem[q - 1] | mem[q] << 8);
if (strtmp == 0)
strtmp = 0x10000;
if (strtmp > unp->_endAdr)
unp->_endAdr = strtmp;
}
unp->_idFlag = 1;
return;
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,78 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnTCScrunch(UnpStr *unp) {
byte *mem;
int q, p;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x819, 0x018536A9) && mem[0x81d] == 0x4c) {
p = READ_LE_UINT16(&mem[0x81e]); // mem[0x81e] | mem[0x81f] << 8;
if (mem[p] == 0xa2 && mem[p + 2] == 0xbd &&
u32eq(mem + p + 0x05, 0xE801109D) &&
(u32eq(mem + p + 0x38, 0x01524CFB) ||
(u32eq(mem + p + 0x38, 0x8DE1A9FB) &&
u32eq(mem + p + 0x3c, 0x524C0328)))) {
unp->_depAdr = 0x334;
unp->_forced = 0x819;
unp->_endAdr = 0x2d;
}
} else if (u32eq(mem + 0x819, 0x018534A9) && mem[0x81d] == 0x4c) {
p = READ_LE_UINT16(&mem[0x81e]); // mem[0x81e] | mem[0x81f] << 8;
if (mem[p] == 0xa2 && mem[p + 2] == 0xbd &&
u32eq(mem + p + 0x05, 0xE801109D) &&
u32eq(mem + p + 0x38, 0x01304CFB)) {
unp->_depAdr = 0x334;
unp->_forced = 0x818;
if (mem[unp->_forced] != 0x78)
unp->_forced++;
unp->_endAdr = 0x2d;
unp->_retAdr = READ_LE_UINT16(&mem[p + 0xd9]); // mem[p + 0xd9] | mem[p + 0xda] << 8;
p += 0xc8;
q = p + 6;
for (; p < q; p += 3) {
if (mem[p] == 0x20 &&
u16gteq(mem + p + 1, 0xa000) &&
u16lteq(mem + p + 1, 0xbfff)) {
mem[p] = 0x2c;
}
}
}
}
if (unp->_depAdr) {
unp->_idFlag = 1;
return;
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,162 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/unp64.h"
#include "glk/scott/unp64/exo_util.h"
namespace Glk {
namespace Scott {
void scnXTC(UnpStr *unp) {
byte *mem;
int q = 0, p;
if (unp->_idFlag)
return;
mem = unp->_mem;
if (unp->_depAdr == 0) {
if (u16eq(mem + 0x80d, 0xE678) &&
u32eq(mem + 0x811, 0x1BCE0818) &&
u32eq(mem + 0x819, 0xC8000099) &&
u32eq(mem + 0x82c, 0x4CF7D0CA) &&
mem[0x85c] == 0x99) {
unp->_retAdr = READ_LE_UINT16(&mem[0x872]); // mem[0x872] | mem[0x873] << 8;
unp->_depAdr = 0x100;
unp->_forced = 0x80d; /* the ldy #$00 can be missing, skipped */
unp->_fEndAf = 0x121;
unp->_endAdC = 0xffff | EA_USE_Y;
unp->_strMem = READ_LE_UINT16(&mem[0x85d]); // mem[0x85d] | mem[0x85e] << 8;
unp->_idFlag = 1;
return;
}
}
/* XTC packer 1.0 & 2.2/2.4 */
if (unp->_depAdr == 0) {
for (p = 0x801; p < 0x80c; p += 0x0a) {
if (u16eq(mem + p + 0x02, 0xE678) &&
u32eq(mem + p + 0x07, (unsigned int)(0xce08 | ((p + 0x10) << 16))) &&
u32eq(mem + p + 0x0e, 0xC8000099) &&
u32eq(mem + p + 0x23, 0x4CF7D0CA)) {
/* has variable codebytes so addresses varies */
for (q = p + 0x37; q < p + 0x60; q += 4) {
if (mem[q] == 0xc9)
continue;
if (mem[q] == 0x99) {
unp->_depAdr = 0x100;
break;
}
break; /* unexpected byte, get out */
}
break;
}
}
if (unp->_depAdr) {
unp->_retAdr = READ_LE_UINT16(&mem[q + 0x16]); // mem[q + 0x16] | mem[q + 0x17] << 8;
if (u16noteq(mem + p, 0x00a0))
unp->_forced = p + 2; /* the ldy #$00 can be missing, skipped */
else
unp->_forced = p;
unp->_fEndAf = READ_LE_UINT16(&mem[q + 0x7]); // mem[q + 0x7] | mem[q + 0x8] << 8;
unp->_fEndAf--;
unp->_endAdC = 0xffff | EA_USE_Y;
unp->_strMem = READ_LE_UINT16(&mem[q + 1]); // mem[q + 1] | mem[q + 2] << 8;
if (u32eq(mem + q + 0x1f, 0xDDD00285)) {
} else if (u32eq(mem + q + 0x1f, 0xF620DFD0)) {
/* rockstar's 2.2+ & shade/light's 2.4 are all the same */
} else { /* actually found to be Visiomizer 6.2/Zagon */
unp->_depAdr = READ_LE_UINT16(&mem[p + 0x27]); // mem[p + 0x27] | mem[p + 0x28] << 8;
}
unp->_idFlag = 1;
return;
}
}
/* XTC 2.3 / 6codezipper */
if (unp->_depAdr == 0) {
if (u32eq(mem + 0x803, 0xB9018478) &&
u32eq(mem + 0x80b, 0xF7D0C8FF) &&
u32eq(mem + 0x81b, 0x00FC9D08) &&
u32eq(mem + 0x85b, 0xD0D0FFE4)) {
unp->_depAdr = READ_LE_UINT16(&mem[0x823]); // mem[0x823] | mem[0x824] << 8;
unp->_forced = 0x803;
unp->_retAdr = READ_LE_UINT16(&mem[0x865]); // mem[0x865] | mem[0x866] << 8;
unp->_strMem = READ_LE_UINT16(&mem[0x850]); // mem[0x850] | mem[0x851] << 8;
unp->_endAdC = 0xffff | EA_USE_Y;
unp->_fEndAf = 0x128;
unp->_idFlag = 1;
return;
}
}
/* XTC 2.3 / G*P, probably by Rockstar */
if (unp->_depAdr == 0) {
if ((u32eq(mem + 0x803, 0xB901e678) ||
u32eq(mem + 0x803, 0xB9018478)) &&
u32eq(mem + 0x80b, 0xF7D0C8FF) &&
u32eq(mem + 0x81b, 0x00F59D08) &&
u32eq(mem + 0x85b, 0xD0D0F8E4)) {
unp->_depAdr = READ_LE_UINT16(&mem[0x823]); // mem[0x823] | mem[0x824] << 8;
unp->_forced = 0x803;
unp->_retAdr = READ_LE_UINT16(&mem[0x865]); // mem[0x865] | mem[0x866] << 8;
unp->_strMem = READ_LE_UINT16(&mem[0x850]); // mem[0x850] | mem[0x851] << 8;
unp->_endAdC = 0xffff | EA_USE_Y;
unp->_fEndAf = 0x121;
unp->_idFlag = 1;
return;
}
}
/* XTC packer 2.x? found in G*P/NEI/Armageddon warez
just some different byte on copy loop, else is equal to 2.3
*/
if (unp->_depAdr == 0) {
for (p = 0x801; p < 0x80c; p += 0x0a) {
if (u32eqmasked(mem + p + 0x00, 0xffff0000, 0xE6780000) &&
u32eqmasked(mem + p + 0x05, 0xffff00ff, 0xB90800CE) &&
u32eq(mem + p + 0x0b, 0xC8000099) &&
u32eq(mem + p + 0x1e, 0x4CF7D0CA)) {
/* has variable codebytes so addresses varies */
for (q = p + 0x36; q < p + 0x60; q += 4) {
if (mem[q] == 0xc9)
continue;
if (mem[q] == 0x99) {
unp->_depAdr = 0x100;
break;
}
break; /* unexpected byte, get out */
}
break;
}
}
if (unp->_depAdr) {
unp->_retAdr = READ_LE_UINT16(&mem[q + 0x16]); // mem[q + 0x16] | mem[q + 0x17] << 8;
unp->_forced = p + 2;
unp->_fEndAf = READ_LE_UINT16(&mem[q + 0x7]); // mem[q + 0x7] | mem[q + 0x8] << 8;
unp->_fEndAf--;
unp->_endAdC = 0xffff | EA_USE_Y;
unp->_strMem = READ_LE_UINT16(&mem[q + 1]); // mem[q + 1] | mem[q + 2] << 8;
unp->_idFlag = 1;
return;
}
}
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,735 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// This is a cut-down version of UNP64 with only the bare minimum
// needed to decompress a number of Scott Adams Commodore 64 games
// for the ScottFree interpreter.
/*
UNP64 - generic Commodore 64 prg unpacker
(C) 2008-2018 iAN CooG/HVSC Crew^C64Intros
original source and idea: testrun.c, taken from exo20b7
Follows original disclaimer
*/
/*
* Copyright (c) 2002 - 2023 Magnus Lind.
*
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
*/
#include "glk/scott/globals.h"
#include "glk/scott/types.h"
#include "glk/scott/unp64/6502_emu.h"
#include "glk/scott/unp64/exo_util.h"
#include "glk/scott/unp64/unp64.h"
#include "common/util.h"
namespace Glk {
namespace Scott {
void reinitUnp(void) {
_G(_unp)._idFlag = 0;
_G(_unp)._forced = 0;
_G(_unp)._strMem = 0x800;
_G(_unp)._retAdr = 0x800;
_G(_unp)._depAdr = 0;
_G(_unp)._endAdr = 0x10000;
_G(_unp)._rtAFrc = 0;
_G(_unp)._wrMemF = 0;
_G(_unp)._lfMemF = 0;
_G(_unp)._exoFnd = 0;
_G(_unp)._ecaFlg = 0;
_G(_unp)._fEndBf = 0;
_G(_unp)._fEndAf = 0;
_G(_unp)._fStrAf = 0;
_G(_unp)._fStrBf = 0;
_G(_unp)._mon1st = 0;
}
int isBasicRun1(int pc) {
if (pc == 0xa7ae || pc == 0xa7ea || pc == 0xa7b1 || pc == 0xa474 || pc == 0xa533 || pc == 0xa871 || pc == 0xa888 || pc == 0xa8bc)
return 1;
else
return 0;
}
int isBasicRun2(int pc) {
if (isBasicRun1(pc) || ((pc >= 0xA57C) && (pc <= 0xA659)) || pc == 0xa660 || pc == 0xa68e)
return 1;
else
return 0;
}
int unp64(byte *compressed, size_t length, byte *destinationBuffer, size_t *finalLength, const char *switches) {
char settings[4][64];
int numSettings = 0;
if (switches != NULL) {
char string[100];
size_t string_length = strlen(switches);
if (string_length > 0 && string_length < 100) {
snprintf(string, sizeof string, "%s", switches);
char *setting = strtok(string, " ");
while (setting != NULL && numSettings < 4) {
snprintf(settings[numSettings], sizeof settings[numSettings], "%s", setting);
numSettings++;
setting = strtok(NULL, " ");
}
}
}
CpuCtx r[1];
LoadInfo info[1];
char name[260] = {0}, forcedname[260] = {0};
byte mem[65536] = {0}, oldmem[65536] = {0};
byte vector[0x20] = {0x31, 0xEA, 0x66, 0xFE, 0x47, 0xFE, 0x4A, 0xF3,
0x91, 0xF2, 0x0E, 0xF2, 0x50, 0xF2, 0x33, 0xF3,
0x57, 0xF1, 0xCA, 0xF1, 0xED, 0xF6, 0x3E, 0xF1,
0x2F, 0xF3, 0x66, 0xFE, 0xA5, 0xF4, 0xED, 0xF5};
byte stack[0x100] = {0x33, 0x38, 0x39, 0x31, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7D,
0xEA, 0x00, 0x00, 0x82, 0x22, 0x0E, 0xBC, 0x81, 0x64, 0xB8,
0x0C, 0xBD, 0xBA, 0xB7, 0xBC, 0x03, 0x00, 0x46, 0xE1, 0xE9,
0xA7, 0xA7, 0x79, 0xA6, 0x9C, 0xE3};
int iterMax = ITERMAX;
int p;
memset(&_G(_unp), 0, sizeof(_G(_unp)));
reinitUnp();
_G(_unp)._fStack = 1;
_G(_unp)._mem = mem;
_G(_unp)._r = r;
_G(_unp)._name = name;
_G(_unp)._info = info;
p = 0;
if (numSettings != 0) {
if (settings[0][0] == '-' && _G(_parsePar) && settings[0][1] == 'f') {
strToInt(settings[p] + 2, (int *)&_G(_unp)._filler);
if (_G(_unp)._filler) {
memset(mem + (_G(_unp)._filler >> 16), _G(_unp)._filler & 0xff, 0x10000 - (_G(_unp)._filler >> 16));
}
p++;
}
}
looprecurse:
info->_basicTxtStart = 0x801;
loadData(compressed, length, mem, info);
/* no start address from load */
if (info->_run == -1) {
/* look for sys line */
info->_run = findSys(mem + info->_basicTxtStart, 0x9e);
}
scanners(&_G(_unp));
if (_G(_unp)._idFlag == 2)
return 0;
if ((_G(_unp)._recurs == 0) && (numSettings > 0)) {
while (p < numSettings) {
if (settings[p][0] == '-') {
switch (settings[p][1]) {
case '-':
p = numSettings;
break;
case 'e':
strToInt(settings[p] + 2, &_G(_unp)._forced);
_G(_unp)._forced &= 0xffff;
if (_G(_unp)._forced < 0x1)
_G(_unp)._forced = 0;
break;
case 'a':
_G(_unp)._strMem = 2;
_G(_unp)._endAdr = 0x10001;
_G(_unp)._fEndAf = 0;
_G(_unp)._fStrAf = 0;
_G(_unp)._strAdC = 0;
_G(_unp)._endAdC = 0;
_G(_unp)._monEnd = 0;
_G(_unp)._monStr = 0;
break;
case 'r':
strToInt(settings[p] + 2, &_G(_unp)._retAdr);
_G(_unp)._retAdr &= 0xffff;
break;
case 'R':
strToInt(settings[p] + 2, &_G(_unp)._retAdr);
_G(_unp)._retAdr &= 0xffff;
_G(_unp)._rtAFrc = 1;
break;
case 'd':
strToInt(settings[p] + 2, &_G(_unp)._depAdr);
_G(_unp)._depAdr &= 0xffff;
break;
case 't':
strToInt(settings[p] + 2, &_G(_unp)._endAdr);
_G(_unp)._endAdr &= 0xffff;
if (_G(_unp)._endAdr >= 0x100)
_G(_unp)._endAdr++;
break;
case 'u':
_G(_unp)._wrMemF = 1;
break;
case 'l':
_G(_unp)._lfMemF = info->_end;
break;
case 's':
_G(_unp)._fStack = 0;
break;
case 'x':
break;
case 'B':
//copyRoms[0][1] = 1;
break;
case 'K':
//copyRoms[1][1] = 1;
break;
case 'c':
_G(_unp)._recurs++;
break;
case 'm': // keep undocumented for now
strToInt(settings[p] + 2, &iterMax);
}
}
p++;
}
}
if (_G(_unp)._idOnly) {
if (_G(_unp)._depAdr == 0)
return 0;
}
if (_G(_unp)._wrMemF | _G(_unp)._lfMemF) {
memcpy(oldmem, mem, sizeof(oldmem));
}
if (_G(_unp)._forced) {
info->_run = _G(_unp)._forced;
}
if (info->_run == -1) {
return 0;
}
if (_G(_unp)._strMem > _G(_unp)._retAdr) {
_G(_unp)._strMem = _G(_unp)._retAdr;
}
mem[0] = 0x60;
r->_cycles = 0;
mem[1] = 0x37;
if (((_G(_unp)._forced >= 0xa000) && (_G(_unp)._forced < 0xc000)) || (_G(_unp)._forced >= 0xd000))
mem[1] = 0x38;
/* some packers rely on basic pointers already set */
mem[0x2b] = info->_basicTxtStart & 0xff;
mem[0x2c] = info->_basicTxtStart >> 8;
if (info->_basicVarStart == -1) {
mem[0x2d] = info->_end & 0xff;
mem[0x2e] = info->_end >> 8;
} else {
mem[0x2d] = info->_basicVarStart & 0xff;
mem[0x2e] = info->_basicVarStart >> 8;
}
mem[0x2f] = mem[0x2d];
mem[0x30] = mem[0x2e];
mem[0x31] = mem[0x2d];
mem[0x32] = mem[0x2e];
mem[0xae] = info->_end & 0xff;
mem[0xaf] = info->_end >> 8;
/* CCS unpacker requires $39/$3a (current basic line number) set */
mem[0x39] = mem[0x803];
mem[0x3a] = mem[0x804];
mem[0x52] = 0;
mem[0x53] = 3;
if (_G(_unp)._fStack) {
memcpy(mem + 0x100, stack,
sizeof(stack)); /* stack as found on clean start */
r->_sp = 0xf6; /* sys from immediate mode leaves $f6 in stackptr */
} else {
r->_sp = 0xff;
}
if (info->_start > (long int)(0x314 + sizeof(vector))) {
/* some packers use values in irq pointers to decrypt themselves */
memcpy(mem + 0x314, vector, sizeof(vector));
}
mem[0x200] = 0x8a;
r->_mem = mem;
r->_pc = info->_run;
r->_flags = 0x20;
r->_a = 0;
r->_y = 0;
if (info->_run > 0x351) /* temporary for XIP */ {
r->_x = 0;
}
_G(_iter) = 0;
while ((_G(_unp)._depAdr ? r->_pc != _G(_unp)._depAdr : r->_pc >= _G(_unp)._retAdr)) {
if ((((mem[1] & 0x7) >= 6) && (r->_pc >= 0xe000)) || ((r->_pc >= 0xa000) && (r->_pc <= 0xbfff) && ((mem[1] & 0x7) > 6))) {
/* some packer relies on regs set at return from CLRSCR */
if ((r->_pc == 0xe536) || (r->_pc == 0xe544) || (r->_pc == 0xff5b) || ((r->_pc == 0xffd2) && (r->_a == 0x93))) {
if (r->_pc != 0xffd2) {
r->_x = 0x01;
r->_y = 0x84;
if (r->_pc == 0xff5b)
r->_a = 0x97; /* actually depends on $d012 */
else
r->_a = 0xd8;
r->_flags &= ~(128 | 2);
r->_flags |= (r->_a == 0 ? 2 : 0) | (r->_a & 128);
}
memset(mem + 0x400, 0x20, 1000);
}
/* intros */
if ((r->_pc == 0xffe4) || (r->_pc == 0xf13e)) {
static int flipspe4 = -1;
static unsigned char fpressedchars[] = {0x20, 0, 0x4e, 0, 3, 0, 0x5f, 0, 0x11, 00, 0x0d, 0, 0x31, 0};
flipspe4++;
if (flipspe4 > ARRAYSIZE(fpressedchars))
flipspe4 = 0;
r->_a = fpressedchars[flipspe4];
r->_flags &= ~(128 | 2);
r->_flags |= (r->_a == 0 ? 2 : 0) | (r->_a & 128);
}
if (r->_pc == 0xfd15) {
r->_a = 0x31;
r->_x = 0x30;
r->_y = 0xff;
}
if (r->_pc == 0xfda3) {
mem[0x01] = 0xe7;
r->_a = 0xd7;
r->_x = 0xff;
}
if (r->_pc == 0xffbd) {
mem[0xB7] = r->_a;
mem[0xBB] = r->_x;
mem[0xBC] = r->_y;
}
if ((r->_pc == 0xffd5) || (r->_pc == 0xf4a2)) {
break;
}
if (isBasicRun1(r->_pc)) {
info->_run = findSys(mem + info->_basicTxtStart, 0x9e);
if (info->_run > 0) {
r->_sp = 0xf6;
r->_pc = info->_run;
} else {
mem[0] = 0x60;
r->_pc = 0; /* force a RTS instead of executing ROM code */
}
} else {
mem[0] = 0x60;
r->_pc = 0; /* force a RTS instead of executing ROM code */
}
}
if (nextInst(r) == 1)
return 0;
_G(_iter)++;
if (_G(_iter) == iterMax) {
return 0;
}
if (_G(_unp)._exoFnd && (_G(_unp)._endAdr == 0x10000) && (r->_pc >= 0x100) && (r->_pc <= 0x200) && (_G(_unp)._strMem != 2)) {
_G(_unp)._endAdr = r->_mem[0xfe] + (r->_mem[0xff] << 8);
if ((_G(_unp)._exoFnd & 0xff) == 0x30) { /* low byte of _endAdr, it's a lda $ff00,y */
_G(_unp)._endAdr = (_G(_unp)._exoFnd >> 8) + (r->_mem[0xff] << 8);
} else if ((_G(_unp)._exoFnd & 0xff) == 0x32) { /* add 1 */
_G(_unp)._endAdr = 1 + ((_G(_unp)._exoFnd >> 8) + (r->_mem[0xff] << 8));
}
if (_G(_unp)._endAdr == 0)
_G(_unp)._endAdr = 0x10001;
}
if (_G(_unp)._fEndBf && (_G(_unp)._endAdr == 0x10000) && (r->_pc == _G(_unp)._depAdr)) {
_G(_unp)._endAdr = r->_mem[_G(_unp)._fEndBf] | r->_mem[_G(_unp)._fEndBf + 1] << 8;
_G(_unp)._endAdr++;
if (_G(_unp)._endAdr == 0)
_G(_unp)._endAdr = 0x10001;
_G(_unp)._fEndBf = 0;
}
if (_G(_unp)._fStrBf && (_G(_unp)._strMem != 0x2) && (r->_pc == _G(_unp)._depAdr)) {
_G(_unp)._strMem = r->_mem[_G(_unp)._fStrBf] | r->_mem[_G(_unp)._fStrBf + 1] << 8;
_G(_unp)._fStrBf = 0;
}
if (_G(_unp)._debugP) {
for (p = 0; p < 0x20; p += 2) {
if (*(unsigned short int *)(mem + 0x314 + p) != *(unsigned short int *)(vector + p)) {
*(unsigned short int *)(vector + p) = *(unsigned short int *)(mem + 0x314 + p);
}
}
}
}
_G(_iter) = 0;
while (_G(_unp)._rtAFrc ? r->_pc != _G(_unp)._retAdr : r->_pc < _G(_unp)._retAdr) {
if (_G(_unp)._monEnd && r->_pc == _G(_unp)._depAdr) {
p = r->_mem[_G(_unp)._monEnd >> 16] | r->_mem[_G(_unp)._monEnd & 0xffff] << 8;
if (p > (_G(_unp)._endAdr & 0xffff)) {
_G(_unp)._endAdr = p;
}
}
if (_G(_unp)._monStr && r->_pc == _G(_unp)._depAdr) {
p = r->_mem[_G(_unp)._monStr >> 16] | r->_mem[_G(_unp)._monStr & 0xffff] << 8;
if (p > 0) {
if (_G(_unp)._mon1st == 0) {
_G(_unp)._strMem = p;
}
_G(_unp)._mon1st = (unsigned int)_G(_unp)._strMem;
_G(_unp)._strMem = (p < _G(_unp)._strMem ? p : _G(_unp)._strMem);
}
}
if (r->_pc >= 0xe000) {
if (((mem[1] & 0x7) >= 6) && ((mem[1] & 0x7) <= 7)) {
mem[0] = 0x60;
r->_pc = 0;
}
}
if (nextInst(r) == 1)
return 0;
if ((mem[r->_pc] == 0x40) && (_G(_unp)._rtiFrc == 1)) {
_G(_unp)._retAdr = r->_pc;
_G(_unp)._rtAFrc = 1;
if (_G(_unp)._retAdr < _G(_unp)._strMem)
_G(_unp)._strMem = 2;
break;
}
_G(_iter)++;
if (_G(_iter) == iterMax) {
return 0;
}
if ((r->_pc >= 0xa000) && (r->_pc <= 0xbfff) && ((mem[1] & 0x7) == 7)) {
if (isBasicRun2(r->_pc)) {
r->_pc = 0xa7ae;
break;
} else {
mem[0] = 0x60;
r->_pc = 0;
}
}
if (r->_pc >= 0xe000) {
if (((mem[1] & 0x7) >= 6) && ((mem[1] & 0x7) <= 7)) {
if (r->_pc == 0xffbd) {
mem[0xB7] = r->_a;
mem[0xBB] = r->_x;
mem[0xBC] = r->_y;
}
/* return into IRQ handler, better stop here */
if (((r->_pc >= 0xea31) && (r->_pc <= 0xeb76)) || (r->_pc == 0xffd5) || (r->_pc == 0xfce2)) {
break;
}
if (r->_pc == 0xfda3) {
mem[0x01] = 0xe7;
r->_a = 0xd7;
r->_x = 0xff;
}
mem[0] = 0x60;
r->_pc = 0;
}
}
}
if (_G(_unp)._fEndAf && _G(_unp)._monEnd) {
_G(_unp)._endAdC = (unsigned int)(mem[_G(_unp)._fEndAf] | mem[_G(_unp)._fEndAf + 1] << 8);
if ((int)_G(_unp)._endAdC > _G(_unp)._endAdr)
_G(_unp)._endAdr = (int)_G(_unp)._endAdC;
_G(_unp)._endAdC = 0;
_G(_unp)._fEndAf = 0;
}
if (_G(_unp)._fEndAf && (_G(_unp)._endAdr == 0x10000)) {
_G(_unp)._endAdr = r->_mem[_G(_unp)._fEndAf] | r->_mem[_G(_unp)._fEndAf + 1] << 8;
if (_G(_unp)._endAdr == 0)
_G(_unp)._endAdr = 0x10000;
else
_G(_unp)._endAdr++;
_G(_unp)._fEndAf = 0;
}
if (_G(_unp)._fStrAf /*&&(_G(_unp)._strMem==0x800)*/) {
_G(_unp)._strMem = r->_mem[_G(_unp)._fStrAf] | r->_mem[_G(_unp)._fStrAf + 1] << 8;
_G(_unp)._strMem++;
_G(_unp)._fStrAf = 0;
}
if (_G(_unp)._exoFnd && (_G(_unp)._strMem != 2)) {
_G(_unp)._strMem = r->_mem[0xfe] + (r->_mem[0xff] << 8);
if ((_G(_unp)._exoFnd & 0xff) == 0x30) {
_G(_unp)._strMem += r->_y;
} else if ((_G(_unp)._exoFnd & 0xff) == 0x32) {
_G(_unp)._strMem += r->_y + 1;
}
}
if (r->_pc == 0xfce2) {
if ((*(unsigned int *)(mem + 0x8004) == 0x38cdc2c3) && (mem[0x8008] == 0x30)) {
r->_pc = r->_mem[0x8000] + (r->_mem[0x8001] << 8);
}
} else if (r->_pc == 0xa7ae) {
info->_basicTxtStart = mem[0x2b] | mem[0x2c] << 8;
if (info->_basicTxtStart == 0x801) {
info->_run = findSys(mem + info->_basicTxtStart, 0x9e);
if (info->_run > 0)
r->_pc = info->_run;
}
}
if (_G(_unp)._wrMemF) {
_G(_unp)._wrMemF = 0;
for (p = 0x800; p < 0x10000; p += 4) {
if (*(unsigned int *)(oldmem + p) == *(unsigned int *)(mem + p)) {
*(unsigned int *)(mem + p) = 0;
_G(_unp)._wrMemF = 1;
}
}
/* clean also the $fd30 table copy in RAM */
if (memcmp(mem + 0xfd30, vector, sizeof(vector)) == 0) {
memset(mem + 0xfd30, 0, sizeof(vector));
}
}
if (_G(_unp)._lfMemF) {
for (p = 0xffff; p > 0x0800; p--) {
if (oldmem[--_G(_unp)._lfMemF] == mem[p])
mem[p] = 0x0;
else {
if (p >= 0xffff)
_G(_unp)._lfMemF = 0 | _G(_unp)._ecaFlg;
break;
}
}
}
if (*forcedname) {
Common::sprintf_s(name, sizeof name, "%s", forcedname);
} else {
size_t ln = strlen(name);
if (ln > 248) {/* dirty hack in case name is REALLY long */
name[248] = 0;
ln = 248;
}
Common::sprintf_s(name + ln, sizeof(name) - ln, ".%04x%s", r->_pc, ((_G(_unp)._wrMemF | _G(_unp)._lfMemF) ? ".clean" : ""));
}
/* endadr is set to a ZP location? then use it as a pointer
todo: use __fEndAf instead, it can be used for any location, not only ZP. */
if (_G(_unp)._endAdr && (_G(_unp)._endAdr < 0x100)) {
p = (mem[_G(_unp)._endAdr] | mem[_G(_unp)._endAdr + 1] << 8) & 0xffff;
_G(_unp)._endAdr = p;
}
if (_G(_unp)._ecaFlg && (_G(_unp)._strMem != 2)) /* checkme */ {
if (_G(_unp)._endAdr >= ((_G(_unp)._ecaFlg >> 16) & 0xffff)) {
/* most of the times transfers $2000 byte from $d000-efff to $e000-ffff but there are exceptions */
if (_G(_unp)._lfMemF)
memset(mem + ((_G(_unp)._ecaFlg >> 16) & 0xffff), 0, 0x1000);
_G(_unp)._endAdr += 0x1000;
}
}
if (_G(_unp)._endAdr <= 0)
_G(_unp)._endAdr = 0x10000;
if (_G(_unp)._endAdr > 0x10000)
_G(_unp)._endAdr = 0x10000;
if (_G(_unp)._endAdr < _G(_unp)._strMem)
_G(_unp)._endAdr = 0x10000;
if (_G(_unp)._endAdC & 0xffff) {
_G(_unp)._endAdr += (_G(_unp)._endAdC & 0xffff);
_G(_unp)._endAdr &= 0xffff;
}
if (_G(_unp)._endAdC & EA_USE_A) {
_G(_unp)._endAdr += r->_a;
_G(_unp)._endAdr &= 0xffff;
}
if (_G(_unp)._endAdC & EA_USE_X) {
_G(_unp)._endAdr += r->_x;
_G(_unp)._endAdr &= 0xffff;
}
if (_G(_unp)._endAdC & EA_USE_Y) {
_G(_unp)._endAdr += r->_y;
_G(_unp)._endAdr &= 0xffff;
}
if (_G(_unp)._strAdC & 0xffff) {
_G(_unp)._strMem += (_G(_unp)._strAdC & 0xffff);
_G(_unp)._strMem &= 0xffff;
/* only if ea_addff, no reg involved */
if (((_G(_unp)._strAdC & 0xffff0000) == EA_ADDFF) && ((_G(_unp)._strMem & 0xff) == 0)) {
_G(_unp)._strMem += 0x100;
_G(_unp)._strMem &= 0xffff;
}
}
if (_G(_unp)._strAdC & EA_USE_A) {
_G(_unp)._strMem += r->_a;
_G(_unp)._strMem &= 0xffff;
if (_G(_unp)._strAdC & EA_ADDFF) {
if ((_G(_unp)._strMem & 0xff) == 0xff)
_G(_unp)._strMem++;
if (r->_a == 0) {
_G(_unp)._strMem += 0x100;
_G(_unp)._strMem &= 0xffff;
}
}
}
if (_G(_unp)._strAdC & EA_USE_X) {
_G(_unp)._strMem += r->_x;
_G(_unp)._strMem &= 0xffff;
if (_G(_unp)._strAdC & EA_ADDFF) {
if ((_G(_unp)._strMem & 0xff) == 0xff)
_G(_unp)._strMem++;
if (r->_x == 0) {
_G(_unp)._strMem += 0x100;
_G(_unp)._strMem &= 0xffff;
}
}
}
if (_G(_unp)._strAdC & EA_USE_Y) {
_G(_unp)._strMem += r->_y;
_G(_unp)._strMem &= 0xffff;
if (_G(_unp)._strAdC & EA_ADDFF) {
if ((_G(_unp)._strMem & 0xff) == 0xff)
_G(_unp)._strMem++;
if (r->_y == 0) {
_G(_unp)._strMem += 0x100;
_G(_unp)._strMem &= 0xffff;
}
}
}
if (_G(_unp)._endAdr <= 0)
_G(_unp)._endAdr = 0x10000;
if (_G(_unp)._endAdr > 0x10000)
_G(_unp)._endAdr = 0x10000;
if (_G(_unp)._endAdr < _G(_unp)._strMem)
_G(_unp)._endAdr = 0x10000;
mem[_G(_unp)._strMem - 2] = _G(_unp)._strMem & 0xff;
mem[_G(_unp)._strMem - 1] = _G(_unp)._strMem >> 8;
memcpy(destinationBuffer, mem + (_G(_unp)._strMem - 2), (size_t)(_G(_unp)._endAdr - _G(_unp)._strMem + 2));
*finalLength = (size_t)(_G(_unp)._endAdr - _G(_unp)._strMem + 2);
if (_G(_unp)._recurs) {
if (++_G(_unp)._recurs > RECUMAX)
return 1;
reinitUnp();
goto looprecurse;
}
return 1;
}
} // End of namespace Scott
} // End of namespace Glk

View File

@@ -0,0 +1,78 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GLK_SCOTT_UNP64_H
#define GLK_SCOTT_UNP64_H
namespace Glk {
namespace Scott {
struct LoadInfo;
struct CpuCtx;
struct UnpStr {
int _idFlag; /* flag, 1=packer identified; 2=not a packer, stop scanning */
int _forced; /* forced entry point */
int _strMem; /* start of unpacked memory */
int _retAdr; /* return address after unpacking */
int _depAdr; /* unpacker entry point */
int _endAdr; /* end of unpacked memory */
int _rtAFrc; /* flag, return address must be exactly RetAdr, else anything >= RetAdr */
int _wrMemF; /* flag, clean unwritten memory */
int _lfMemF; /* flag, clean end memory leftovers */
int _exoFnd; /* flag, Exomizer detected */
int _fStack; /* flag, fill stack with 0 and SP=$ff, else as in C64 */
int _ecaFlg; /* ECA found, holds relocated areas high bytes */
int _fEndBf; /* End memory address pointer before unpacking, set when DepAdr is reached */
int _fEndAf; /* End memory address pointer after unpacking, set when RetAdr is reached */
int _fStrBf; /* Start memory address pointer before unpacking, set when DepAdr is reached */
int _fStrAf; /* Start memory address pointer after unpacking, set when RetAdr is reached */
int _idOnly; /* flag, just identify packer and exit */
int _debugP; /* flag, verbosely emit various infos */
int _rtiFrc; /* flag, RTI instruction forces return from unpacker */
int _recurs; /* recursion counter */
unsigned int _monEnd; /* End memory address pointers monitored during execution, updated every time DepAdr is reached */
unsigned int _monStr; /* Start memory address pointers monitored during execution, updated every time DepAdr is reached */
unsigned int _mon1st; /* flag for forcingly assign monitored str/end ptr the 1st time */
unsigned int _endAdC; /* add fixed values and/or registers AXY to End memory address */
unsigned int _strAdC; /* add fixed values and/or registers AXY to Start memory address */
unsigned int _filler; /* Memory filler byte*/
unsigned char *_mem; /* pointer to the memory array */
char *_name; /* name of the prg file */
LoadInfo *_info; /* pointer to the loaded prg info struct */
CpuCtx *_r; /* pointer to the registers struct */
};
typedef void (*Scnptr)(UnpStr *);
#define EA_USE_A 0x01000000
#define EA_USE_X 0x00100000
#define EA_USE_Y 0x00010000
#define EA_ADDFF 0x10000000
#define ITERMAX 0x02000000
#define RECUMAX 16
void scanners(UnpStr *);
} // End of namespace Scott
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,35 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GLK_SCOTT_UNP64_INTERFACE_H
#define GLK_SCOTT_UNP64_INTERFACE_H
#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
int unp64(uint8_t *compressed, size_t length, uint8_t *destinationBuffer, size_t *finalLength, const char *settings);
} // End of namespace Scott
} // End of namespace Glk
#endif