imported AGI engine

svn-id: r22588
This commit is contained in:
Paweł Kołodziejski 2006-05-23 23:43:52 +00:00
parent 3a025038da
commit 107073537e
54 changed files with 15596 additions and 4 deletions

View File

@ -24,6 +24,10 @@ ScummVM Team
Gregory Montoir
Eugene Sandulenko
AGI:
Pawel Kolodziejski
Eugene Sandulenko
BASS:
Robert Goeffringmann
Oliver Kiehl
@ -193,6 +197,7 @@ Special thanks to
Jezar - For his freeverb filter implementation
Jim Leiterman - Various info on his FM-TOWNS/Marty SCUMM ports
lloyd - For deep tech details about C64 Zak & MM
Sarien Team - Original AGI engine code
Jimmi Thogersen - For ScummRev, and much obscure code/documentation
Tristan - For additional work on the original MT-32 emulator

View File

@ -368,6 +368,9 @@ void PluginManager::loadPlugins() {
#ifndef DISABLE_CINE
LINK_PLUGIN(CINE)
#endif
#ifndef DISABLE_CINE
LINK_PLUGIN(AGI)
#endif
#endif
}

338
dists/msvc8/agi.vcproj Normal file
View File

@ -0,0 +1,338 @@
<?xml version="1.0" encoding="windows-1250"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name="agi"
ProjectGUID="{F5F57066-CDF4-4F80-B9E7-7F4D21850D6E}"
RootNamespace="agi"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="agi_Debug"
IntermediateDirectory="agi_Debug"
ConfigurationType="4"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalOptions="/wd4201 /wd4512 /wd4511 /wd4100 /wd4121 /wd4310 /wd4706 /wd4127 /wd4189 /wd4702 /wd4996"
Optimization="0"
AdditionalIncludeDirectories="../..;../../engines"
PreprocessorDefinitions="WIN32;_DEBUG;USE_ZLIB;USE_MAD;USE_VORBIS"
MinimalRebuild="true"
ExceptionHandling="1"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
BufferSecurityCheck="true"
EnableFunctionLevelLinking="true"
ForceConformanceInForLoopScope="true"
UsePrecompiledHeader="0"
WarningLevel="4"
WarnAsError="false"
SuppressStartupBanner="false"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
OutputFile="$(OutDir)/agi.lib"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="agi_Release"
IntermediateDirectory="agi_Release"
ConfigurationType="4"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalOptions="/wd4201 /wd4512 /wd4511 /wd4100 /wd4121 /wd4310 /wd4706 /wd4127 /wd4189 /wd4702 /wd4996"
Optimization="3"
InlineFunctionExpansion="2"
OmitFramePointers="true"
AdditionalIncludeDirectories="../../;../../common"
PreprocessorDefinitions="WIN32;NDEBUG;USE_ZLIB;USE_MAD;USE_VORBIS"
StringPooling="true"
ExceptionHandling="1"
RuntimeLibrary="0"
BufferSecurityCheck="false"
EnableFunctionLevelLinking="false"
ForceConformanceInForLoopScope="true"
UsePrecompiledHeader="0"
WarningLevel="4"
WarnAsError="true"
DebugInformationFormat="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
OutputFile="$(OutDir)/agi.lib"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath="..\..\engines\agi\agi.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\agi.h"
>
</File>
<File
RelativePath="..\..\engines\agi\agi_v2.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\agi_v3.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\checks.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\console.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\console.h"
>
</File>
<File
RelativePath="..\..\engines\agi\cycle.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\font.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\global.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\graphics.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\graphics.h"
>
</File>
<File
RelativePath="..\..\engines\agi\id.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\inv.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\keyboard.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\keyboard.h"
>
</File>
<File
RelativePath="..\..\engines\agi\list.h"
>
</File>
<File
RelativePath="..\..\engines\agi\logic.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\logic.h"
>
</File>
<File
RelativePath="..\..\engines\agi\lzw.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\lzw.h"
>
</File>
<File
RelativePath="..\..\engines\agi\menu.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\menu.h"
>
</File>
<File
RelativePath="..\..\engines\agi\motion.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\objects.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\op_cmd.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\op_dbg.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\op_test.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\opcodes.h"
>
</File>
<File
RelativePath="..\..\engines\agi\patches.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\picture.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\picture.h"
>
</File>
<File
RelativePath="..\..\engines\agi\savegame.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\savegame.h"
>
</File>
<File
RelativePath="..\..\engines\agi\sound.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\sound.h"
>
</File>
<File
RelativePath="..\..\engines\agi\sprite.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\sprite.h"
>
</File>
<File
RelativePath="..\..\engines\agi\text.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\text.h"
>
</File>
<File
RelativePath="..\..\engines\agi\view.cpp"
>
</File>
<File
RelativePath="..\..\engines\agi\view.h"
>
</File>
<File
RelativePath="..\..\engines\agi\words.cpp"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -4,6 +4,7 @@
Version="8,00"
Name="gob"
ProjectGUID="{976D947A-A45F-4437-991E-412F695C64C7}"
RootNamespace="gob"
Keyword="Win32Proj"
>
<Platforms>
@ -246,6 +247,14 @@
RelativePath="..\..\engines\gob\goblin.h"
>
</File>
<File
RelativePath="..\..\engines\gob\goblin_v1.cpp"
>
</File>
<File
RelativePath="..\..\engines\gob\goblin_v2.cpp"
>
</File>
<File
RelativePath="..\..\engines\gob\init.cpp"
>
@ -286,6 +295,14 @@
RelativePath="..\..\engines\gob\map.h"
>
</File>
<File
RelativePath="..\..\engines\gob\map_v1.cpp"
>
</File>
<File
RelativePath="..\..\engines\gob\map_v2.cpp"
>
</File>
<File
RelativePath="..\..\engines\gob\mult.cpp"
>

View File

@ -4,6 +4,7 @@
Version="8,00"
Name="kyra"
ProjectGUID="{9D9A98A0-F88F-4CA2-B8FF-462470EBE3EC}"
RootNamespace="kyra"
Keyword="Win32Proj"
>
<Platforms>
@ -190,6 +191,10 @@
RelativePath="..\..\engines\kyra\kyra.h"
>
</File>
<File
RelativePath="..\..\engines\kyra\kyra3.cpp"
>
</File>
<File
RelativePath="..\..\engines\kyra\plugin.cpp"
>
@ -254,6 +259,10 @@
RelativePath="..\..\engines\kyra\sound_adlib.cpp"
>
</File>
<File
RelativePath="..\..\engines\kyra\sound_digital.cpp"
>
</File>
<File
RelativePath="..\..\engines\kyra\sprites.cpp"
>
@ -278,6 +287,14 @@
RelativePath="..\..\engines\kyra\timer.cpp"
>
</File>
<File
RelativePath="..\..\engines\kyra\vqa.cpp"
>
</File>
<File
RelativePath="..\..\engines\kyra\vqa.h"
>
</File>
<File
RelativePath="..\..\engines\kyra\wsamovie.cpp"
>

View File

@ -1,15 +1,16 @@
Microsoft Visual Studio Solution File, Format Version 9.00
# Visual C++ Express 2005
# Visual Studio 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scummvm", "scummvm.vcproj", "{8434CB15-D08F-427D-9E6D-581AE5B28440}"
ProjectSection(ProjectDependencies) = postProject
{6CC3E421-779D-4E80-8100-520886A0F9FF} = {6CC3E421-779D-4E80-8100-520886A0F9FF}
{1CA4AC50-5426-433A-8B5E-FFE39568098E} = {1CA4AC50-5426-433A-8B5E-FFE39568098E}
{F5F57066-CDF4-4F80-B9E7-7F4D21850D6E} = {F5F57066-CDF4-4F80-B9E7-7F4D21850D6E}
{676DB4C5-9A3E-4EE1-8483-EBB79DC0700E} = {676DB4C5-9A3E-4EE1-8483-EBB79DC0700E}
{9D9A98A0-F88F-4CA2-B8FF-462470EBE3EC} = {9D9A98A0-F88F-4CA2-B8FF-462470EBE3EC}
{976D947A-A45F-4437-991E-412F695C64C7} = {976D947A-A45F-4437-991E-412F695C64C7}
{E0EC9C72-A33E-49DA-B1DC-BB44B9799BFA} = {E0EC9C72-A33E-49DA-B1DC-BB44B9799BFA}
{6A55AF61-7CA1-49E0-9385-59C1FE9D4DB7} = {6A55AF61-7CA1-49E0-9385-59C1FE9D4DB7}
{B5527758-2F51-4CCD-AAE1-B0E28654BD6A} = {B5527758-2F51-4CCD-AAE1-B0E28654BD6A}
{1CA4AC50-5426-433A-8B5E-FFE39568098E} = {1CA4AC50-5426-433A-8B5E-FFE39568098E}
{B6AFD548-63D2-40CD-A652-E87095AFCBAF} = {B6AFD548-63D2-40CD-A652-E87095AFCBAF}
{C8AAE83E-198B-4ECA-A877-166827953979} = {C8AAE83E-198B-4ECA-A877-166827953979}
{1A1CA028-61B5-4A6C-A918-F5D8721AB1AC} = {1A1CA028-61B5-4A6C-A918-F5D8721AB1AC}
@ -37,6 +38,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lure", "lure.vcproj", "{1A1
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cine", "cine.vcproj", "{1CA4AC50-5426-433A-8B5E-FFE39568098E}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "agi", "agi.vcproj", "{F5F57066-CDF4-4F80-B9E7-7F4D21850D6E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@ -91,6 +94,10 @@ Global
{1CA4AC50-5426-433A-8B5E-FFE39568098E}.Debug|Win32.Build.0 = Debug|Win32
{1CA4AC50-5426-433A-8B5E-FFE39568098E}.Release|Win32.ActiveCfg = Release|Win32
{1CA4AC50-5426-433A-8B5E-FFE39568098E}.Release|Win32.Build.0 = Release|Win32
{F5F57066-CDF4-4F80-B9E7-7F4D21850D6E}.Debug|Win32.ActiveCfg = Debug|Win32
{F5F57066-CDF4-4F80-B9E7-7F4D21850D6E}.Debug|Win32.Build.0 = Debug|Win32
{F5F57066-CDF4-4F80-B9E7-7F4D21850D6E}.Release|Win32.ActiveCfg = Release|Win32
{F5F57066-CDF4-4F80-B9E7-7F4D21850D6E}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -4,6 +4,7 @@
Version="8,00"
Name="scummvm"
ProjectGUID="{8434CB15-D08F-427D-9E6D-581AE5B28440}"
RootNamespace="scummvm"
Keyword="Win32Proj"
>
<Platforms>
@ -67,7 +68,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib sdl.lib zdll.lib libmad.lib vorbisfile_static.lib vorbis_static.lib ogg_static.lib libmpeg2.lib sword1_debug/sword1.lib sword2_debug/sword2.lib lure_debug/lure.lib cine_debug/cine.lib kyra_debug/kyra.lib gob_debug/gob.lib queen_debug/queen.lib saga_debug/saga.lib scumm_debug/scumm.lib simon_debug/simon.lib sky_debug/sky.lib"
AdditionalDependencies="winmm.lib sdl.lib zlib.lib libmad.lib vorbisfile_static.lib vorbis_static.lib ogg_static.lib libmpeg2.lib sword1_debug/sword1.lib sword2_debug/sword2.lib lure_debug/lure.lib cine_debug/cine.lib kyra_debug/kyra.lib gob_debug/gob.lib queen_debug/queen.lib saga_debug/saga.lib agi_debug/agi.lib scumm_debug/scumm.lib simon_debug/simon.lib sky_debug/sky.lib"
OutputFile="$(OutDir)/scummvm.exe"
LinkIncremental="2"
IgnoreDefaultLibraryNames="libc.lib;libcmt.lib"
@ -155,7 +156,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib sdl.lib zdll.lib libmad.lib vorbisfile_static.lib vorbis_static.lib ogg_static.lib libmpeg2.lib saga_release/saga.lib sword1_release/sword1.lib sword2_release/sword2.lib lure_release/lure.lib cine_release/cine.lib kyra_release/kyra.lib gob_release/gob.lib queen_release/queen.lib scumm_release/scumm.lib simon_release/simon.lib sky_release/sky.lib"
AdditionalDependencies="winmm.lib sdl.lib zlib.lib libmad.lib vorbisfile_static.lib vorbis_static.lib ogg_static.lib libmpeg2.lib saga_release/saga.lib agi_release/agi.lib sword1_release/sword1.lib sword2_release/sword2.lib lure_release/lure.lib cine_release/cine.lib kyra_release/kyra.lib gob_release/gob.lib queen_release/queen.lib scumm_release/scumm.lib simon_release/simon.lib sky_release/sky.lib"
OutputFile="$(OutDir)/scummvm.exe"
LinkIncremental="1"
SuppressStartupBanner="true"
@ -997,6 +998,14 @@
RelativePath="..\..\graphics\imageman.h"
>
</File>
<File
RelativePath="..\..\graphics\paletteman.cpp"
>
</File>
<File
RelativePath="..\..\graphics\paletteman.h"
>
</File>
<File
RelativePath="..\..\graphics\primitives.cpp"
>

View File

@ -34,6 +34,13 @@
Eugene Sandulenko & \textit{}\\
\end{tabular}
\end{list}
\item \textbf{ AGI}
\begin{list}{}{\setlength{\leftmargin}{0.2cm}}
\item \begin{tabular}[h]{p{0.3\linewidth}p{0.6\linewidth}}
Pawe{\l} Ko{\l}odziejski & \textit{}\\
Eugene Sandulenko & \textit{}\\
\end{tabular}
\end{list}
\item \textbf{ BASS}
\begin{list}{}{\setlength{\leftmargin}{0.2cm}}
\item \begin{tabular}[h]{p{0.3\linewidth}p{0.6\linewidth}}
@ -293,6 +300,7 @@ And to all the contributors, users, and beta testers we've missed. Thanks!
Jezar & \textit{For his freeverb filter implementation}\\
Jim Leiterman & \textit{Various info on his FM-TOWNS/Marty SCUMM ports}\\
lloyd & \textit{For deep tech details about C64 Zak \& MM}\\
Sarien Team & \textit{Original AGI engine code}\\
Jimmi Th{\o}gersen & \textit{For ScummRev, and much obscure code/documentation}\\
Tristan & \textit{For additional work on the original MT-32 emulator}\\
\end{tabular}

627
engines/agi/agi.cpp Normal file
View File

@ -0,0 +1,627 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "common/file.h"
#include "common/savefile.h"
#include "common/config-manager.h"
#include "base/plugins.h"
#include "backends/fs/fs.h"
#include "sound/mididrv.h"
#include "sound/mixer.h"
#include "agi/agi.h"
#include "agi/text.h"
#include "agi/graphics.h"
#include "agi/sprite.h"
#include "agi/opcodes.h"
#include "agi/keyboard.h"
#include "agi/menu.h"
#include "agi/savegame.h"
namespace Agi {
void gfx_set_palette();
extern int optind;
struct agi_options opt;
struct game_id_list game_info;
struct agi_game game;
static struct agi_loader *loader; /* loader */
extern struct agi_loader agi_v2;
extern struct agi_loader agi_v3;
static volatile uint32 tick_timer = 0;
#define TICK_SECONDS 20
static int key_control = 0;
static int key_alt = 0;
#define KEY_QUEUE_SIZE 16
static int key_queue[KEY_QUEUE_SIZE];
static int key_queue_start = 0;
static int key_queue_end = 0;
#define key_enqueue(k) do { key_queue[key_queue_end++] = (k); \
key_queue_end %= KEY_QUEUE_SIZE; } while (0)
#define key_dequeue(k) do { (k) = key_queue[key_queue_start++]; \
key_queue_start %= KEY_QUEUE_SIZE; } while (0)
static void process_events() {
OSystem::Event event;
int key = 0;
while (g_system->pollEvent(event)) {
switch (event.type) {
case OSystem::EVENT_QUIT:
deinit_video();
deinit_machine();
g_system->quit();
break;
case OSystem::EVENT_LBUTTONDOWN:
key = BUTTON_LEFT;
mouse.button = 1;
key_enqueue(key);
mouse.x = event.mouse.x;
mouse.y = event.mouse.y;
break;
case OSystem::EVENT_RBUTTONDOWN:
key = BUTTON_RIGHT;
mouse.button = 2;
key_enqueue(key);
mouse.x = event.mouse.x;
mouse.y = event.mouse.y;
break;
case OSystem::EVENT_MOUSEMOVE:
mouse.x = event.mouse.x;
mouse.y = event.mouse.y;
break;
case OSystem::EVENT_LBUTTONUP:
case OSystem::EVENT_RBUTTONUP:
mouse.button = 0;
break;
case OSystem::EVENT_KEYDOWN:
if (event.kbd.flags == OSystem::KBD_CTRL) {
key_control |= 1;
key = 0;
break;
} else if (event.kbd.flags == OSystem::KBD_ALT) {
key_alt |= 1;
key = 0;
break;
} else if (event.kbd.flags == OSystem::KBD_SHIFT) {
key = 0;
break;
}
switch (key = event.kbd.keycode) {
case 256 + 20: // left arrow
key = KEY_LEFT;
break;
case 256 + 19: // right arrow
key = KEY_RIGHT;
break;
case 256 + 17: // up arrow
key = KEY_UP;
break;
case 256 + 18: // down arrow
key = KEY_DOWN;
break;
case 256 + 24: // page up
key = KEY_UP_RIGHT;
break;
case 256 + 25: // page down
key = KEY_DOWN_RIGHT;
break;
case 256 + 22: // home
key = KEY_UP_LEFT;
break;
case 256 + 23: // end
key = KEY_DOWN_LEFT;
break;
case '+':
key = '+';
break;
case '-':
key = '-';
break;
case 9:
key = 0x0009;
break;
case 282:
key = 0x3b00;
break;
case 283:
key = 0x3c00;
break;
case 284:
key = 0x3d00;
break;
case 285:
key = 0x3e00;
break;
case 286:
key = 0x3f00;
break;
case 287:
key = 0x4000;
break;
case 288:
key = 0x4100;
break;
case 289:
key = 0x4200;
break;
case 290:
key = 0x4300;
break;
case 291:
key = 0x4400;
break;
case 292:
key = KEY_STATUSLN;
break;
case 293:
key = KEY_PRIORITY;
break;
case 27:
key = 0x1b;
break;
case '\n':
case '\r':
key = KEY_ENTER;
break;
default:
if (!isalpha(key))
break;
if (key_control)
key = (key & ~0x20) - 0x40;
else if (key_alt)
key = scancode_table[(key & ~0x20) - 0x41] << 8;
break;
}
if (key)
key_enqueue(key);
break;
case OSystem::EVENT_KEYUP:
if (event.kbd.flags == OSystem::KBD_CTRL) {
key_control &= ~1;
key = 0;
break;
} else if (event.kbd.flags == OSystem::KBD_ALT) {
key_alt &= ~1;
key = 0;
break;
} else if (event.kbd.flags == OSystem::KBD_SHIFT) {
key = 0;
break;
}
break;
default:
break;
}
}
}
int agi_is_keypress_low() {
process_events();
return key_queue_start != key_queue_end;
}
void agi_timer_low() {
static uint32 m = 0;
uint32 dm;
if (tick_timer < m)
m = 0;
while ((dm = tick_timer - m) < 5) {
process_events();
g_system->delayMillis(10);
g_system->updateScreen();
}
m = tick_timer;
}
int agi_get_keypress_low() {
int k;
while (key_queue_start == key_queue_end) /* block */
agi_timer_low();
key_dequeue(k);
return k;
}
static uint32 agi_timer_function_low(uint32 i) {
tick_timer++;
return i;
}
static void init_pri_table() {
int i, p, y = 0;
for (p = 1; p < 15; p++) {
for (i = 0; i < 12; i++) {
game.pri_table[y++] = p < 4 ? 4 : p;
}
}
}
int agi_init() {
int ec, i;
debug(2, "initializing");
debug(2, "game.ver = 0x%x", game.ver);
/* reset all flags to false and all variables to 0 */
for (i = 0; i < MAX_FLAGS; i++)
game.flags[i] = 0;
for (i = 0; i < MAX_VARS; i++)
game.vars[i] = 0;
/* clear all resources and events */
for (i = 0; i < MAX_DIRS; i++) {
memset(&game.views[i], 0, sizeof(struct agi_view));
memset(&game.pictures[i], 0, sizeof(struct agi_picture));
memset(&game.logics[i], 0, sizeof(struct agi_logic));
memset(&game.sounds[i], 0, sizeof(struct agi_sound));
}
/* clear view table */
for (i = 0; i < MAX_VIEWTABLE; i++)
memset(&game.view_table[i], 0, sizeof(struct vt_entry));
init_words();
menu_init();
init_pri_table();
/* clear string buffer */
for (i = 0; i < MAX_STRINGS; i++)
game.strings[i][0] = 0;
/* setup emulation */
switch (loader->int_version >> 12) {
case 2:
report("Emulating Sierra AGI v%x.%03x\n",
(int)(loader->int_version >> 12) & 0xF,
(int)(loader->int_version) & 0xFFF);
break;
case 3:
report("Emulating Sierra AGI v%x.002.%03x\n",
(int)(loader->int_version >> 12) & 0xF,
(int)(loader->int_version) & 0xFFF);
break;
}
game.game_flags |= opt.amiga ? ID_AMIGA : 0;
game.game_flags |= opt.agds ? ID_AGDS : 0;
if (game.game_flags & ID_AMIGA)
report("Amiga padded game detected.\n");
if (game.game_flags & ID_AGDS)
report("AGDS mode enabled.\n");
ec = loader->init(); /* load vol files, etc */
if (ec == err_OK)
ec = loader->load_objects(OBJECTS);
/* note: demogs has no words.tok */
if (ec == err_OK)
ec = loader->load_words(WORDS);
/* FIXME: load IIgs instruments and samples */
/* load_instruments("kq.sys16"); */
/* Load logic 0 into memory */
if (ec == err_OK)
ec = loader->load_resource(rLOGIC, 0);
return ec;
}
/*
* Public functions
*/
void agi_unload_resources() {
int i;
/* Make sure logic 0 is always loaded */
for (i = 1; i < MAX_DIRS; i++) {
loader->unload_resource(rLOGIC, i);
}
for (i = 0; i < MAX_DIRS; i++) {
loader->unload_resource(rVIEW, i);
loader->unload_resource(rPICTURE, i);
loader->unload_resource(rSOUND, i);
}
}
int agi_deinit() {
int ec;
clean_input(); /* remove all words from memory */
agi_unload_resources(); /* unload resources in memory */
loader->unload_resource(rLOGIC, 0);
ec = loader->deinit();
unload_objects();
unload_words();
clear_image_stack();
return ec;
}
int agi_detect_game() {
int ec = err_OK;
loader = &agi_v2;
ec = loader->detect_game();
if (ec != err_OK) {
loader = &agi_v3;
ec = loader->detect_game();
}
return ec;
}
int agi_version() {
return loader->version;
}
int agi_get_release() {
return loader->int_version;
}
void agi_set_release(int n) {
loader->int_version = n;
}
int agi_load_resource(int r, int n) {
int i;
i = loader->load_resource(r, n);
#ifdef PATCH_LOGIC
if (r == rLOGIC)
patch_logic(n);
#endif
return i;
}
int agi_unload_resource(int r, int n) {
return loader->unload_resource(r, n);
}
const char *_savePath;
extern AGIMusic *g_agi_music;
struct GameSettings {
const char *gameid;
const char *description;
byte id;
uint32 features;
const char *detectname;
};
static const GameSettings agi_settings[] = {
{"agi", "AGI game", GID_AGI, MDT_ADLIB, "VIEWDIR"},
{NULL, NULL, 0, 0, NULL}
};
Common::RandomSource * rnd;
AgiEngine::AgiEngine(OSystem * syst) : Engine(syst) {
// Setup mixer
if (!_mixer->isReady()) {
warning("Sound initialization failed.");
}
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
_savePath = _saveFileMan->getSavePath();
const GameSettings *g;
const char *gameid = ConfMan.get("gameid").c_str();
for (g = agi_settings; g->gameid; ++g)
if (!scumm_stricmp(g->gameid, gameid))
_gameId = g->id;
rnd = new Common::RandomSource();
game.clock_enabled = false;
game.state = STATE_INIT;
}
void AgiEngine::initialize() {
memset(&opt, 0, sizeof(struct agi_options));
opt.gamerun = GAMERUN_RUNGAME;
#ifdef USE_HIRES
opt.hires = true;
#endif
opt.soundemu = SOUND_EMU_NONE;
init_machine();
game.color_fg = 15;
game.color_bg = 0;
*game.name = 0;
game.sbuf = (uint8 *) calloc(_WIDTH, _HEIGHT);
#ifdef USE_HIRES
game.hires = (uint8 *) calloc(_WIDTH * 2, _HEIGHT);
#endif
init_sprites();
init_video();
tick_timer = 0;
Common::g_timer->installTimerProc((Common::Timer::TimerProc) agi_timer_function_low, 10 * 1000, NULL);
console_init();
game.ver = -1; /* Don't display the conf file warning */
debugC(2, kDebugLevelMain, "Detect game");
if (agi_detect_game() == err_OK) {
game.state = STATE_LOADED;
debugC(2, kDebugLevelMain, "game loaded");
} else {
report("Could not open AGI game");
}
debugC(2, kDebugLevelMain, "Init sound");
init_sound();
g_agi_music = new AGIMusic(_mixer);
}
AgiEngine::~AgiEngine() {
agi_deinit();
delete g_agi_music;
deinit_sound();
deinit_video();
deinit_sprites();
#ifdef USE_HIRES
free(game.hires);
#endif
free(game.sbuf);
deinit_machine();
delete rnd;
}
void AgiEngine::errorString(const char *buf1, char *buf2) {
strcpy(buf2, buf1);
}
int AgiEngine::init() {
// Initialize backend
_system->beginGFXTransaction();
initCommonGFX(false);
_system->initSize(320, 200);
_system->endGFXTransaction();
Common::addSpecialDebugLevel(kDebugLevelMain, "Main", "Generic debug level");
Common::addSpecialDebugLevel(kDebugLevelResources, "Resources", "Resources debugging");
Common::addSpecialDebugLevel(kDebugLevelSprites, "Sprites", "Sprites debugging");
Common::addSpecialDebugLevel(kDebugLevelInventory, "Inventory", "Inventory debugging");
Common::addSpecialDebugLevel(kDebugLevelInput, "Input", "Input events debugging");
Common::addSpecialDebugLevel(kDebugLevelMenu, "Menu", "Menu debugging");
Common::addSpecialDebugLevel(kDebugLevelScripts, "Scrpits", "Scripts debugging");
Common::addSpecialDebugLevel(kDebugLevelSound, "Sound", "Sound debugging");
Common::addSpecialDebugLevel(kDebugLevelText, "Text", "Text output debugging");
initialize();
gfx_set_palette();
return 0;
}
int AgiEngine::go() {
_system->showMouse(true);
report(" \nAGI engine " VERSION " is ready.\n");
if (game.state < STATE_LOADED) {
console_prompt();
do {
main_cycle();
} while (game.state < STATE_RUNNING);
if (game.ver < 0)
game.ver = 0; /* Enable conf file warning */
}
run_game();
return 0;
}
} // End of namespace Agi
GameList Engine_AGI_gameIDList() {
GameList games;
const Agi::GameSettings *g = Agi::agi_settings;
while (g->gameid) {
games.push_back(*g);
g++;
}
return games;
}
GameDescriptor Engine_AGI_findGameID(const char *gameid) {
const Agi::GameSettings *g = Agi::agi_settings;
while (g->gameid) {
if (0 == scumm_stricmp(gameid, g->gameid))
break;
g++;
}
return *g;
}
DetectedGameList Engine_AGI_detectGames(const FSList &fslist) {
DetectedGameList detectedGames;
const Agi::GameSettings * g;
for (g = Agi::agi_settings; g->gameid; ++g) {
// Iterate over all files in the given directory
for (FSList::const_iterator file = fslist.begin();
file != fslist.end(); ++file) {
const char *gameName = file->displayName().c_str();
if (0 == scumm_stricmp(g->detectname, gameName)) {
// Match found, add to list of candidates, then abort inner loop.
detectedGames.push_back(*g);
break;
}
}
}
return detectedGames;
}
PluginError Engine_AGI_create(OSystem *syst, Engine **engine) {
assert(engine);
*engine = new Agi::AgiEngine(syst);
return kNoError;
}
REGISTER_PLUGIN(AGI, "AGI Engine");

545
engines/agi/agi.h Normal file
View File

@ -0,0 +1,545 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_H
#define __AGI_H
#include "common/stdafx.h"
#include "common/scummsys.h"
#include "common/endian.h"
#include "common/util.h"
#include "common/file.h"
#include "common/savefile.h"
#include "common/system.h"
#include "base/engine.h"
namespace Agi {
typedef signed int Err;
/*
* Version and other definitions
*/
#define INLINE __inline
#define VERSION __DATE__ " " __TIME__
/* Default features -- can be overriden in portdefs.h */
#define USE_CONSOLE
#define USE_PCM_SOUND
#define USE_IIGS_SOUND
#define USE_HIRES
#define USE_MOUSE
#define AGDS_SUPPORT
#define TITLE "AGI engine"
#define DIR_ "dir"
#define LOGDIR "logdir"
#define PICDIR "picdir"
#define VIEWDIR "viewdir"
#define SNDDIR "snddir"
#define OBJECTS "object"
#define WORDS "words.tok"
#define MAX_DIRS 256
#define MAX_VARS 256
#define MAX_FLAGS (256 >> 3)
#define MAX_VIEWTABLE 255 /* KQ3 uses o255! */
#define MAX_WORDS 20
#define MAX_STRINGS 24 /* MAX_STRINGS + 1 used for get.num */
#define MAX_STRINGLEN 40
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
#define _EMPTY 0xfffff
#define EGO_OWNED 0xff
#define CRYPT_KEY_SIERRA "Avis Durgan"
#define CRYPT_KEY_AGDS "Alex Simkin"
#ifndef INLINE
#define INLINE
#endif
#ifndef USE_PCM_SOUND
#undef USE_IIGS_SOUND
#endif
#define MSG_BOX_COLOUR 0x0f /* White */
#define MSG_BOX_TEXT 0x00 /* Black */
#define MSG_BOX_LINE 0x04 /* Red */
#define STATUS_FG 0x00 /* Black */
#define STATUS_BG 0x0f /* White */
#define PATCH_LOGIC /* disable copy protection on some games */
} // End of namespace Agi
/* AGI resources */
#include "agi/console.h"
#include "agi/view.h"
#include "agi/picture.h"
#include "agi/logic.h"
#include "agi/sound.h"
namespace Agi {
int getflag(int);
void setflag(int, int);
void flipflag(int);
int getvar(int);
void setvar(int, int);
void decrypt(uint8 * mem, int len);
void release_sprites(void);
int main_cycle(void);
int view_pictures(void);
int parse_cli(int, char **);
int run_game(void);
int init_machine(void);
int deinit_machine(void);
int get_direction(int x, int y, int x0, int y0, int s);
void inventory(void);
void list_games(void);
uint32 match_crc(uint32, char *, int);
int v2id_game(void);
int v3id_game(void);
int v4id_game(uint32 ver);
void update_timer(void);
int get_app_dir(char *app_dir, unsigned int size);
enum {
NO_GAMEDIR = 0,
GAMEDIR
};
enum AGIErrors {
err_OK = 0,
err_DoNothing,
err_BadCLISwitch,
err_InvalidAGIFile,
err_BadFileOpen,
err_NotEnoughMemory,
err_BadResource,
err_UnknownAGIVersion,
err_RestartGame,
err_NoLoopsInView,
err_ViewDataError,
err_NoGameList,
err_Unk = 127
};
enum kDebugLevels {
kDebugLevelMain = 1 << 0,
kDebugLevelResources = 1 << 1,
kDebugLevelSprites = 1 << 2,
kDebugLevelInventory = 1 << 3,
kDebugLevelInput = 1 << 4,
kDebugLevelMenu = 1 << 5,
kDebugLevelScripts = 1 << 6,
kDebugLevelSound = 1 << 7,
kDebugLevelText = 1 << 8
};
/**
* AGI resources.
*/
enum {
rLOGIC = 1,
rSOUND,
rVIEW,
rPICTURE
};
enum {
RES_LOADED = 1,
RES_COMPRESSED = 0x40
};
enum {
lCOMMAND_MODE = 1,
lTEST_MODE
};
struct game_id_list {
struct game_id_list *next;
uint32 version;
uint32 crc;
char *gName;
char *switches;
};
#ifdef USE_MOUSE
struct mouse {
int button;
unsigned int x;
unsigned int y;
};
#endif
/**
* Command-line options.
*/
struct agi_options {
#define GAMERUN_RUNGAME 0
#define GAMERUN_PICVIEW 1
#define GAMERUN_WORDS 2
#define GAMERUN_OBJECTS 3
#define GAMERUN_GAMES 4
#define GAMERUN_CRC 5
int gamerun; /**< game run mode*/
int emuversion; /**< AGI version to emulate */
int agds; /**< enable AGDS mode */
int amiga; /**< enable Amiga mode */
int fullscreen; /**< use full screen mode if available */
int nosound; /**< disable sound */
int egapal; /**< use PC EGA palette */
int cgaemu; /**< use PC CGA emulation */
#ifdef USE_HIRES
int hires; /**< use hi-res pictures */
#endif
int soundemu; /**< sound emulation mode */
#ifdef USE_MOUSE
int agimouse; /**< AGI Mouse 1.0 emulation */
#endif
};
extern struct agi_options opt;
extern uint8 *exec_name;
extern volatile uint32 clock_ticks;
extern volatile uint32 clock_count;
extern volatile uint32 msg_box_secs2;
#ifdef USE_CONSOLE
extern struct agi_debug debug_;
#endif
#ifdef USE_MOUSE
extern struct mouse mouse;
#endif
int console_keyhandler(int);
int console_init(void);
void console_cycle(void);
void console_lock(void);
void console_prompt(void);
#define report printf
enum GameId {
GID_AGI = 1
};
extern Common::RandomSource * rnd;
extern const char *_savePath;
class AgiEngine:public::Engine {
int _gameId;
void errorString(const char *buf_input, char *buf_output);
protected:
int init();
int go();
void shutdown();
void initialize();
public:
AgiEngine(OSystem * syst);
virtual ~ AgiEngine();
int getGameId() {
return _gameId;
}};
#define WIN_TO_PIC_X(x) ((x) / 2)
#define WIN_TO_PIC_Y(y) ((y) < 8 ? 999 : (y) >= (8 + _HEIGHT) ? 999 : (y) - 8)
/**
* AGI variables.
*/
enum {
V_cur_room = 0, /* 0 */
V_prev_room,
V_border_touch_ego,
V_score,
V_border_code,
V_border_touch_obj, /* 5 */
V_ego_dir,
V_max_score,
V_free_pages,
V_word_not_found,
V_time_delay, /* 10 */
V_seconds,
V_minutes,
V_hours,
V_days,
V_joystick_sensitivity, /* 15 */
V_ego_view_resource,
V_agi_err_code,
V_agi_err_code_info,
V_key,
V_computer, /* 20 */
V_window_reset,
V_soundgen,
V_volume,
V_max_input_chars,
V_sel_item, /* 25 */
V_monitor
};
/**
* AGI flags
*/
enum {
F_ego_water = 0, /* 0 */
F_ego_invisible,
F_entered_cli,
F_ego_touched_p2,
F_said_accepted_input,
F_new_room_exec, /* 5 */
F_restart_game,
F_script_blocked,
F_joy_sensitivity,
F_sound_on,
F_debugger_on, /* 10 */
F_logic_zero_firsttime,
F_restore_just_ran,
F_status_selects_items,
F_menus_work,
F_output_mode, /* 15 */
F_auto_restart
};
struct agi_event {
uint16 data;
uint8 occured;
};
struct agi_object {
int location;
char *name;
};
struct agi_word {
int id;
char *word;
};
struct agi_dir {
uint8 volume;
uint32 offset;
uint32 len;
uint32 clen;
uint8 flags;
/* 0 = not in mem, can be freed
* 1 = in mem, can be released
* 2 = not in mem, cant be released
* 3 = in mem, cant be released
* 0x40 = was compressed
*/
};
struct agi_block {
int active;
int x1, y1;
int x2, y2;
uint8 *buffer; /* used for window background */
};
#define EGO_VIEW_TABLE 0
#define HORIZON 36
#define _WIDTH 160
#define _HEIGHT 168
/**
* AGI game structure.
* This structure contains all global data of an AGI game executed
* by the interpreter.
*/
struct agi_game {
#define STATE_INIT 0x00
#define STATE_LOADED 0x01
#define STATE_RUNNING 0x02
int state; /**< state of the interpreter */
char name[8]; /**< lead in id (e.g. `GR' for goldrush) */
char id[8]; /**< game id */
uint32 crc; /**< game CRC */
/* game flags and variables */
uint8 flags[MAX_FLAGS];
/**< 256 1-bit flags */
uint8 vars[MAX_VARS];
/**< 256 variables */
/* internal variables */
int horizon; /**< horizon y coordinate */
int line_status; /**< line number to put status on */
int line_user_input;
/**< line to put user input on */
int line_min_print;
/**< num lines to print on */
int cursor_pos; /**< column where the input cursor is */
uint8 input_buffer[40];
/**< buffer for user input */
uint8 echo_buffer[40];
/**< buffer for echo.line */
int keypress;
#define INPUT_NORMAL 0x01
#define INPUT_GETSTRING 0x02
#define INPUT_MENU 0x03
#define INPUT_NONE 0x04
int input_mode; /**< keyboard input mode */
int input_enabled;
/**< keyboard input enabled */
int lognum; /**< current logic number */
/* internal flags */
int player_control;
/**< player is in control */
int quit_prog_now;
/**< quit now */
int status_line; /**< status line on/off */
int clock_enabled;
/**< clock is on/off */
int exit_all_logics;
/**< break cycle after new.room */
int picture_shown;
/**< show.pic has been issued */
int has_prompt; /**< input prompt has been printed */
#define ID_AGDS 0x00000001
#define ID_AMIGA 0x00000002
int game_flags; /**< agi options flags */
uint8 pri_table[_HEIGHT];
/**< priority table */
/* windows */
uint32 msg_box_ticks;
/**< timed message box tick counter */
struct agi_block block;
struct agi_block window;
int has_window;
/* graphics & text */
int gfx_mode;
char cursor_char;
unsigned int color_fg;
unsigned int color_bg;
uint8 *sbuf; /**< 160x168 AGI screen buffer */
#ifdef USE_HIRES
uint8 *hires; /**< 320x168 hi-res buffer */
#endif
/* player command line */
struct agi_word ego_words[MAX_WORDS];
int num_ego_words;
unsigned int num_objects;
struct agi_event ev_keyp[MAX_DIRS];
/**< keyboard keypress events */
char strings[MAX_STRINGS + 1][MAX_STRINGLEN];
/**< strings */
/* directory entries for resources */
struct agi_dir dir_logic[MAX_DIRS];
struct agi_dir dir_pic[MAX_DIRS];
struct agi_dir dir_view[MAX_DIRS];
struct agi_dir dir_sound[MAX_DIRS];
/* resources */
struct agi_picture pictures[MAX_DIRS];
/**< AGI picture resources */
struct agi_logic logics[MAX_DIRS];
/**< AGI logic resources */
struct agi_view views[MAX_DIRS]; /**< AGI view resources */
struct agi_sound sounds[MAX_DIRS];
/**< AGI sound resources */
/* view table */
struct vt_entry view_table[MAX_VIEWTABLE];
int32 ver; /**< detected game version */
int simple_save; /**< select simple savegames */
};
/**
*
*/
struct agi_loader {
int version;
int int_version;
int (*init) (void);
int (*deinit) (void);
int (*detect_game) ();
int (*load_resource) (int, int);
int (*unload_resource) (int, int);
int (*load_objects) (char *);
int (*load_words) (char *);
};
extern struct agi_game game;
int agi_init(void);
int agi_deinit(void);
int agi_version(void);
int agi_get_release(void);
void agi_set_release(int);
int agi_detect_game();
int agi_load_resource(int, int);
int agi_unload_resource(int, int);
void agi_unload_resources(void);
/* words */
int show_words(void);
int load_words(char *);
void unload_words(void);
int find_word(char *word, int *flen);
void dictionary_words(char *);
/* objects */
int show_objects(void);
int load_objects(char *fname);
int alloc_objects(int);
void unload_objects(void);
char *object_name(unsigned int);
int object_get_location(unsigned int);
void object_set_location(unsigned int, int);
void new_input_mode(int);
void old_input_mode(void);
int run_logic(int);
void agi_timer_low();
int agi_get_keypress_low();
int agi_is_keypress_low();
} // End of namespace Agi
#endif /* __AGI_H */

309
engines/agi/agi_v2.cpp Normal file
View File

@ -0,0 +1,309 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "common/file.h"
#include "agi/agi.h"
namespace Agi {
static int agi_v2_init(void);
static int agi_v2_deinit(void);
static int agi_v2_detect_game();
static int agi_v2_load_resource(int, int);
static int agi_v2_unload_resource(int, int);
static int agi_v2_load_objects(char *);
static int agi_v2_load_words(char *);
struct agi_loader agi_v2 = {
2,
0,
agi_v2_init,
agi_v2_deinit,
agi_v2_detect_game,
agi_v2_load_resource,
agi_v2_unload_resource,
agi_v2_load_objects,
agi_v2_load_words
};
static int agi_v2_detect_game() {
if (!Common::File::exists(LOGDIR) ||
!Common::File::exists(PICDIR) ||
!Common::File::exists(SNDDIR) ||
!Common::File::exists(VIEWDIR))
return err_InvalidAGIFile;
agi_v2.int_version = 0x2917; /* setup for 2.917 */
return v2id_game();
}
static int agi_v2_load_dir(struct agi_dir *agid, char *fname) {
Common::File fp;
uint8 *mem;
uint32 flen;
unsigned int i;
char *path;
path = fname;
report("Loading directory: %s\n", path);
if ((!fp.open(path))) {
return err_BadFileOpen;
}
fp.seek(0, SEEK_END);
flen = fp.pos();
fp.seek(0, SEEK_SET);
if ((mem = (uint8 *) malloc(flen + 32)) == NULL) {
fp.close();
return err_NotEnoughMemory;
}
fp.read(mem, flen);
/* set all directory resources to gone */
for (i = 0; i < MAX_DIRS; i++) {
agid[i].volume = 0xff;
agid[i].offset = _EMPTY;
}
/* build directory entries */
for (i = 0; i < flen; i += 3) {
agid[i / 3].volume = *(mem + i) >> 4;
agid[i / 3].offset = READ_BE_UINT24(mem + i) & (uint32) _EMPTY;
debugC(3, kDebugLevelResources, "%d: volume %d, offset 0x%05x", i / 3, agid[i / 3].volume, agid[i / 3].offset);
}
free(mem);
fp.close();
return err_OK;
}
static int agi_v2_init() {
int ec = err_OK;
/* load directory files */
ec = agi_v2_load_dir(game.dir_logic, LOGDIR);
if (ec == err_OK)
ec = agi_v2_load_dir(game.dir_pic, PICDIR);
if (ec == err_OK)
ec = agi_v2_load_dir(game.dir_view, VIEWDIR);
if (ec == err_OK)
ec = agi_v2_load_dir(game.dir_sound, SNDDIR);
return ec;
}
static int agi_v2_deinit() {
int ec = err_OK;
#if 0
/* unload words */
agi_v2_unload_words();
/* unload objects */
agi_v2_unload_objects();
#endif
return ec;
}
static int agi_v2_unload_resource(int t, int n) {
debugC(3, kDebugLevelResources, "unload resource");
switch (t) {
case rLOGIC:
unload_logic(n);
break;
case rPICTURE:
unload_picture(n);
break;
case rVIEW:
unload_view(n);
break;
case rSOUND:
unload_sound(n);
break;
}
return err_OK;
}
/*
* This function does noting but load a raw resource into memory,
* if further decoding is required, it must be done by another
* routine. NULL is returned if unsucsessfull.
*/
static uint8 *agi_v2_load_vol_res(struct agi_dir *agid) {
uint8 *data = NULL;
char x[MAX_PATH], *path;
Common::File fp;
unsigned int sig;
sprintf(x, "vol.%i", agid->volume);
path = x;
debugC(3, kDebugLevelResources, "Vol res: path = %s", path);
if (agid->offset != _EMPTY && fp.open(path)) {
debugC(3, kDebugLevelResources, "loading resource at offset %d", agid->offset);
fp.seek(agid->offset, SEEK_SET);
fp.read(&x, 5);
if ((sig = READ_BE_UINT16((uint8 *) x)) == 0x1234) {
agid->len = READ_LE_UINT16((uint8 *) x + 3);
data = (uint8 *) calloc(1, agid->len + 32);
if (data != NULL) {
fp.read(data, agid->len);
} else {
abort();
}
} else {
#if 0
/* FIXME: call some panic handler instead of
* deiniting directly
*/
deinit_video_mode();
#endif
report("Error: bad signature %04x\n", sig);
// fprintf (stderr, "ACK! BAD RESOURCE!!!\n");
return 0;
}
fp.close();
} else {
/* we have a bad volume resource */
/* set that resource to NA */
agid->offset = _EMPTY;
}
return data;
}
/*
* Loads a resource into memory, a raw resource is loaded in
* with above routine, then further decoded here.
*/
int agi_v2_load_resource(int t, int n) {
int ec = err_OK;
uint8 *data = NULL;
debugC(3, kDebugLevelResources, "(t = %d, n = %d)", t, n);
if (n > MAX_DIRS)
return err_BadResource;
switch (t) {
case rLOGIC:
if (~game.dir_logic[n].flags & RES_LOADED) {
debugC(3, kDebugLevelResources, "loading logic resource %d", n);
agi_v2.unload_resource(rLOGIC, n);
/* load raw resource into data */
data = agi_v2_load_vol_res(&game.dir_logic[n]);
game.logics[n].data = data;
ec = data ? decode_logic(n) : err_BadResource;
game.logics[n].sIP = 2;
}
/* if logic was cached, we get here */
/* reset code pointers incase it was cached */
game.logics[n].cIP = game.logics[n].sIP;
break;
case rPICTURE:
/* if picture is currently NOT loaded *OR* cacheing is off,
* unload the resource (caching == off) and reload it
*/
debugC(3, kDebugLevelResources, "loading picture resource %d", n);
if (game.dir_pic[n].flags & RES_LOADED)
break;
/* if loaded but not cached, unload it */
/* if cached but not loaded, etc */
agi_v2.unload_resource(rPICTURE, n);
data = agi_v2_load_vol_res(&game.dir_pic[n]);
if (data != NULL) {
game.pictures[n].rdata = data;
game.dir_pic[n].flags |= RES_LOADED;
} else {
ec = err_BadResource;
}
break;
case rSOUND:
debugC(3, kDebugLevelResources, "loading sound resource %d", n);
if (game.dir_sound[n].flags & RES_LOADED)
break;
data = agi_v2_load_vol_res(&game.dir_sound[n]);
if (data != NULL) {
game.sounds[n].rdata = data;
game.dir_sound[n].flags |= RES_LOADED;
decode_sound(n);
} else {
ec = err_BadResource;
}
break;
case rVIEW:
/* Load a VIEW resource into memory...
* Since VIEWS alter the view table ALL the time
* can we cache the view? or must we reload it all
* the time?
*/
if (game.dir_view[n].flags & RES_LOADED)
break;
debugC(3, kDebugLevelResources, "loading view resource %d", n);
agi_v2.unload_resource(rVIEW, n);
data = agi_v2_load_vol_res(&game.dir_view[n]);
if (data) {
game.views[n].rdata = data;
game.dir_view[n].flags |= RES_LOADED;
ec = decode_view(n);
} else {
ec = err_BadResource;
}
break;
default:
ec = err_BadResource;
break;
}
return ec;
}
static int agi_v2_load_objects(char *fname) {
return load_objects(fname);
}
static int agi_v2_load_words(char *fname) {
return load_words(fname);
}
} // End of namespace Agi

389
engines/agi/agi_v3.cpp Normal file
View File

@ -0,0 +1,389 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/lzw.h"
#include "backends/fs/fs.h"
#include "common/config-manager.h"
namespace Agi {
static int agi_v3_init(void);
static int agi_v3_deinit(void);
static int agi_v3_detect_game();
static int agi_v3_load_resource(int, int);
static int agi_v3_unload_resource(int, int);
static int agi_v3_load_objects(char *);
static int agi_v3_load_words(char *);
struct agi_loader agi_v3 = {
3,
0,
agi_v3_init,
agi_v3_deinit,
agi_v3_detect_game,
agi_v3_load_resource,
agi_v3_unload_resource,
agi_v3_load_objects,
agi_v3_load_words
};
int agi_v3_detect_game() {
int ec = err_Unk;
bool found = false;
FSList fslist;
FilesystemNode dir(ConfMan.get("path"));
if (!dir.listDir(fslist, FilesystemNode::kListFilesOnly)) {
warning("AgiEngine: invalid game path '%s'",
dir.path().c_str());
return err_InvalidAGIFile;
}
for (FSList::const_iterator file = fslist.begin();
file != fslist.end() && !found; ++file) {
Common::String f = file->displayName();
f.toLowercase();
if (f.hasSuffix("vol.0")) {
strncpy(game.name, f.c_str(), f.size() > 5 ? f.size() - 5 : f.size());
debugC(3, kDebugLevelMain, "game.name = %s", game.name);
agi_v3.int_version = 0x3149; // setup for 3.002.149
ec = v3id_game();
found = true;
}
}
if (!found) {
debugC(3, kDebugLevelMain, "not found");
ec = err_InvalidAGIFile;
}
return ec;
}
static int agi_v3_load_dir(struct agi_dir *agid, Common::File *fp,
uint32 offs, uint32 len) {
int ec = err_OK;
uint8 *mem;
unsigned int i;
fp->seek(offs, SEEK_SET);
if ((mem = (uint8 *) malloc(len + 32)) != NULL) {
fp->read(mem, len);
/* set all directory resources to gone */
for (i = 0; i < MAX_DIRS; i++) {
agid[i].volume = 0xff;
agid[i].offset = _EMPTY;
}
/* build directory entries */
for (i = 0; i < len; i += 3) {
agid[i / 3].volume = *(mem + i) >> 4;
agid[i / 3].offset = READ_BE_UINT24(mem + i) & (uint32) _EMPTY;
}
free(mem);
} else {
ec = err_NotEnoughMemory;
}
return ec;
}
struct agi3vol {
uint32 sddr;
uint32 len;
};
int agi_v3_init(void) {
int ec = err_OK;
struct agi3vol agi_vol3[4];
int i;
uint16 xd[4];
Common::File fp;
Common::String path;
path = Common::String(game.name) + DIR_;
if (!fp.open(path)) {
printf("Failed to open \"%s\"\n", path.c_str());
return err_BadFileOpen;
}
/* build offset table for v3 directory format */
fp.read(&xd, 8);
fp.seek(0, SEEK_END);
for (i = 0; i < 4; i++)
agi_vol3[i].sddr = READ_LE_UINT16((uint8 *) & xd[i]);
agi_vol3[0].len = agi_vol3[1].sddr - agi_vol3[0].sddr;
agi_vol3[1].len = agi_vol3[2].sddr - agi_vol3[1].sddr;
agi_vol3[2].len = agi_vol3[3].sddr - agi_vol3[2].sddr;
agi_vol3[3].len = fp.pos() - agi_vol3[3].sddr;
if (agi_vol3[3].len > 256 * 3)
agi_vol3[3].len = 256 * 3;
fp.seek(0, SEEK_SET);
/* read in directory files */
ec = agi_v3_load_dir(game.dir_logic, &fp, agi_vol3[0].sddr,
agi_vol3[0].len);
if (ec == err_OK) {
ec = agi_v3_load_dir(game.dir_pic, &fp, agi_vol3[1].sddr, agi_vol3[1].len);
}
if (ec == err_OK) {
ec = agi_v3_load_dir(game.dir_view, &fp, agi_vol3[2].sddr, agi_vol3[2].len);
}
if (ec == err_OK) {
ec = agi_v3_load_dir(game.dir_sound, &fp, agi_vol3[3].sddr, agi_vol3[3].len);
}
return ec;
}
int agi_v3_deinit() {
int ec = err_OK;
#if 0
/* unload words */
agi_v3_unload_words();
/* unload objects */
agi_v3_unload_objects();
#endif
return ec;
}
int agi_v3_unload_resource(int t, int n) {
switch (t) {
case rLOGIC:
unload_logic(n);
break;
case rPICTURE:
unload_picture(n);
break;
case rVIEW:
unload_view(n);
break;
case rSOUND:
unload_sound(n);
break;
}
return err_OK;
}
/*
* This function does noting but load a raw resource into memory.
* If further decoding is required, it must be done by another
* routine.
*
* NULL is returned if unsucsessful.
*/
uint8 *agi_v3_load_vol_res(struct agi_dir *agid) {
char x[MAX_PATH];
uint8 *data = NULL, *comp_buffer;
Common::File fp;
Common::String path;
debugC(3, kDebugLevelResources, "(%p)", agid);
sprintf(x, "vol.%i", agid->volume);
path = Common::String(game.name) + x;
if (agid->offset != _EMPTY && fp.open(path)) {
fp.seek(agid->offset, SEEK_SET);
fp.read(&x, 7);
if (READ_BE_UINT16((uint8 *) x) != 0x1234) {
#if 0
/* FIXME */
deinit_video_mode();
#endif
debugC(3, kDebugLevelResources, "path = %s", path.c_str());
debugC(3, kDebugLevelResources, "offset = %d", agid->offset);
debugC(3, kDebugLevelResources, "x = %x %x", x[0], x[1]);
error("ACK! BAD RESOURCE");
g_system->quit();
}
agid->len = READ_LE_UINT16((uint8 *) x + 3); /* uncompressed size */
agid->clen = READ_LE_UINT16((uint8 *) x + 5); /* compressed len */
comp_buffer = (uint8 *)calloc(1, agid->clen + 32);
fp.read(comp_buffer, agid->clen);
if (x[2] & 0x80 || agid->len == agid->clen) {
/* do not decompress */
data = comp_buffer;
#if 0
/* CM: added to avoid problems in
* convert_v2_v3_pic() when clen > len
* e.g. Sierra demo 4, first picture
* (Tue Mar 16 13:13:43 EST 1999)
*/
agid->len = agid->clen;
/* Now removed to fix Gold Rush! in demo4 */
#endif
} else {
/* it is compressed */
data = (uint8 *)calloc(1, agid->len + 32);
LZW_expand(comp_buffer, data, agid->len);
free(comp_buffer);
agid->flags |= RES_COMPRESSED;
}
fp.close();
} else {
/* we have a bad volume resource */
/* set that resource to NA */
agid->offset = _EMPTY;
}
return data;
}
/*
* Loads a resource into memory, a raw resource is loaded in
* with above routine, then further decoded here.
*/
int agi_v3_load_resource(int t, int n) {
int ec = err_OK;
uint8 *data = NULL;
if (n > MAX_DIRS)
return err_BadResource;
switch (t) {
case rLOGIC:
/* load resource into memory, decrypt messages at the end
* and build the message list (if logic is in memory)
*/
if (~game.dir_logic[n].flags & RES_LOADED) {
/* if logic is already in memory, unload it */
agi_v3.unload_resource(rLOGIC, n);
/* load raw resource into data */
data = agi_v3_load_vol_res(&game.dir_logic[n]);
game.logics[n].data = data;
/* uncompressed logic files need to be decrypted */
if (data != NULL) {
/* resloaded flag gets set by decode logic */
/* needed to build string table */
ec = decode_logic(n);
game.logics[n].sIP = 2;
} else {
ec = err_BadResource;
}
/*logics[n].sIP=2; *//* saved IP = 2 */
/*logics[n].cIP=2; *//* current IP = 2 */
game.logics[n].cIP = game.logics[n].sIP;
}
/* if logic was cached, we get here */
/* reset code pointers incase it was cached */
game.logics[n].cIP = game.logics[n].sIP;
break;
case rPICTURE:
/* if picture is currently NOT loaded *OR* cacheing is off,
* unload the resource (caching==off) and reload it
*/
if (~game.dir_pic[n].flags & RES_LOADED) {
agi_v3.unload_resource(rPICTURE, n);
data = agi_v3_load_vol_res(&game.dir_pic[n]);
if (data != NULL) {
data = convert_v3_pic(data, game.dir_pic[n].len);
game.pictures[n].rdata = data;
game.dir_pic[n].flags |= RES_LOADED;
} else {
ec = err_BadResource;
}
}
break;
case rSOUND:
if (game.dir_sound[n].flags & RES_LOADED)
break;
if ((data = agi_v3_load_vol_res(&game.dir_sound[n])) != NULL) {
game.sounds[n].rdata = data;
game.dir_sound[n].flags |= RES_LOADED;
decode_sound(n);
} else {
ec = err_BadResource;
}
break;
case rVIEW:
/* Load a VIEW resource into memory...
* Since VIEWS alter the view table ALL the time can we
* cache the view? or must we reload it all the time?
*/
/* load a raw view from a VOL file into data */
if (game.dir_view[n].flags & RES_LOADED)
break;
agi_v3.unload_resource(rVIEW, n);
if ((data = agi_v3_load_vol_res(&game.dir_view[n])) != NULL) {
game.views[n].rdata = data;
game.dir_view[n].flags |= RES_LOADED;
ec = decode_view(n);
} else {
ec = err_BadResource;
}
break;
default:
ec = err_BadResource;
break;
}
return ec;
}
static int agi_v3_load_objects(char *fname) {
return load_objects(fname);
}
static int agi_v3_load_words(char *fname) {
return load_words(fname);
}
} // End of namespace Agi

320
engines/agi/checks.cpp Normal file
View File

@ -0,0 +1,320 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
namespace Agi {
static int check_position(struct vt_entry *v) {
debugC(4, kDebugLevelSprites, "check position @ %d, %d", v->x_pos, v->y_pos);
if (v->x_pos < 0 ||
v->x_pos + v->x_size > _WIDTH ||
v->y_pos - v->y_size + 1 < 0 ||
v->y_pos >= _HEIGHT ||
((~v->flags & IGNORE_HORIZON) && v->y_pos <= game.horizon)) {
debugC(4, kDebugLevelSprites, "check position failed: x=%d, y=%d, h=%d, w=%d",
v->x_pos, v->y_pos, v->x_size, v->y_size);
return 0;
}
/* MH1 needs this, but it breaks LSL1 */
if (agi_get_release() >= 0x3000) {
if (v->y_pos < v->y_size)
return 0;
}
return 1;
}
/**
* Check if there's another object on the way
*/
static int check_collision(struct vt_entry *v) {
struct vt_entry *u;
if (v->flags & IGNORE_OBJECTS)
return 0;
for (u = game.view_table; u < &game.view_table[MAX_VIEWTABLE]; u++) {
if ((u->flags & (ANIMATED | DRAWN)) != (ANIMATED | DRAWN))
continue;
if (u->flags & IGNORE_OBJECTS)
continue;
/* Same object, check next */
if (v->entry == u->entry)
continue;
/* No horizontal overlap, check next */
if (v->x_pos + v->x_size < u->x_pos ||
v->x_pos > u->x_pos + u->x_size)
continue;
/* Same y, return error! */
if (v->y_pos == u->y_pos)
goto return_1;
/* Crossed the baseline, return error! */
if ((v->y_pos > u->y_pos && v->y_pos2 < u->y_pos2) ||
(v->y_pos < u->y_pos && v->y_pos2 > u->y_pos2)) {
goto return_1;
}
}
return 0;
return_1:
debugC(4, kDebugLevelSprites, "check returns 1 (object %d)", v->entry);
return 1;
}
static int check_priority(struct vt_entry *v) {
int i, trigger, water, pass, pri;
uint8 *p0;
if (~v->flags & FIXED_PRIORITY) {
/* Priority bands */
v->priority = game.pri_table[v->y_pos];
}
trigger = 0;
water = 0;
pass = 1;
if (v->priority == 0x0f)
goto _check_ego;
water = 1;
p0 = &game.sbuf[v->x_pos + v->y_pos * _WIDTH];
for (i = 0; i < v->x_size; i++, p0++) {
pri = *p0 >> 4;
if (pri == 0) { /* unconditional black. no go at all! */
pass = 0;
break;
}
if (pri == 3) /* water surface */
continue;
water = 0;
if (pri == 1) { /* conditional blue */
if (v->flags & IGNORE_BLOCKS)
continue;
debugC(4, kDebugLevelSprites, "Blocks observed!");
pass = 0;
break;
}
if (pri == 2) { /* trigger */
debugC(4, kDebugLevelSprites, "stepped on trigger");
#ifdef USE_CONSOLE
if (!debug_.ignoretriggers)
#endif
trigger = 1;
}
}
if (pass) {
if (!water && v->flags & ON_WATER)
pass = 0;
if (water && v->flags & ON_LAND)
pass = 0;
}
_check_ego:
if (v->entry == 0) {
setflag(F_ego_touched_p2, trigger ? true : false);
setflag(F_ego_water, water ? true : false);
}
return pass;
}
/*
* Public functions
*/
/**
* Update position of objects
* This function updates the position of all animated, updating view
* table entries according to its motion type, step size, etc. The
* new position must be valid according to the sprite positioning
* rules, otherwise the previous position will be kept.
*/
void update_position() {
struct vt_entry *v;
int x, y, old_x, old_y, border;
game.vars[V_border_code] = 0;
game.vars[V_border_touch_ego] = 0;
game.vars[V_border_touch_obj] = 0;
for (v = game.view_table; v < &game.view_table[MAX_VIEWTABLE]; v++) {
if ((v->flags & (ANIMATED | UPDATE | DRAWN)) != (ANIMATED | UPDATE | DRAWN)) {
continue;
}
if (v->step_time_count != 0) {
if (--v->step_time_count != 0)
continue;
}
v->step_time_count = v->step_time;
x = old_x = v->x_pos;
y = old_y = v->y_pos;
/* If object has moved, update its position */
if (~v->flags & UPDATE_POS) {
int dx[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
int dy[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };
x += v->step_size * dx[v->direction];
y += v->step_size * dy[v->direction];
}
/* Now check if it touched the borders */
border = 0;
/* Check left/right borders */
if (x < 0) {
x = 0;
border = 4;
} else if (x <= 0 && agi_get_release() == 0x3086) { /* KQ4 */
x = 0; /* See bug #590462 */
border = 4;
} else if (v->entry == 0 && x == 0 && v->flags & ADJ_EGO_XY) {
/* Extra test to walk west clicking the mouse */
x = 0;
border = 4;
} else if (x + v->x_size > _WIDTH) {
x = _WIDTH - v->x_size;
border = 2;
}
/* Check top/bottom borders. */
if (y - v->y_size + 1 < 0) {
y = v->y_size - 1;
border = 1;
} else if (y > _HEIGHT - 1) {
y = _HEIGHT - 1;
border = 3;
} else if ((~v->flags & IGNORE_HORIZON) && y <= game.horizon) {
debugC(4, kDebugLevelSprites, "y = %d, horizon = %d", y, game.horizon);
y = game.horizon + 1;
border = 1;
}
/* Test new position. rollback if test fails */
v->x_pos = x;
v->y_pos = y;
if (check_collision(v) || !check_priority(v)) {
v->x_pos = old_x;
v->y_pos = old_y;
border = 0;
fix_position(v->entry);
}
if (border != 0) {
if (v == game.view_table) {
game.vars[V_border_touch_ego] = border;
} else {
game.vars[V_border_code] = v->entry;
game.vars[V_border_touch_obj] = border;
}
if (v->motion == MOTION_MOVE_OBJ) {
in_destination(v);
}
}
v->flags &= ~UPDATE_POS;
}
}
/**
* Adjust position of a sprite
* This function adjusts the position of a sprite moving it until
* certain criteria is matched. According to priority and control line
* data, a sprite may not always appear at the location we specified.
* This behaviour is also known as the "Budin-Sonneveld effect".
*
* @param n view table entry number
*/
void fix_position(int n) {
struct vt_entry *v = &game.view_table[n];
int count, dir, size;
debugC(4, kDebugLevelSprites, "adjusting view table entry #%d (%d,%d)", n, v->x_pos, v->y_pos);
/* test horizon */
if ((~v->flags & IGNORE_HORIZON) && v->y_pos <= game.horizon)
v->y_pos = game.horizon + 1;
dir = 0;
count = size = 1;
while (!check_position(v) || check_collision(v) || !check_priority(v)) {
switch (dir) {
case 0: /* west */
v->x_pos--;
if (--count)
continue;
dir = 1;
break;
case 1: /* south */
v->y_pos++;
if (--count)
continue;
dir = 2;
size++;
break;
case 2: /* east */
v->x_pos++;
if (--count)
continue;
dir = 3;
break;
case 3: /* north */
v->y_pos--;
if (--count)
continue;
dir = 0;
size++;
break;
}
count = size;
}
debugC(4, kDebugLevelSprites, "view table entry #%d position adjusted to (%d,%d)", n, v->x_pos, v->y_pos);
}
} // End of namespace Agi

873
engines/agi/console.cpp Normal file
View File

@ -0,0 +1,873 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2002 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/graphics.h"
#include "agi/sprite.h"
#include "agi/text.h"
#include "agi/keyboard.h"
#include "agi/opcodes.h"
#include "agi/console.h"
namespace Agi {
#ifdef USE_CONSOLE
/*
* The main interpreter engine has not been designed to have a console, and a few
* kludges were needed to make the console work. First of all, the main
* interpreter loop works at cycle level, not instruction level. To keep
* the illusion that the console is threaded, a console updating function
* is called from loops inside instructions such as get.string().
*/
#define CONSOLE_LINES_ONSCREEN 20
#define CONSOLE_PROMPT "$"
#define CONSOLE_CURSOR_HOLLOW 1
#define CONSOLE_CURSOR_SOLID 2
#define CONSOLE_CURSOR_EMPTY 3
#define CONSOLE_COLOR 14
#define CONSOLE_SCROLLUP_KEY KEY_PGUP
#define CONSOLE_SCROLLDN_KEY KEY_PGDN
#define CONSOLE_START_KEY KEY_HOME
#define CONSOLE_END_KEY KEY_END
#define CONSOLE_INPUT_SIZE 39
struct console_command {
char cmd[8];
char dsc[40];
void (*handler) (void);
};
struct agi_console console;
struct agi_debug debug_;
/* This used to be a linked list, but we reduce total memory footprint
* by implementing it as a statically allocated array.
*/
#define MAX_CCMD 16
static struct console_command ccmd_list[MAX_CCMD];
static int num_ccmd = 0;
static uint8 has_console = 0;
static uint8 console_input = 0;
static char *_p0, *_p1, *_p2, *_p3, *_p4, *_p5; /* FIXME: array */
static char _p[80];
static int _pn;
/*
* Console line management
*/
static int first_line = 0;
static void add_console_line(char *s) {
int last_line;
last_line = (CONSOLE_LINES_BUFFER - 1 + first_line) % CONSOLE_LINES_BUFFER;
strncpy(console.line[last_line], s, CONSOLE_LINE_SIZE);
first_line %= CONSOLE_LINES_BUFFER;
}
static char *get_console_line(int n) {
return console.line[(n + first_line) % CONSOLE_LINES_BUFFER];
}
static char *get_last_console_line() {
int last_line = (CONSOLE_LINES_BUFFER - 1 + first_line) % CONSOLE_LINES_BUFFER;
return console.line[last_line];
}
/*
* Console command parsing
* 'o' commands added by Shaun Jackman <sjackman@shaw.ca>, 11 Apr 2002
*/
static int console_parse(char *b) {
struct console_command *d;
int i;
for (; *b && *b == ' '; b++) {
} /* eat spaces */
for (; *b && b[strlen(b) - 1] == ' '; b[strlen(b) - 1] = 0) {
}
if (!*b)
return 0;
/* get or set flag/var/obj values */
if ((b[0] == 'f' || b[0] == 'v' || b[0] == 'o') && isdigit(b[1])) {
char *e;
int f = (int)strtoul(&b[1], &e, 10);
if (*e == 0) {
if (f >= 0 && f <= 255) {
switch (b[0]) {
case 'f':
report(getflag(f) ? "true\n" : "false\n");
break;
case 'v':
report("%3d\n", getvar(f));
break;
case 'o':
report("%3d]%-24s(%3d)\n", f, object_name(f), object_get_location(f));
break;
}
return 0;
}
return -1;
} else if (*e == '=') {
int n = (int)strtoul(e + 1, NULL, 0);
switch (b[0]) {
case 'f':
setflag(f, !!n);
break;
case 'v':
setvar(f, n);
break;
case 'o':
object_set_location(f, n);
break;
}
return 0;
}
}
/* tokenize the input line */
if (strchr(b, ' '))
strcpy(_p, strchr(b, ' ') + 1);
_p0 = strtok(b, " ");
_p1 = strtok(NULL, " ");
_p2 = strtok(NULL, " ");
_p3 = strtok(NULL, " ");
_p4 = strtok(NULL, " ");
_p5 = strtok(NULL, " ");
/* set number of parameters. ugh, must rewrite this later */
_pn = 5;
if (!_p5)
_pn = 4;
if (!_p4)
_pn = 3;
if (!_p3)
_pn = 2;
if (!_p2)
_pn = 1;
if (!_p1)
_pn = 0;
debugC(5, kDebugLevelMain, "number of parameters: %d", _pn);
for (i = 0; i < num_ccmd; i++) {
d = &ccmd_list[i];
if (!strcmp(_p0, d->cmd) && d->handler) {
d->handler();
return 0;
}
}
for (i = 0; logic_names_cmd[i].name; i++) {
if (!strcmp(_p0, logic_names_cmd[i].name)) {
uint8 p[16];
if (_pn != logic_names_cmd[i].num_args) {
report("AGI command wants %d arguments\n", logic_names_cmd[i].num_args);
return 0;
}
p[0] = _p1 ? (char)strtoul(_p1, NULL, 0) : 0;
p[1] = _p2 ? (char)strtoul(_p2, NULL, 0) : 0;
p[2] = _p3 ? (char)strtoul(_p3, NULL, 0) : 0;
p[3] = _p4 ? (char)strtoul(_p4, NULL, 0) : 0;
p[4] = _p5 ? (char)strtoul(_p5, NULL, 0) : 0;
debugC(5, kDebugLevelMain, "ccmd: %s %d %d %d", logic_names_cmd[i].name, p[0], p[1], p[2]);
execute_agi_command(i, p);
return 0;
}
}
return -1;
}
/*
* Console commands
*/
static void ccmd_help() {
int i;
if (!_p1) {
report("Command Description\n");
report("------- --------------------------------\n");
for (i = 0; i < num_ccmd; i++)
report("%-8s%s\n", ccmd_list[i].cmd, ccmd_list[i].dsc);
return;
}
for (i = 0; i < num_ccmd; i++) {
if (!strcmp(ccmd_list[i].cmd, _p1) && ccmd_list[i].handler) {
report("%s\n", ccmd_list[i].dsc);
return;
}
}
report("Unknown command or no help available\n");
return;
}
static void ccmd_ver() {
report(VERSION "\n");
return;
}
static void ccmd_crc() {
char name[80];
report("0x%05x\n", game.crc);
if (match_crc(game.crc, name, 80))
report("%s\n", name);
else
report("Unknown game\n");
return;
}
static void ccmd_quit() {
deinit_video();
/* deinit_machine (); */
exit(0);
}
#if 0
static void ccmd_exec() {
if (game.state < STATE_LOADED) {
report("No game loaded.\n");
return;
}
if (game.state >= STATE_RUNNING) {
report("Game already running.\n");
return;
}
report("Executing AGI game.\n");
game.state = STATE_RUNNING;
}
#endif
#ifdef USE_HIRES
static void ccmd_hires() {
if (_pn != 1 || (strcmp(_p1, "on") && strcmp(_p1, "off"))) {
report("Usage: hires on|off\n");
return;
}
opt.hires = !strcmp(_p1, "on");
erase_both();
show_pic();
blit_both();
return;
}
#endif
static void ccmd_agiver() {
int ver, maj, min;
ver = agi_get_release();
maj = (ver >> 12) & 0xf;
min = ver & 0xfff;
report(maj <= 2 ? "%x.%03x\n" : "%x.002.%03x\n", maj, min);
return;
}
static void ccmd_flags() {
int i, j;
report(" ");
for (j = 0; j < 10; j++)
report("%d ", j);
report("\n");
for (i = 0; i < 255;) {
report("%3d ", i);
for (j = 0; j < 10; j++, i++) {
report("%c ", getflag(i) ? 'T' : 'F');
}
report("\n");
}
return;
}
static void ccmd_vars() {
int i, j;
for (i = 0; i < 255;) {
for (j = 0; j < 5; j++, i++) {
report("%03d:%3d ", i, getvar(i));
}
report("\n");
}
return;
}
#if 0
static void ccmd_say() {
setflag(F_entered_cli, true);
dictionary_words(_p);
}
static void ccmd_inv() {
unsigned int i, j;
for (j = i = 0; i < game.num_objects; i++) {
if (object_get_location(i) == EGO_OWNED) {
report("%3d]%-16.16s", i, object_name(i));
if (j % 2)
report("\n");
j++;
}
}
if (j == 0) {
report("none\n");
return;
}
if (j % 2)
report("\n");
}
#endif
static void ccmd_objs() {
unsigned int i;
for (i = 0; i < game.num_objects; i++) {
report("%3d]%-24s(%3d)\n", i, object_name(i), object_get_location(i));
}
return;
}
static void ccmd_opcode() {
if (_pn != 1 || (strcmp(_p1, "on") && strcmp(_p1, "off"))) {
report("Usage: opcode on|off\n");
return;
}
debug_.opcodes = !strcmp(_p1, "on");
return;
}
static void ccmd_logic0() {
if (_pn != 1 || (strcmp(_p1, "on") && strcmp(_p1, "off"))) {
report("Usage: logic0 on|off\n");
return;
}
debug_.logic0 = !strcmp(_p1, "on");
return;
}
static void ccmd_trigger() {
if (_pn != 1 || (strcmp(_p1, "on") && strcmp(_p1, "off"))) {
report("Usage: trigger on|off\n");
return;
}
debug_.ignoretriggers = strcmp(_p1, "on");
return;
}
static void ccmd_step() {
debug_.enabled = 1;
if (_pn == 0) {
debug_.steps = 1;
return;
}
debug_.steps = strtoul(_p1, NULL, 0);
return;
}
static void ccmd_debug() {
debug_.enabled = 1;
debug_.steps = 0;
return;
}
static void ccmd_cont() {
debug_.enabled = 0;
debug_.steps = 0;
return;
}
/*
* Register console commands
*/
static void console_cmd(char *cmd, char *dsc, void (*handler) (void)) {
assert(num_ccmd < MAX_CCMD);
strcpy(ccmd_list[num_ccmd].cmd, cmd);
strcpy(ccmd_list[num_ccmd].dsc, dsc);
ccmd_list[num_ccmd].handler = handler;
num_ccmd++;
}
/* Console reporting */
/* A slightly modified strtok() for report() */
static char *get_token(char *s, char d) {
static char *x;
char *n, *m;
if (s)
x = s;
m = x;
n = strchr(x, d);
if (n) {
*n = 0;
x = n + 1;
} else {
x = strchr(x, 0);
}
return m;
}
static void build_console_lines(int n) {
int j, y1;
char *line;
clear_console_screen(GFX_HEIGHT - n * 10);
for (j = CONSOLE_LINES_ONSCREEN - n; j < CONSOLE_LINES_ONSCREEN; j++) {
line = get_console_line(console.first_line + j);
print_text_console(line, 0, j, strlen(line) + 1, CONSOLE_COLOR, 0);
}
y1 = console.y - n * 10;
if (y1 < 0)
y1 = 0;
/* CM:
* This will cause blinking when using menus+console, but this
* function is called by report() which can be called from any
* point with or without sprites placed. If we protect the
* flush_block() call with redraw/release sprites cloning will
* happen when activating the console. This could be fixed by
* keeping a control flag in redraw/release to not do the
* actions twice, but I don't want to do that -- not yet.
*/
flush_block(0, y1, GFX_WIDTH - 1, console.y);
}
/*
* Public functions
*/
int console_init() {
console_cmd("agiver", "Show emulated Sierra AGI version", ccmd_agiver);
console_cmd("cont", "Resume interpreter execution", ccmd_cont);
console_cmd("debug", "Stop interpreter execution", ccmd_debug);
console_cmd("crc", "Show AGI game CRC", ccmd_crc);
#if 0
console_cmd("exec", "Execute loaded AGI game", ccmd_exec);
#endif
console_cmd("flags", "Dump all AGI flags", ccmd_flags);
console_cmd("help", "List available commands", ccmd_help);
#ifdef USE_HIRES
console_cmd("hires", "Turn hi-res mode on/off", ccmd_hires);
#endif
console_cmd("logic0", "Turn logic 0 debugging on/off", ccmd_logic0);
console_cmd("objs", "List all objects and locations", ccmd_objs);
console_cmd("opcode", "Turn opcodes on/off in debug", ccmd_opcode);
console_cmd("quit", "Quit the interpreter", ccmd_quit);
#if 0
console_cmd("inv", "List current inventory", ccmd_inv);
console_cmd("say", "Pass argument to the AGI parser", ccmd_say);
#endif
console_cmd("step", "Execute the next AGI instruction", ccmd_step);
console_cmd("trigger", "Turn trigger lines on/off", ccmd_trigger);
console_cmd("vars", "Dump all AGI variables", ccmd_vars);
console_cmd("ver", "Show interpreter version", ccmd_ver);
console.active = 1;
console.input_active = 1;
console.index = 0;
console.max_y = 150;
console.y = console.max_y;
console.first_line = CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN;
debug_.enabled = 0;
debug_.opcodes = 0;
debug_.logic0 = 1;
debug_.priority = 0;
has_console = 1;
clear_screen(0);
return err_OK;
}
static void build_console_layer() {
build_console_lines(console.max_y / 10);
}
void report(char *message, ...) {
char x[512], y[512], z[64], *msg, *n;
va_list args;
int i, s, len;
if (!has_console)
return;
va_start(args, message);
#ifdef HAVE_VSNPRINTF
vsnprintf(y, 510, (char *)message, args);
#else
vsprintf(y, (char *)message, args);
#endif
va_end(args);
if (console_input) {
strcpy(z, get_last_console_line());
strcpy(x, ">");
} else {
strcpy(x, get_last_console_line());
}
strcat(x, y);
len = 40;
msg = n = word_wrap_string(x, &len);
for (s = 1; *n; n++)
if (*n == '\n')
s++;
add_console_line(get_token(msg, '\n'));
for (i = 1; i < s; i++) {
first_line++;
n = get_token(NULL, '\n');
add_console_line(n);
}
console.first_line = CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN;
if (console_input) {
add_console_line(z);
}
/* Build layer */
if (console.y) {
if (s > 1)
build_console_layer();
else
build_console_lines(1);
}
free(msg);
do_update();
}
void console_cycle() {
static int old_y = 0;
static int blink = 0;
static char cursor[] = " ";
/* If no game has been loaded, keep the console visible! */
if (game.state < STATE_RUNNING) {
console.active = 1;
console.count = 10;
}
/* Initial console auto-hide timer */
if (console.count > 0)
console.count--;
if (console.count == 0) {
console.active = 0;
console.count = -1;
}
if (console.active) {
if (console.y < console.max_y)
console.y += 15;
else
console.y = console.max_y;
} else {
console.count = -1;
if (console.y > 0)
console.y -= 15;
else
console.y = 0;
}
/* console shading animation */
if (old_y != console.y) {
int y = console.y;
if (old_y > console.y) {
/* going up */
y = old_y;
}
flush_block(0, 0, GFX_WIDTH - 1, y);
old_y = console.y;
}
blink++;
blink %= 16;
if (console.input_active) {
*cursor = blink < 8 ?
CONSOLE_CURSOR_SOLID : CONSOLE_CURSOR_EMPTY;
} else {
*cursor = CONSOLE_CURSOR_HOLLOW;
}
if (console.y > 0
&& console.first_line ==
CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN) {
int16 y1 = console.y - 10, y2 = console.y - 1;
if (y1 < 0)
y1 = 0;
if (y2 < 0)
y2 = 0;
print_text_console(cursor, (1 + console.index), 19, 2, CONSOLE_COLOR, 0);
flush_block((1 + console.index) * 8, y1, (1 + console.index) * 8 + 7, y2 - 1);
}
do_update();
}
/* Return true if key was handled */
int console_keyhandler(int k) {
static char buffer[CONSOLE_INPUT_SIZE];
int16 y1, y2;
char m[2];
#ifdef USE_MOUSE
/* Right button switches console on/off */
if (!opt.agimouse) /* AGI Mouse uses right button */
if (k == BUTTON_RIGHT)
k = CONSOLE_ACTIVATE_KEY;
#endif
if (!console.active) {
if (k == CONSOLE_ACTIVATE_KEY) {
console.active = 1;
return true;
}
return false;
}
if (!console.input_active) {
if (k == CONSOLE_SWITCH_KEY) {
console.input_active = 1;
return true;
}
if (k == CONSOLE_ACTIVATE_KEY) {
console.active = 0;
return true;
}
return false;
}
y1 = console.y - 10;
y2 = console.y - 1;
if (y1 < 0)
y1 = 0;
if (y2 < 0)
y2 = 0;
#ifdef USE_MOUSE
/* Ignore left button in console */
if (k == BUTTON_LEFT)
return true;
#endif
/* this code breaks scrolling up, maybe it shoud only be executed
* in the default case?
*/
if (k) {
/* make sure it's not enter or a scroll key */
if ((k != KEY_ENTER) && (k != CONSOLE_SCROLLUP_KEY) && (k != CONSOLE_SCROLLDN_KEY)
&& (k != CONSOLE_START_KEY)) {
/* on any other input reset the console to the bottom */
if (console.first_line != CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN) {
console.first_line = CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN;
build_console_layer();
}
console.count = -1;
}
}
switch (k) {
case KEY_ENTER:
console_lock();
console.index = 0;
report("\n");
if (console_parse(buffer) != 0)
report("What? Where?\n");
buffer[0] = 0;
console_prompt();
break;
case KEY_BACKSPACE:{
char *x;
if (!console.index)
break;
x = get_last_console_line();
x[console.index] = 0;
*m = CONSOLE_CURSOR_EMPTY;
print_text_console(m, (console.index + 1), 19, 2, CONSOLE_COLOR, 0);
flush_block((console.index + 1) * CHAR_COLS, y1, (console.index + 1 + 1) * CHAR_COLS - 1, y2);
console.index--;
buffer[console.index] = 0;
}
break;
case CONSOLE_ACTIVATE_KEY:
console.active = !console.active;
if (console.active)
build_console_layer();
break;
case CONSOLE_SWITCH_KEY:
console.count = -1;
if (console.y)
console.input_active = !console.input_active;
break;
case CONSOLE_SCROLLUP_KEY:
console.count = -1;
if (!console.y)
break;
if (console.first_line > (CONSOLE_LINES_ONSCREEN / 2))
console.first_line -= CONSOLE_LINES_ONSCREEN / 2;
else
console.first_line = 0;
build_console_layer();
break;
case CONSOLE_SCROLLDN_KEY:
console.count = -1;
if (!console.y)
break;
if (console.first_line < (CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN - CONSOLE_LINES_ONSCREEN / 2))
console.first_line += CONSOLE_LINES_ONSCREEN / 2;
else
console.first_line = CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN;
build_console_layer();
break;
case CONSOLE_START_KEY:
console.count = -1;
if (console.y)
console.first_line = 0;
break;
case CONSOLE_END_KEY:
console.count = -1;
if (console.y)
console.first_line = CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN;
break;
default:
if (k >= 0x20 && k <= 0x7f && (console.index < CONSOLE_INPUT_SIZE - 2)) {
char l[42];
buffer[console.index] = k;
*m = k;
m[1] = 0;
console.index++;
sprintf(l, "%s%c", get_last_console_line(), k);
strncpy(get_last_console_line(), l, CONSOLE_LINE_SIZE);
buffer[console.index] = 0;
print_text_console(m, console.index, 19, 2, CONSOLE_COLOR, 0);
flush_block(console.index * 8, y1, console.index * 8 + 7, y2);
}
break;
}
do_update();
return true;
}
void console_prompt() {
report(CONSOLE_PROMPT);
console_input = 1;
}
void console_lock() {
console_input = 0;
}
#else
void *debug;
void report(char *message, ...) {
/* dummy */
}
int console_init() {
return 0;
}
/* Date: Sun, 14 Oct 2001 23:02:02 -0700
* From: Vasyl Tsvirkunov <vasyl@pacbell.net>
*
* This one was rather harmless and affected only builds without console.
* In SQ1&2 (and likely some others) name entry screen did not update
* properly. The bug caused by implicit assumption in cycle.c that
* console_cycle() updates the screen. Well, it does, if console is enabled.
* The fix is simple. In the second version of console_cycle() in console.c
* (the "dummy" one) add call to do_update(). The thing raises some
* questions about overall architecture of main cycle, but otherwise the fix
* works just fine.
*/
void console_cycle() {
do_update();
}
void console_lock() {
/* dummy */
}
void console_prompt() {
/* dummy */
}
int console_keyhandler(int i) {
return false;
}
#endif /* USE_CONSOLE */
} // End of namespace Agi

71
engines/agi/console.h Normal file
View File

@ -0,0 +1,71 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_CONSOLE_H
#define __AGI_CONSOLE_H
namespace Agi {
#ifdef USE_CONSOLE
#define CONSOLE_LINES_BUFFER 80
#define CONSOLE_LINE_SIZE (GFX_WIDTH / 8)
#define CONSOLE_ACTIVATE_KEY '`'
#define CONSOLE_SWITCH_KEY '~'
struct agi_console {
int active;
int input_active;
int index;
int y;
int max_y;
int first_line;
int count;
char *line[CONSOLE_LINES_BUFFER];
};
struct agi_debug {
int enabled;
int opcodes;
int logic0;
int steps;
int priority;
int statusline;
int ignoretriggers;
};
extern struct agi_console console;
#endif /* USE_CONSOLE */
int console_keyhandler(int);
int console_init(void);
void console_cycle(void);
void console_lock(void);
void console_prompt(void);
void report(char *, ...);
} // End of namespace Agi
#endif /* __AGI_CONSOLE_H */

423
engines/agi/cycle.cpp Normal file
View File

@ -0,0 +1,423 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/text.h"
#include "agi/sprite.h"
#include "agi/graphics.h"
#include "agi/keyboard.h"
#include "agi/menu.h"
#include "agi/savegame.h"
namespace Agi {
#define TICK_SECONDS 20
#ifdef USE_MOUSE
struct mouse mouse;
#endif
volatile uint32 clock_ticks;
volatile uint32 clock_count;
/**
* Set up new room.
* This function is called when ego enters a new room.
* @param n room number
*/
void new_room(int n) {
struct vt_entry *v;
int i;
debugC(4, kDebugLevelMain, "*** room %d ***", n);
stop_sound();
i = 0;
for (v = game.view_table; v < &game.view_table[MAX_VIEWTABLE]; v++) {
v->entry = i++;
v->flags &= ~(ANIMATED | DRAWN);
v->flags |= UPDATE;
v->step_time = 1;
v->step_time_count = 1;
v->cycle_time = 1;
v->cycle_time_count = 1;
v->step_size = 1;
}
agi_unload_resources();
game.player_control = true;
game.block.active = false;
game.horizon = 36;
game.vars[V_prev_room] = game.vars[V_cur_room];
game.vars[V_cur_room] = n;
game.vars[V_border_touch_obj] = 0;
game.vars[V_border_code] = 0;
game.vars[V_ego_view_resource] = game.view_table[0].current_view;
agi_load_resource(rLOGIC, n);
/* Reposition ego in the new room */
switch (game.vars[V_border_touch_ego]) {
case 1:
game.view_table[0].y_pos = _HEIGHT - 1;
break;
case 2:
game.view_table[0].x_pos = 0;
break;
case 3:
game.view_table[0].y_pos = HORIZON + 1;
break;
case 4:
game.view_table[0].x_pos = _WIDTH - game.view_table[0].x_size;
break;
}
game.vars[V_border_touch_ego] = 0;
setflag(F_new_room_exec, true);
game.exit_all_logics = true;
write_status();
write_prompt();
}
static void reset_controllers() {
int i;
for (i = 0; i < MAX_DIRS; i++) {
game.ev_keyp[i].occured = false;
}
}
static void interpret_cycle() {
int old_sound, old_score;
if (game.player_control)
game.vars[V_ego_dir] = game.view_table[0].direction;
else
game.view_table[0].direction = game.vars[V_ego_dir];
check_all_motions();
old_score = game.vars[V_score];
old_sound = getflag(F_sound_on);
game.exit_all_logics = false;
while (run_logic(0) == 0 && !game.quit_prog_now) {
game.vars[V_word_not_found] = 0;
game.vars[V_border_touch_obj] = 0;
game.vars[V_border_code] = 0;
old_score = game.vars[V_score];
setflag(F_entered_cli, false);
game.exit_all_logics = false;
reset_controllers();
}
reset_controllers();
game.view_table[0].direction = game.vars[V_ego_dir];
if (game.vars[V_score] != old_score || getflag(F_sound_on) != old_sound)
write_status();
game.vars[V_border_touch_obj] = 0;
game.vars[V_border_code] = 0;
setflag(F_new_room_exec, false);
setflag(F_restart_game, false);
setflag(F_restore_just_ran, false);
if (game.gfx_mode) {
update_viewtable();
do_update();
}
}
/**
* Update AGI interpreter timer.
*/
void update_timer() {
clock_count++;
if (clock_count <= TICK_SECONDS)
return;
clock_count -= TICK_SECONDS;
if (!game.clock_enabled)
return;
setvar(V_seconds, getvar(V_seconds) + 1);
if (getvar(V_seconds) < 60)
return;
setvar(V_seconds, 0);
setvar(V_minutes, getvar(V_minutes) + 1);
if (getvar(V_minutes) < 60)
return;
setvar(V_minutes, 0);
setvar(V_hours, getvar(V_hours) + 1);
if (getvar(V_hours) < 24)
return;
setvar(V_hours, 0);
setvar(V_days, getvar(V_days) + 1);
}
static int old_mode = -1;
void new_input_mode(int i) {
old_mode = game.input_mode;
game.input_mode = i;
}
void old_input_mode() {
game.input_mode = old_mode;
}
/* If main_cycle returns false, don't process more events! */
int main_cycle() {
unsigned int key, kascii;
struct vt_entry *v = &game.view_table[0];
poll_timer(); /* msdos driver -> does nothing */
update_timer();
if (game.ver == 0) {
message_box("Warning: game CRC not listed, assuming AGI version 2.917.");
game.ver = -1;
}
key = do_poll_keyboard();
#ifdef USE_MOUSE
/* In AGI Mouse emulation mode we must update the mouse-related
* vars in every interpreter cycle.
*/
if (opt.agimouse) {
game.vars[28] = mouse.x / 2;
game.vars[29] = mouse.y;
}
#endif
#ifdef USE_CONSOLE
if (key == KEY_PRIORITY) {
erase_both();
debug_.priority = !debug_.priority;
show_pic();
blit_both();
commit_both();
key = 0;
}
if (key == KEY_STATUSLN) {
debug_.statusline = !debug_.statusline;
write_status();
key = 0;
}
#endif
/* Click-to-walk mouse interface */
if (game.player_control && v->flags & ADJ_EGO_XY) {
v->direction = get_direction(v->x_pos, v->y_pos, v->parm1, v->parm2, v->step_size);
if (v->direction == 0)
in_destination(v);
}
kascii = KEY_ASCII(key);
if (!console_keyhandler(key)) {
if (kascii)
setvar(V_key, kascii);
process_key:
switch (game.input_mode) {
case INPUT_NORMAL:
if (!handle_controller(key)) {
if (key == 0 || !game.input_enabled)
break;
handle_keys(key);
/* if ESC pressed, activate menu before
* accept.input from the interpreter cycle
* sets the input mode to normal again
* (closes: #540856)
*/
if (key == KEY_ESCAPE) {
key = 0;
goto process_key;
}
/* commented out to close bug #438872
* if (key) game.keypress = key;
*/
}
break;
case INPUT_GETSTRING:
handle_controller(key);
handle_getstring(key);
setvar(V_key, 0); /* clear ENTER key */
break;
case INPUT_MENU:
menu_keyhandler(key);
console_cycle();
return false;
case INPUT_NONE:
handle_controller(key);
if (key)
game.keypress = key;
break;
}
} else {
if (game.input_mode == INPUT_MENU) {
console_cycle();
return false;
}
}
console_cycle();
if (game.msg_box_ticks > 0)
game.msg_box_ticks--;
return true;
}
static int play_game() {
int ec = err_OK;
debugC(2, kDebugLevelMain, "initializing...");
debugC(2, kDebugLevelMain, "game.ver = 0x%x", game.ver);
stop_sound();
clear_screen(0);
game.horizon = HORIZON;
game.player_control = false;
setflag(F_logic_zero_firsttime, true); /* not in 2.917 */
setflag(F_new_room_exec, true); /* needed for MUMG and SQ2! */
setflag(F_sound_on, true); /* enable sound */
setvar(V_time_delay, 2); /* "normal" speed */
game.gfx_mode = true;
game.quit_prog_now = false;
game.clock_enabled = true;
game.line_user_input = 22;
#ifdef USE_MOUSE
if (opt.agimouse)
report("Using AGI Mouse 1.0 protocol\n");
#endif
report("Running AGI script.\n");
#ifdef USE_CONSOLE
console.count = 5;
console_prompt();
#endif
setflag(F_entered_cli, false);
setflag(F_said_accepted_input, false);
game.vars[V_word_not_found] = 0;
game.vars[V_key] = 0;
debugC(2, kDebugLevelMain, "Entering main loop");
do {
if (!main_cycle())
continue;
if (getvar(V_time_delay) == 0 ||
(1 + clock_count) % getvar(V_time_delay) == 0) {
if (!game.has_prompt && game.input_mode == INPUT_NORMAL) {
write_prompt();
game.has_prompt = 1;
} else
if (game.has_prompt && game.input_mode == INPUT_NONE) {
write_prompt();
game.has_prompt = 0;
}
interpret_cycle();
setflag(F_entered_cli, false);
setflag(F_said_accepted_input, false);
game.vars[V_word_not_found] = 0;
game.vars[V_key] = 0;
}
if (game.quit_prog_now == 0xff)
ec = err_RestartGame;
} while (game.quit_prog_now == 0);
stop_sound();
return ec;
}
int run_game() {
int i, ec = err_OK;
#ifdef USE_HIRES
if (opt.cgaemu)
opt.hires = 0;
#endif
for (i = 0; i < MAX_DIRS; i++)
memset(&game.ev_keyp[i], 0, sizeof(struct agi_event));
/* Execute the game */
do {
debugC(2, kDebugLevelMain, "game loop");
debugC(2, kDebugLevelMain, "game.ver = 0x%x", game.ver);
if (agi_init() != err_OK)
break;
if (ec == err_RestartGame)
setflag(F_restart_game, true);
setvar(V_computer, 0); /* IBM PC (4 = Atari ST) */
setvar(V_soundgen, 1); /* IBM PC SOUND */
setvar(V_monitor, 0x3); /* EGA monitor */
setvar(V_max_input_chars, 38);
game.input_mode = INPUT_NONE;
game.input_enabled = 0;
game.has_prompt = 0;
game.state = STATE_RUNNING;
ec = play_game();
game.state = STATE_LOADED;
agi_deinit();
} while (ec == err_RestartGame);
menu_deinit();
release_image_stack();
return ec;
}
} // End of namespace Agi

295
engines/agi/font.cpp Normal file
View File

@ -0,0 +1,295 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
namespace Agi {
/* 8x8 font patterns */
uint8 cur_font[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, /* cursor hollow */
0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, /* cursor solid */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* cursor empty */
0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00,
0x3C, 0x3C, 0x18, 0xFF, 0xE7, 0x18, 0x3C, 0x00,
0x10, 0x38, 0x7C, 0xFE, 0xEE, 0x10, 0x38, 0x00,
0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00,
0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF,
0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* \n */
0x0F, 0x07, 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78,
0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18,
0x08, 0x0C, 0x0A, 0x0A, 0x08, 0x78, 0xF0, 0x00,
0x18, 0x14, 0x1A, 0x16, 0x72, 0xE2, 0x0E, 0x1C,
0x10, 0x54, 0x38, 0xEE, 0x38, 0x54, 0x10, 0x00,
0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00,
0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00,
0x18, 0x3C, 0x5A, 0x18, 0x5A, 0x3C, 0x18, 0x00,
0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x00,
0x1C, 0x22, 0x38, 0x44, 0x44, 0x38, 0x88, 0x70,
0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x00,
0x18, 0x3C, 0x5A, 0x18, 0x5A, 0x3C, 0x18, 0x7E,
0x18, 0x3C, 0x5A, 0x18, 0x18, 0x18, 0x18, 0x00,
0x18, 0x18, 0x18, 0x18, 0x5A, 0x3C, 0x18, 0x00,
0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00,
0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00,
0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00,
0x00, 0x24, 0x42, 0xFF, 0x42, 0x24, 0x00, 0x00,
0x00, 0x10, 0x38, 0x7C, 0xFE, 0xFE, 0x00, 0x00,
0x00, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00,
0x6C, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00,
0x10, 0x7C, 0xD0, 0x7C, 0x16, 0xFC, 0x10, 0x00,
0x00, 0x66, 0xAC, 0xD8, 0x36, 0x6A, 0xCC, 0x00,
0x38, 0x4C, 0x38, 0x78, 0xCE, 0xCC, 0x7A, 0x00,
0x30, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00,
0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x20,
0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00,
0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00,
0x7C, 0xCE, 0xDE, 0xF6, 0xE6, 0xE6, 0x7C, 0x00,
0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x7E, 0x00,
0x7C, 0xC6, 0x06, 0x1C, 0x70, 0xC6, 0xFE, 0x00,
0x7C, 0xC6, 0x06, 0x3C, 0x06, 0xC6, 0x7C, 0x00,
0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00,
0xFE, 0xC0, 0xFC, 0x06, 0x06, 0xC6, 0x7C, 0x00,
0x7C, 0xC6, 0xC0, 0xFC, 0xC6, 0xC6, 0x7C, 0x00,
0xFE, 0xC6, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00,
0x7C, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0x7C, 0x00,
0x7C, 0xC6, 0xC6, 0x7E, 0x06, 0xC6, 0x7C, 0x00,
0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x10, 0x20,
0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00,
0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00,
0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00,
0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00,
0x7C, 0x82, 0x9E, 0xA6, 0x9E, 0x80, 0x7C, 0x00,
0x7C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00,
0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00,
0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00,
0xFC, 0x66, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00,
0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00,
0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00,
0x7C, 0xC6, 0xC6, 0xC0, 0xCE, 0xC6, 0x7E, 0x00,
0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00,
0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00,
0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00,
0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00,
0x82, 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00,
0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00,
0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00,
0x7C, 0xC6, 0xC6, 0xC6, 0xD6, 0xDE, 0x7C, 0x06,
0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xE6, 0x00,
0x7C, 0xC6, 0xC0, 0x7C, 0x06, 0xC6, 0x7C, 0x00,
0x7E, 0x5A, 0x5A, 0x18, 0x18, 0x18, 0x3C, 0x00,
0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x10, 0x00,
0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x82, 0x00,
0xC6, 0x6C, 0x38, 0x38, 0x38, 0x6C, 0xC6, 0x00,
0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x3C, 0x00,
0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00,
0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00,
0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x30, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00,
0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00,
0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC6, 0x7C, 0x00,
0x1C, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0x00,
0x00, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0x7C, 0x00,
0x1C, 0x36, 0x30, 0x78, 0x30, 0x30, 0x78, 0x00,
0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x78,
0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00,
0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00,
0x00, 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0xCC, 0x78,
0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00,
0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
0x00, 0x00, 0xCC, 0xFE, 0xD6, 0xD6, 0xD6, 0x00,
0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x00,
0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0,
0x00, 0x00, 0x7C, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E,
0x00, 0x00, 0xDE, 0x76, 0x60, 0x60, 0xF0, 0x00,
0x00, 0x00, 0x7C, 0xC0, 0x7C, 0x06, 0x7C, 0x00,
0x10, 0x30, 0xFC, 0x30, 0x30, 0x34, 0x18, 0x00,
0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00,
0x00, 0x00, 0xC6, 0xC6, 0x6C, 0x38, 0x10, 0x00,
0x00, 0x00, 0xC6, 0xD6, 0xD6, 0xFE, 0x6C, 0x00,
0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00,
0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8,
0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00,
0x0E, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0E, 0x00,
0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
0xE0, 0x30, 0x30, 0x18, 0x30, 0x30, 0xE0, 0x00,
0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0x00, */
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*replacement 0x7F */
#ifdef AGDS_SUPPORT
0x1E, 0x36, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x00,
0x7C, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00,
0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00,
0x7E, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00,
0x38, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0xFE, 0xC6,
0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00,
0xDB, 0xDB, 0x7E, 0x3C, 0x7E, 0xDB, 0xDB, 0x00,
0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00,
0x66, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x66, 0x00,
0x3C, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x66, 0x00,
0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00,
0x1E, 0x36, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00,
0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00,
0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00,
0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00,
0x7E, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00,
0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00,
0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00,
0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
0x66, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00,
0x7E, 0xDB, 0xDB, 0xDB, 0x7E, 0x18, 0x18, 0x00,
0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7F, 0x03,
0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x00,
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xFF, 0x00,
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xFF, 0x03,
0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00,
0xC6, 0xC6, 0xC6, 0xF6, 0xDE, 0xDE, 0xF6, 0x00,
0x60, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00,
0x78, 0x8C, 0x06, 0x3E, 0x06, 0x8C, 0x78, 0x00,
0xCE, 0xDB, 0xDB, 0xFB, 0xDB, 0xDB, 0xCE, 0x00,
0x3E, 0x66, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x00,
0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3A, 0x00,
0x00, 0x3C, 0x60, 0x3C, 0x66, 0x66, 0x3C, 0x00,
0x00, 0x00, 0x7C, 0x66, 0x7C, 0x66, 0x7C, 0x00,
0x00, 0x00, 0x7E, 0x60, 0x60, 0x60, 0x60, 0x00,
0x00, 0x00, 0x3C, 0x6C, 0x6C, 0x6C, 0xFE, 0xC6,
0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00,
0x00, 0x00, 0xDB, 0x7E, 0x3C, 0x7E, 0xDB, 0x00,
0x00, 0x00, 0x3C, 0x66, 0x0C, 0x66, 0x3C, 0x00,
0x00, 0x00, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x00,
0x00, 0x18, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x00,
0x00, 0x00, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00,
0x00, 0x00, 0x1E, 0x36, 0x66, 0x66, 0x66, 0x00,
0x00, 0x00, 0xC6, 0xFE, 0xFE, 0xD6, 0xC6, 0x00,
0x00, 0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x00,
0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00,
0x00, 0x00, 0x7E, 0x66, 0x66, 0x66, 0x66, 0x00,
0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18,
0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18,
0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, 0x36,
0x00, 0x00, 0x00, 0xFE, 0x36, 0x36, 0x36, 0x36,
0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18,
0x36, 0xF6, 0x06, 0xF6, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x00, 0xFE, 0x06, 0xF6, 0x36, 0x36, 0x36, 0x36,
0x36, 0xF6, 0x06, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x36, 0x36, 0x36, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x18, 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00,
0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18,
0x18, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18,
0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36,
0x36, 0x37, 0x30, 0x3F, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3F, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
0x36, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0x00, 0xF7, 0x36, 0x36, 0x36, 0x36,
0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x36, 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, 0x36,
0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, 0x00, 0x00,
0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0xFF, 0x36, 0x36, 0x36, 0x36,
0x18, 0xFF, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, 0x18,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x00,
0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00,
0x00, 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00,
0x00, 0x00, 0x66, 0x66, 0x3E, 0x06, 0x7C, 0x00,
0x00, 0x00, 0x7E, 0xDB, 0xDB, 0x7E, 0x18, 0x00,
0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00,
0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7F, 0x03,
0x00, 0x00, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x00,
0x00, 0x00, 0xDB, 0xDB, 0xDB, 0xDB, 0xFF, 0x00,
0x00, 0x00, 0xDB, 0xDB, 0xDB, 0xDB, 0xFF, 0x03,
0x00, 0x00, 0xE0, 0x60, 0x7C, 0x66, 0x7C, 0x00,
0x00, 0x00, 0xC6, 0xC6, 0xF6, 0xDE, 0xF6, 0x00,
0x00, 0x00, 0x60, 0x60, 0x7C, 0x66, 0x7C, 0x00,
0x00, 0x00, 0x7C, 0x06, 0x3E, 0x06, 0x7C, 0x00,
0x00, 0x00, 0xCE, 0xDB, 0xFB, 0xDB, 0xCE, 0x00,
0x00, 0x00, 0x3E, 0x66, 0x3E, 0x36, 0x66, 0x00,
0x00, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00,
0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x7C, 0x00,
0x00, 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30,
0x00, 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C,
0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0xD8, 0x70,
0x00, 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18,
0x00, 0x76, 0xDC, 0x00, 0x76, 0xDC, 0x00, 0x00,
0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00,
0x03, 0x02, 0x06, 0x04, 0xCC, 0x68, 0x38, 0x10,
0x3C, 0x42, 0x99, 0xA1, 0xA1, 0x99, 0x42, 0x3C,
0x30, 0x48, 0x10, 0x20, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7C, 0x7C, 0x7C, 0x7C, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x7E, 0x00
#endif /* AGDS_SUPPORT */
};
} // End of namespace Agi

73
engines/agi/global.cpp Normal file
View File

@ -0,0 +1,73 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
namespace Agi {
int getflag(int n) {
uint8 *set = (uint8 *) & game.flags;
set += n >> 3;
return (*set & (1 << (n & 0x07))) != 0;
}
void setflag(int n, int v) {
uint8 *set = (uint8 *) & game.flags;
set += n >> 3;
if (v)
*set |= 1 << (n & 0x07); /* set bit */
else
*set &= ~(1 << (n & 0x07)); /* clear bit */
}
void flipflag(int n) {
uint8 *set = (uint8 *) & game.flags;
set += n >> 3;
*set ^= 1 << (n & 0x07); /* flip bit */
}
void setvar(int var, int val) {
game.vars[var] = val;
}
int getvar(int var) {
return game.vars[var];
}
void decrypt(uint8 *mem, int len) {
uint8 *key;
int i;
key = opt.agds ? (uint8 *)CRYPT_KEY_AGDS : (uint8 *)CRYPT_KEY_SIERRA;
for (i = 0; i < len; i++)
*(mem + i) ^= *(key + (i % 11));
}
} // End of namespace Agi

742
engines/agi/graphics.cpp Normal file
View File

@ -0,0 +1,742 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/graphics.h"
namespace Agi {
#define DEV_X0(x) ((x) << 1)
#define DEV_X1(x) (((x) << 1) + 1)
#define DEV_Y(x) (x)
#ifndef MAX_INT
# define MAX_INT (int)((unsigned)~0 >> 1)
#endif
static uint8 *agi_screen;
#ifdef USE_CONSOLE
static uint8 *console_screen;
#endif
static unsigned char *screen;
extern uint8 cur_font[];
/**
* 16 color RGB palette (plus 16 transparent colors).
* This array contains the 6-bit RGB values of the EGA palette exported
* to the console drivers.
*/
uint8 ega_palette[16 * 3] = {
0x00, 0x00, 0x00,
0x00, 0x00, 0x2a,
0x00, 0x2a, 0x00,
0x00, 0x2a, 0x2a,
0x2a, 0x00, 0x00,
0x2a, 0x00, 0x2a,
0x2a, 0x15, 0x00,
0x2a, 0x2a, 0x2a,
0x15, 0x15, 0x15,
0x15, 0x15, 0x3f,
0x15, 0x3f, 0x15,
0x15, 0x3f, 0x3f,
0x3f, 0x15, 0x15,
0x3f, 0x15, 0x3f,
0x3f, 0x3f, 0x15,
0x3f, 0x3f, 0x3f
};
/**
* 16 color amiga-ish palette.
*/
uint8 new_palette[16 * 3] = {
0x00, 0x00, 0x00,
0x00, 0x00, 0x3f,
0x00, 0x2A, 0x00,
0x00, 0x2A, 0x2A,
0x33, 0x00, 0x00,
0x2f, 0x1c, 0x37,
0x23, 0x14, 0x00,
0x2f, 0x2f, 0x2f,
0x15, 0x15, 0x15,
0x00, 0x2f, 0x3f,
0x00, 0x33, 0x15,
0x15, 0x3F, 0x3F,
0x3f, 0x27, 0x23,
0x3f, 0x15, 0x3f,
0x3b, 0x3b, 0x00,
0x3F, 0x3F, 0x3F
};
uint8 palette[32 * 3];
static uint16 cga_map[16] = {
0x0000, /* 0 - black */
0x0d00, /* 1 - blue */
0x0b00, /* 2 - green */
0x0f00, /* 3 - cyan */
0x000b, /* 4 - red */
0x0b0d, /* 5 - magenta */
0x000d, /* 6 - brown */
0x0b0b, /* 7 - gray */
0x0d0d, /* 8 - dark gray */
0x0b0f, /* 9 - light blue */
0x0b0d, /* 10 - light green */
0x0f0d, /* 11 - light cyan */
0x0f0d, /* 12 - light red */
0x0f00, /* 13 - light magenta */
0x0f0b, /* 14 - yellow */
0x0f0f /* 15 - white */
};
struct update_block {
int x1, y1;
int x2, y2;
};
static struct update_block update = {
MAX_INT, MAX_INT, 0, 0
};
struct gfx_driver *gfx; /* graphics driver */
/*
* Layer 4: 640x480? ================== User display
* ^
* | do_update(), put_block()
* |
* Layer 3: 640x480? ================== Framebuffer
* ^
* | flush_block(), put_pixels()
* |
* Layer 2: 320x200 ================== AGI engine screen (console), put_pixel()
* |
* Layer 1: 160x168 ================== AGI screen
*/
#ifdef USE_CONSOLE
/**
* Draws a row of pixels in the output device framebuffer.
* This function adds the console layer using transparent colors if
* appropriate.
*/
static void put_pixels(const int x, const int y, const int w, uint8 *p) {
int i;
uint8 _b[GFX_WIDTH] = { 0 };
uint8 *b, *c = NULL;
if (console.y <= y) {
gfx_putpixels(x, y, w, p);
return;
}
b = &_b[0];
c = &console_screen[x + (y + GFX_HEIGHT - 1 - console.y) * GFX_WIDTH];
for (i = 0; i < w; i++, c++, p++) {
*b++ = *c ? *c : *p + 16;
}
gfx_putpixels(x, y, w, _b);
}
static void init_console() {
int i;
/* Console */
console.line[0] = (char *)calloc(CONSOLE_LINES_BUFFER, CONSOLE_LINE_SIZE + 1);
for (i = 1; i < CONSOLE_LINES_BUFFER; i++)
console.line[i] = console.line[i - 1] + CONSOLE_LINE_SIZE + 1;
}
#else
static void put_pixels(const int x, const int y, const int w, uint8 *p) {
gfx->put_pixels(x, y, w, p);
}
static void init_console()
{
}
#endif /* USE_CONSOLE */
#define SHAKE_MAG 3
static uint8 *shake_h, *shake_v;
void shake_start() {
int i;
if ((shake_h = (uint8 *)malloc(GFX_WIDTH * SHAKE_MAG)) == NULL)
return;
if ((shake_v = (uint8 *)malloc(SHAKE_MAG * (GFX_HEIGHT - SHAKE_MAG))) == NULL) {
free(shake_h);
return;
}
for (i = 0; i < GFX_HEIGHT - SHAKE_MAG; i++) {
memcpy(shake_v + i * SHAKE_MAG, agi_screen + i * GFX_WIDTH, SHAKE_MAG);
}
for (i = 0; i < SHAKE_MAG; i++) {
memcpy(shake_h + i * GFX_WIDTH, agi_screen + i * GFX_WIDTH, GFX_WIDTH);
}
}
void shake_screen(int n) {
int i;
if (n == 0) {
for (i = 0; i < (GFX_HEIGHT - SHAKE_MAG); i++) {
memmove(&agi_screen[GFX_WIDTH * i],
&agi_screen[GFX_WIDTH * (i + SHAKE_MAG) + SHAKE_MAG],
GFX_WIDTH - SHAKE_MAG);
}
} else {
for (i = GFX_HEIGHT - SHAKE_MAG - 1; i >= 0; i--) {
memmove(&agi_screen[GFX_WIDTH * (i + SHAKE_MAG) + SHAKE_MAG],
&agi_screen[GFX_WIDTH * i], GFX_WIDTH - SHAKE_MAG);
}
}
}
void shake_end() {
int i;
for (i = 0; i < GFX_HEIGHT - SHAKE_MAG; i++) {
memcpy(agi_screen + i * GFX_WIDTH, shake_v + i * SHAKE_MAG, SHAKE_MAG);
}
for (i = 0; i < SHAKE_MAG; i++) {
memcpy(agi_screen + i * GFX_WIDTH, shake_h + i * GFX_WIDTH, GFX_WIDTH);
}
flush_block(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1);
free(shake_v);
free(shake_h);
}
void put_text_character(int l, int x, int y, unsigned int c, int fg, int bg) {
int x1, y1, xx, yy, cc;
uint8 *p;
p = cur_font + ((unsigned int)c * CHAR_LINES);
for (y1 = 0; y1 < CHAR_LINES; y1++) {
for (x1 = 0; x1 < CHAR_COLS; x1++) {
xx = x + x1;
yy = y + y1;
cc = (*p & (1 << (7 - x1))) ? fg : bg;
#ifdef USE_CONSOLE
if (l) {
console_screen[xx + yy * GFX_WIDTH] = cc;
} else
#endif
{
agi_screen[xx + yy * GFX_WIDTH] = cc;
}
}
p++;
}
/* FIXME: we don't want this when we're writing on the
* console!
*/
flush_block(x, y, x + CHAR_COLS - 1, y + CHAR_LINES - 1);
}
void draw_rectangle(int x1, int y1, int x2, int y2, int c) {
int y, w, h;
uint8 *p0;
if (x1 >= GFX_WIDTH)
x1 = GFX_WIDTH - 1;
if (y1 >= GFX_HEIGHT)
y1 = GFX_HEIGHT - 1;
if (x2 >= GFX_WIDTH)
x2 = GFX_WIDTH - 1;
if (y2 >= GFX_HEIGHT)
y2 = GFX_HEIGHT - 1;
w = x2 - x1 + 1;
h = y2 - y1 + 1;
p0 = &agi_screen[x1 + y1 * GFX_WIDTH];
for (y = 0; y < h; y++) {
memset(p0, c, w);
p0 += GFX_WIDTH;
}
}
static void draw_frame(int x1, int y1, int x2, int y2, int c1, int c2) {
int y, w;
uint8 *p0;
/* top line */
w = x2 - x1 + 1;
p0 = &agi_screen[x1 + y1 * GFX_WIDTH];
memset(p0, c1, w);
/* bottom line */
p0 = &agi_screen[x1 + y2 * GFX_WIDTH];
memset(p0, c2, w);
/* side lines */
for (y = y1; y <= y2; y++) {
agi_screen[x1 + y * GFX_WIDTH] = c1;
agi_screen[x2 + y * GFX_WIDTH] = c2;
}
}
void draw_box(int x1, int y1, int x2, int y2, int colour1, int colour2, int m) {
x1 += m;
y1 += m;
x2 -= m;
y2 -= m;
draw_rectangle(x1, y1, x2, y2, colour1);
draw_frame(x1 + 2, y1 + 2, x2 - 2, y2 - 2, colour2, colour2);
flush_block(x1, y1, x2, y2);
}
void print_character(int x, int y, char c, int fg, int bg) {
x *= CHAR_COLS;
y *= CHAR_LINES;
put_text_character(0, x, y, c, fg, bg);
// redundant! already inside put_text_character!
// flush_block (x, y, x + CHAR_COLS - 1, y + CHAR_LINES - 1);
}
/**
* Draw button
* @param x x coordinate of the button
* @param y y coordinate of the button
* @param a set if the button has focus
* @param p set if the button is pressed
*/
void draw_button(int x, int y, char *s, int a, int p) {
int len = strlen(s);
int x1, y1, x2, y2;
x1 = x - 3;
y1 = y - 3;
x2 = x + CHAR_COLS * len + 2;
y2 = y + CHAR_LINES + 2;
while (*s) {
put_text_character(0, x + (!!p), y + (!!p), *s++,
a ? 15 : 0, a ? 0 : 15);
x += CHAR_COLS;
}
x1 -= 2;
y1 -= 2;
x2 += 2;
y2 += 2;
flush_block(x1, y1, x2, y2);
}
#ifdef USE_MOUSE
int test_button(int x, int y, char *s) {
int len = strlen(s);
int x1, y1, x2, y2;
x1 = x - 3;
y1 = y - 3;
x2 = x + CHAR_COLS * len + 2;
y2 = y + CHAR_LINES + 2;
if ((int)mouse.x >= x1 && (int)mouse.y >= y1
&& (int)mouse.x <= x2 && (int)mouse.y <= y2)
return true;
return false;
}
#endif
void put_block(int x1, int y1, int x2, int y2) {
gfx_putblock(x1, y1, x2, y2);
}
void put_screen() {
put_block(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1);
}
void poll_timer() {
agi_timer_low();
}
int get_key() {
return agi_get_keypress_low();
}
int keypress() {
return agi_is_keypress_low();
}
/*
* Public functions
*/
/**
* Initialize the color palette
* This function initializes the color palette using the specified 16-color
* RGB palette and creates 16 extra palette entries with translucent colors
* for the interpreter console.
* @param p A pointer to the 16-color RGB palette.
*/
void init_palette(uint8 *p) {
int i;
for (i = 0; i < 48; i++) {
palette[i] = p[i];
palette[i + 48] = (p[i] + 0x30) >> 2;
}
}
void gfx_set_palette() {
int i;
byte pal[32 * 4];
for (i = 0; i < 32; i++) {
pal[i * 4 + 0] = palette[i * 3 + 0] << 2;
pal[i * 4 + 1] = palette[i * 3 + 1] << 2;
pal[i * 4 + 2] = palette[i * 3 + 2] << 2;
pal[i * 4 + 3] = 0;
}
g_system->setPalette(pal, 0, 32);
}
void gfx_putpixels(int x, int y, int w, uint8 *p) {
uint8 *p0 = screen + x + y * 320;
memcpy(p0, p, w);
}
/* put a block onto the screen */
void gfx_putblock(int x1, int y1, int x2, int y2) {
if (x1 >= GFX_WIDTH)
x1 = GFX_WIDTH - 1;
if (y1 >= GFX_HEIGHT)
y1 = GFX_HEIGHT - 1;
if (x2 >= GFX_WIDTH)
x2 = GFX_WIDTH - 1;
if (y2 >= GFX_HEIGHT)
y2 = GFX_HEIGHT - 1;
// force full update until fix wrong partial updates
g_system->copyRectToScreen(screen, 320, 0, 0, 320, 200);
//g_system->copyRectToScreen(screen, 320, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
//g_system->updateScreen();
}
static const byte mouseCursorArrow[] = {
0x00, 0x00, 0x40, 0x00, 0x60, 0x00, 0x70, 0x00,
0x78, 0x00, 0x7C, 0x00, 0x7E, 0x00, 0x7F, 0x00,
0x7F, 0x80, 0x7C, 0x00, 0x6C, 0x00, 0x46, 0x00,
0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
0xC0, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0xF8, 0x00,
0xFC, 0x00, 0xFE, 0x00, 0xFF, 0x00, 0xFF, 0x80,
0xFF, 0xC0, 0xFF, 0xC0, 0xFE, 0x00, 0xFF, 0x00,
0xCF, 0x00, 0x07, 0x80, 0x07, 0x80, 0x03, 0x80
};
/**
* Initialize graphics device.
*
* @see deinit_video()
*/
int init_video() {
if (opt.egapal)
init_palette(ega_palette);
else
init_palette(new_palette);
init_console();
if ((agi_screen = (uint8 *)calloc(GFX_WIDTH, GFX_HEIGHT)) == NULL)
return err_NotEnoughMemory;
#ifdef USE_CONSOLE
if ((console_screen = (uint8 *)calloc(GFX_WIDTH, GFX_HEIGHT)) == NULL) {
free(agi_screen);
return err_NotEnoughMemory;
}
#endif
gfx_set_palette();
byte mouseCursor[16 * 16];
const byte *src = mouseCursorArrow;
for (int i = 0; i < 32; ++i) {
int offs = i * 8;
for (byte mask = 0x80; mask != 0; mask >>= 1) {
if (src[0] & mask) {
mouseCursor[offs] = 2;
} else if (src[32] & mask) {
mouseCursor[offs] = 0;
} else {
mouseCursor[offs] = 0xFF;
}
++offs;
}
++src;
}
g_system->setMouseCursor(mouseCursor, 16, 16, 1, 1);
return err_OK;
}
/**
* Deinitialize graphics device.
*
* @see init_video()
*/
int deinit_video() {
free(agi_screen);
#ifdef USE_CONSOLE
free(console_screen);
#endif
return err_OK;
}
int init_machine() {
screen = (unsigned char *)malloc(320 * 200);
clock_count = 0;
clock_ticks = 0;
return err_OK;
}
int deinit_machine() {
free(screen);
return err_OK;
}
/**
* Write pixels on the output device.
* This function writes a row of pixels on the output device. Only the
* lower 4 bits of each pixel in the row will be used, making this
* function suitable for use with rows from the AGI screen.
* @param x x coordinate of the row start (AGI coord.)
* @param y y coordinate of the row start (AGI coord.)
* @param n number of pixels in the row
* @param p pointer to the row start in the AGI screen
*/
void put_pixels_a(int x, int y, int n, uint8 *p) {
if (opt.cgaemu) {
for (x *= 2; n--; p++, x += 2) {
register uint16 q = (cga_map[(*p & 0xf0) >> 4] << 4) | cga_map[*p & 0x0f];
#ifdef USE_CONSOLE
if (debug_.priority)
q >>= 4;
#endif
*(uint16 *)&agi_screen[x + y * GFX_WIDTH] = q & 0x0f0f;
}
} else {
for (x *= 2; n--; p++, x += 2) {
register uint16 q = ((uint16) * p << 8) | *p;
#ifdef USE_CONSOLE
if (debug_.priority)
q >>= 4;
#endif
*(uint16 *)&agi_screen[x + y * GFX_WIDTH] = q & 0x0f0f;
}
}
}
#ifdef USE_HIRES
void put_pixels_hires(int x, int y, int n, uint8 *p) {
//y += CHAR_LINES;
for (; n--; p++, x++) {
uint8 q = *p;
#ifdef USE_CONSOLE
if (debug_.priority)
q >>= 4;
#endif
agi_screen[x + y * GFX_WIDTH] = q & 0x0f;
}
}
#endif
/**
* Schedule blocks for blitting on the output device.
* This function gets the coordinates of a block in the AGI screen and
* schedule it to be updated in the output device.
* @param x1 x coordinate of the upper left corner of the block (AGI coord.)
* @param y1 y coordinate of the upper left corner of the block (AGI coord.)
* @param x2 x coordinate of the lower right corner of the block (AGI coord.)
* @param y2 y coordinate of the lower right corner of the block (AGI coord.)
*
* @see do_update()
*/
void schedule_update(int x1, int y1, int x2, int y2) {
if (x1 < update.x1)
update.x1 = x1;
if (y1 < update.y1)
update.y1 = y1;
if (x2 > update.x2)
update.x2 = x2;
if (y2 > update.y2)
update.y2 = y2;
}
/**
* Update scheduled blocks on the output device.
* This function exposes the blocks scheduled for updating to the output
* device. Blocks can be scheduled at any point of the AGI cycle.
*
* @see schedule_update()
*/
void do_update() {
if (update.x1 <= update.x2 && update.y1 <= update.y2) {
gfx_putblock(update.x1, update.y1, update.x2, update.y2);
}
/* reset update block variables */
update.x1 = MAX_INT;
update.y1 = MAX_INT;
update.x2 = 0;
update.y2 = 0;
}
/**
* Updates a block of the framebuffer with contents of the AGI engine screen.
* This function updates a block in the output device with the contents of
* the AGI engine screen, handling console transparency.
* @param x1 x coordinate of the upper left corner of the block
* @param y1 y coordinate of the upper left corner of the block
* @param x2 x coordinate of the lower right corner of the block
* @param y2 y coordinate of the lower right corner of the block
*
* @see flush_block_a()
*/
void flush_block(int x1, int y1, int x2, int y2) {
int y, w;
uint8 *p0;
schedule_update(x1, y1, x2, y2);
p0 = &agi_screen[x1 + y1 * GFX_WIDTH];
w = x2 - x1 + 1;
for (y = y1; y <= y2; y++) {
put_pixels(x1, y, w, p0);
p0 += GFX_WIDTH;
}
}
/**
* Updates a block of the framebuffer receiving AGI picture coordinates.
* @param x1 x AGI picture coordinate of the upper left corner of the block
* @param y1 y AGI picture coordinate of the upper left corner of the block
* @param x2 x AGI picture coordinate of the lower right corner of the block
* @param y2 y AGI picture coordinate of the lower right corner of the block
*
* @see flush_block()
*/
void flush_block_a(int x1, int y1, int x2, int y2) {
//y1 += 8;
//y2 += 8;
flush_block(DEV_X0(x1), DEV_Y(y1), DEV_X1(x2), DEV_Y(y2));
}
/**
* Updates the framebuffer with contents of the AGI engine screen (console-aware).
* This function updates the output device with the contents of the AGI
* screen, handling console transparency.
*/
void flush_screen() {
flush_block(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1);
}
/**
* Clear the output device screen (console-aware).
* This function clears the output device screen and updates the
* output device. Contents of the AGI screen are left untouched. This
* function can be used to simulate a switch to a text mode screen in
* a graphic-only device.
* @param c color to clear the screen
*/
void clear_screen(int c) {
memset(agi_screen, c, GFX_WIDTH * GFX_HEIGHT);
flush_screen();
}
#ifdef USE_CONSOLE
/**
* Clear the console screen.
* This function clears the top n lines of the console screen.
* @param n number of lines to clear (in pixels)
*/
void clear_console_screen(int n) {
memset(console_screen + n * GFX_WIDTH, 0, (GFX_HEIGHT - n) * GFX_WIDTH);
}
#endif
/**
* Save a block of the AGI engine screen
*/
void save_block(int x1, int y1, int x2, int y2, uint8 *b) {
uint8 *p0;
int w, h;
p0 = &agi_screen[x1 + GFX_WIDTH * y1];
w = x2 - x1 + 1;
h = y2 - y1 + 1;
while (h--) {
memcpy(b, p0, w);
b += w;
p0 += GFX_WIDTH;
}
}
/**
* Restore a block of the AGI engine screen
*/
void restore_block(int x1, int y1, int x2, int y2, uint8 *b) {
uint8 *p0;
int w, h;
p0 = &agi_screen[x1 + GFX_WIDTH * y1];
w = x2 - x1 + 1;
h = y2 - y1 + 1;
while (h--) {
memcpy(p0, b, w);
b += w;
p0 += GFX_WIDTH;
}
flush_block(x1, y1, x2, y2);
}
} // End of namespace Agi

86
engines/agi/graphics.h Normal file
View File

@ -0,0 +1,86 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_GRAPHICS_H
#define __AGI_GRAPHICS_H
#include "common/stdafx.h"
#include "agi/agi.h"
namespace Agi {
#define GFX_WIDTH 320
#define GFX_HEIGHT 200
#define CHAR_COLS 8
#define CHAR_LINES 8
extern uint8 palette[];
/* Transparent layer */
extern uint8 layer1_data[];
extern uint8 layer2_data[];
void gfx_putpixels(int x, int y, int w, uint8 * p);
void gfx_putblock(int x1, int y1, int x2, int y2);
void put_text_character(int, int, int, unsigned int, int, int);
void shake_screen(int);
void shake_start(void);
void shake_end(void);
void save_screen(void);
void restore_screen(void);
int init_video(void);
int deinit_video(void);
void schedule_update(int, int, int, int);
void do_update(void);
void put_screen(void);
void flush_block(int, int, int, int);
void flush_block_a(int, int, int, int);
void put_pixels_a(int, int, int, uint8 *);
void flush_screen(void);
void clear_screen(int);
void clear_console_screen(int);
void draw_box(int, int, int, int, int, int, int);
void draw_button(int, int, char *, int, int);
int test_button(int, int, char *);
void draw_rectangle(int, int, int, int, int);
void save_block(int, int, int, int, uint8 *);
void restore_block(int, int, int, int, uint8 *);
void init_palette(uint8 *);
void put_pixel(int, int, int);
#ifdef USE_HIRES
void put_pixels_hires(int x, int y, int n, uint8 * p);
#endif
int keypress(void);
int get_key(void);
void print_character(int, int, char, int, int);
void poll_timer(void);
} // End of namespace Agi
#endif /* __AGI_GRAPHICS_H */

475
engines/agi/id.cpp Normal file
View File

@ -0,0 +1,475 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/opcodes.h"
namespace Agi {
/*
* Determine what AGI v2 system to emulate, these are the major version
* to emulate, thus 2.915 comes under 2.917, 2.4xxx is 2.440, etc.
*
* 0x2089
* 0x2272
* 0x2440
* 0x2917
* 0x2936
*/
const char *ids_database = "\
# CRC Int Ver [options] Game name # Comment \n\
\n\
#---------------------------------------------------------------------------- \n\
# PC-DOS versions \n\
#---------------------------------------------------------------------------- \n\
\n\
0x484AA 0x2440 AGI Demo 1 (PC) 05/87 [AGI 2.425] # A.K.A. Demo 5 \n\
0x8CC43 0x2917 AGI Demo 2 (PC 5.25) 11/87 [v1] [AGI 2.915] # Demo 1 \n\
0x8856C 0x2917 AGI Demo 2 (PC 3.5) 11/87 [AGI 2.915] \n\
0x843AC 0x2917 AGI Demo 2 (PC 5.25) 01/88 [v2] [AGI 2.917] # Demo 3 \n\
0x89592 0x3149 AGI Demo 3 (PC) 09/88 [AGI 3.002.102] # Demo 4 \n\
0x24A18 0x2440 Black Cauldron (PC) 2.00 6/14/87 [AGI 2.439] \n\
0x22B50 0x3149 Black Cauldron (PC 5.25) 2.10 11/10/88 [AGI 3.002.098] \n\
0x23E0E 0x3149 Black Cauldron (PC 3.5) 2.10 11/10/88 [AGI 3.002.098] \n\
0xB5A25 0x3149 Gold Rush! (PC 5.25) 2.01 12/22/88 [AGI 3.002.149] \n\
0xB1C9E 0x3149 Gold Rush! (PC 3.5) 2.01 12/22/88 [AGI 3.002.149] \n\
0x49EDA 0x2917 King's Quest 1 (PC 5.25/3.5) 2.0F [AGI 2.917] # also 2.425 \n\
0x633CB 0x2440 King's Quest 2 (PC 5.25/3.5) 2.1 [AGI 2.411] \n\
0x63338 0x2917 King's Quest 2 (PC 5.25/3.5) 2.2 [AGI 2.426] # also 2.917 \n\
0x88673 0x2272 King's Quest 3 (PC) 1.01 11/08/86 [AGI 2.272] \n\
0x840D3 0x2440 King's Quest 3 (PC 5.25) 2.00 5/25/87 [AGI 2.435] \n\
0x83191 0x2440 King's Quest 3 (PC 3.5) 2.00 5/25/87 [AGI 2.435] \n\
0x83695 0x2936 King's Quest 3 (PC 3.5) 2.14 3/15/88 [AGI 2.936] \n\
0x8410B 0x2936 King's Quest 3 (PC 5.25) 2.14 3/15/88 [AGI 2.936] \n\
0xB124B 0x3086 King's Quest 4 (PC 3.5) 2.0 7/27/88 [AGI 3.002.086] \n\
0xB291F 0x3086 King's Quest 4 (PC 3.5) 2.2 9/27/88 [AGI 3.002.086] \n\
0xB3722 0x3086 King's Quest 4 (PC 5.25) 2.3 9/27/88 [AGI 3.002.086] \n\
0x9CB15 0x3149 King's Quest 4 demo (PC) [AGI 3.002.102] \n\
0x6F5E1 0x2440 Leisure Suit Larry 1 (PC 5.25/3.5) 1.00 6/1/87 [AGI 2.440] \n\
0x4C16D 0x3149 Manhunter NY (PC 5.25) 1.22 8/31/88 [AGI 3.002.107] # also 3.003.102 \n\
0x49687 0x3149 Manhunter NY (PC 3.5) 1.22 8/31/88 [AGI 3.002.102] \n\
0x53971 0x3149 Manhunter SF (PC 3.5) 3.02 7/26/89 [AGI 3.002.149] \n\
0x584F9 0x3149 Manhunter SF (PC 5.25) 3.03 8/17/89 [AGI 3.002.149] \n\
0x5D77C 0x2917 Mixed-Up Mother Goose (PC) [AGI 2.915] \n\
0x5D7C6 0x2917 Mixed Up Mother Goose (PC) [AGI 2.915] (Broken) \n\
0x7F18B 0x2917 Police Quest 1 (PC) 2.0A 10/23/87 [AGI 2.903/2.911] \n\
0x7EF35 0x2917 Police Quest 1 (PC) 2.0E 11/17/87 [AGI 2.915] \n\
0x7EF06 0x2917 Police Quest 1 (PC 5.25/ST) 2.0G 12/03/87 [AGI 2.917] \n\
0x7E0BC 0x2917 Police Quest 1 (PC 3.5) 2.0G 12/03/87 [AGI 2.917] \n\
0x67FCC 0x2089 Space Quest 1 (PC) 1.0 [AGI 2.089] \n\
0x68036 0x2089 Space Quest 1 (PC) 1.0X [AGI 2.089] \n\
0x67F6E 0x2272 Space Quest 1 (PC) 1.1A [AGI 2.272] \n\
0x68244 0x2440 Space Quest 1 (PC 5.25/3.5) 2.2 [AGI 2.426/2.917] \n\
0x8DB32 0x2917 Space Quest 2 (PC 5.25) 2.0A [AGI 2.912] \n\
0x8D825 0x2917 Space Quest 2 (PC 3.5) 2.0A [AGI 2.912] \n\
0x8DA3E 0x2917 Space Quest 2 (PC 5.25/ST) 2.0C/A [AGI 2.915] \n\
0x8E6A7 0x2917 Space Quest 2 (PC 3.5) 2.0C/B [AGI 2.917] \n\
0x8E29B 0x2936 Space Quest 2 (PC 3.5) 2.0F [AGI 2.936] \n\
0x8DF84 0x2936 Space Quest 2 (PC 5.25) 2.0D [AGI 2.936] \n\
0x8DE46 0x2936 Space Quest 2 (PC 3.5) 2.0D [AGI 2.936] \n\
0x8E310 0x2936 Space Quest 2 (PC 5.25) 2.0F [AGI 2.936] \n\
0x31677 0x2272 Xmas Card 1986 (PC) [AGI 2.272] \n\
\n\
#---------------------------------------------------------------------------- \n\
# Apple //gs versions \n\
# all guessed interpreter versions \n\
# \n\
# Notes: \n\
# - (CE) in Apple IIgs versions stands for Carlos Escobar --PDD \n\
#---------------------------------------------------------------------------- \n\
\n\
0x93260 0x2917 AGI Demo 2 (IIgs) 1.0C (Censored) \n\
0x285FB 0x3149 Black Cauldron (Apple IIgs) 1.0O 2/24/89 (CE) # 2.24.89 (CE) \n\
0xB6F67 0x3149 Gold Rush! (Apple IIgs) 1.0M 2/28/89 (CE) aka 2.01 12/22/88 \n\
0x4A9E8 0x2272 King's Quest 1 (IIgs) 1.0S-88223 \n\
0x79D1B 0x2917 King's Quest 2 (IIgs) 2.0A 6/16/88 (CE) \n\
0x85CD4 0x2917 King's Quest 3 (IIgs) 2.0A 8/28/88 (CE) \n\
0xAF778 0x3086 King's Quest 4 (IIgs) 1.0K 11/22/88 (CE) \n\
0x6E41E 0x2440 Leisure Suit Larry 1 (IIgs) 1.0E \n\
0x4C705 0x3149 Manhunter NY (IIgs) 2.0E 10/05/88 (CE) \n\
0x5F4E8 0x2917 Mixed Up Mother Goose (IIgs) \n\
0x7DB3F 0x2917 Police Quest 1 (IIgs) 2.0A-88318 \n\
0x7DBE5 0x2917 Police Quest 1 (IIgs) 2.0B-88421 \n\
0x69EC0 0x2917 Space Quest 1 (IIgs) 2.2 \n\
0x8E983 0x2936 Space Quest 2 (IIgs) 2.0A 7/25/88 (CE) \n\
\n\
#---------------------------------------------------------------------------- \n\
# Macintosh versions \n\
# all guessed interpreter versions \n\
#---------------------------------------------------------------------------- \n\
\n\
0x4C02C 0x2440 King's Quest 1 (Mac) 2.0C \n\
0x6382E 0x2440 King's Quest 2 (Mac) 2.0R \n\
0x8410B 0x2440 King's Quest 3 (Mac) 2.14 3/15/88 \n\
0x78202 0x2440 Leisure Suit Larry 1 (Mac) 1.05 6/26/87 \n\
0x7EF06 0x2440 Police Quest 1 (Mac) 2.0G 12/3/87 \n\
0x6A277 0x2440 Space Quest 1 (Mac) 1.5D \n\
0x8DF84 0x2936 Space Quest 2 (Mac) 2.0D \n\
\n\
#---------------------------------------------------------------------------- \n\
# Atari ST versions \n\
# all guessed interpreter versions \n\
# \n\
# Notes: \n\
# - Chris Iden wrote the Atari ST port of AGI --PDD \n\
#---------------------------------------------------------------------------- \n\
\n\
0x1A7AF 0x2272 Donald Duck's Playground (ST) 1.0A 8/8/86 \n\
0x4A079 0x2272 King's Quest 1 (ST) 1.0V \n\
0x882B7 0x2272 King's Quest 3 (ST) 1.02 11/18/86 \n\
0xB68AB 0x3149 Gold Rush! (ST) 1.01 1/13/89 aka 2.01 12/22/88 \n\
0x6F7E1 0x2440 Leisure Suit Larry 1 (ST) 1.04 6/18/87 \n\
0x4CE19 0x3149 Manhunter NY (ST) 1.03 10/20/88 \n\
0x5360B 0x3149 Manhunter SF (ST) 1.0 7/29/89 \n\
0x69597 0x2440 Space Quest 1 (ST) 1.1A \n\
\n\
#---------------------------------------------------------------------------- \n\
# Amiga versions \n\
# Use option -A- to enable padding \n\
# \n\
# Notes: \n\
# - Amiga KQ3 (2.333) seems to need interpreter version 3.002.086 \n\
#---------------------------------------------------------------------------- \n\
\n\
0x25640 0x2440 [A] Black Cauldron (Amiga) 2.00 6/14/87 # guessed int \n\
0x1AFBA 0x2272 [A] Donald Duck's Playground (Amiga) 1.0C # guessed int \n\
0xB3E1A 0x3149 [A] Gold Rush! (Amiga) 1.01 1/13/89 aka 2.05 3/9/89 # 2.316 \n\
0x49C6B 0x2440 [A] King's Quest 1 (Amiga) 1.0U # 2.082 \n\
0x5D395 0x2440 [A] King's Quest 2 (Amiga) 2.0J # guessed int \n\
0x5BCE6 0x2440 [A] King's Quest 2 (Amiga) 2.0J (Broken) \n\
0x5F4B9 0x2440 [A] King's Quest 2 (Amiga) 2.0J (Broken) # 2.176 \n\
0x888C1 0x2440 [A] King's Quest 3 (Amiga) 1.01 11/8/86 \n\
0x84793 0x3086 [A] King's Quest 3 (Amiga) 2.15 11/15/89 # 2.333 \n\
0x6FDDB 0x2440 [A] Leisure Suit Larry 1 (Amiga) 1.05 6/26/87 # x.yyy \n\
0x4BA94 0x3149 [A] Manhunter NY (Amiga) 1.06 3/18/89 # x.yyy \n\
0x53D51 0x3086 [A] Manhunter SF (Amiga) 3.06 8/17/89 # 2.333 \n\
0x5CFB1 0x3086 [A] Mixed-Up Mother Goose (Amiga) 1.1 # guessed int \n\
0x7F752 0x3149 [A] Police Quest 1 (Amiga) 2.0B 2/22/89 # 2.310 \n\
0x696DD 0x2440 [A] Space Quest 1 (Amiga) 1.2 # 2.082 \n\
0x8FEA6 0x2936 [A] Space Quest 2 (Amiga) 2.0F # 2.202 \n\
\n\
#---------------------------------------------------------------------------- \n\
# CoCo versions \n\
# what version of DOS AGI does CoCo 2.023 correspond with? \n\
# guessing 2.272 because the PC version 1.0 is 2.272; doesn't fit date though \n\
# \n\
# Notes: \n\
# - Chris Iden wrote the CoCo port of AGI --PDD \n\
#---------------------------------------------------------------------------- \n\
\n\
0x7CBE8 0x2272 King's Quest 3 (CoCo3) 1.0C 6/27/88 # 2.023 \n\
0x70D35 0x2440 Leisure Suit Larry 1 (CoCo3) \n\
\n\
#---------------------------------------------------------------------------- \n\
# AGDS games \n\
# Use option -a- for AGDS games \n\
#---------------------------------------------------------------------------- \n\
\n\
0x5501A 0x2440 [a] Groza # AGDS sample game \n\
\n\
#---------------------------------------------------------------------------- \n\
# Fan-made AGI games \n\
#---------------------------------------------------------------------------- \n\
\n\
0x3F2F7 0x2917 [m] AGI Mouse 0.7 Demo \n\
0x3F744 0x2917 [m] AGI Mouse 1.0 Demo # 2.917 6/24/00 \n\
0x3F74F 0x2917 [m] AGI Mouse 1.1 Demo # 2.917 1/01/01 \n\
0x17599 0x2917 [m] Sliding Tile Game v1.00 # 2.917 6/02/01 \n\
0x785c4 0x2936 [m] Jolimie v0.6 # 2.936 2000 \n\
#Jolimie uses AGIPal only and not AGIMouse; no way to separate these currently \n\
0x40D80 0x2440 AGI Trek # 2.440 9/21/98 \n\
0x64CB7 0x2440 Space Trek 1.0 # 2.440 12/13/98 \n\
0x6596A 0x2917 Space Trek (remake) # 2.917 6/09/99 \n\
0x96909 0x2917 Operation: RECON teaser 1.1 # \n\
0x185A6 0x2917 AGI Piano v1.0 # ? 1998 \n\
0x91ACF 0x2917 Dave's Quest .07 # ? \n\
0x620F6 0x2917 Time Quest demo D0.2 # ? 1998 \n\
0x7466F 0x2917 Tex McPhilip I # ? 2000 \n\
0x9E400 0x2917 Tex McPhilip II # ? 2000 \n\
0xAB9A8 0x2917 Justin Quest 1.0 # 2.917 \n\
0x7D473 0x2917 The Ruby Cast demo 0.2 # 2.917 1998 \n\
0xB4D7A 0x2917 Residence 44 Quest 1.0a # 2.917 1999 \n\
0x5D077 0x2917 Escape Quest demo # ? 1998 \n\
0x5A434 0x2917 Acidopolis (1.0) demo # ? \n\
0x45CDF 0x2917 Go West, Young Hippie demo # 2.917 \n\
0x4C9DC 0x2917 Speeder Bike Challenge v1.0 # ? \n\
0x112BF9 0x2917 Space Quest 0: Replicated 1.04 # 6/27/2003 \n\
0x6E70F 0x2917 Space Quest: The Lost Chapter v10.0 \n\
0x5859E 0x2917 Phantasmagoria \n\
0x7B5DF 0x2917 Dashiki demo \n\
0x9405B 0x2917 Dashiki 256-color demo (Unsupported) \n\
0x6ADCD 0x2917 Jen's Quest 0.1 demo \n\
0x4EE64 0x2917 Monkey Man \n\
";
int setup_v2_game(int ver, uint32 crc);
int setup_v3_game(int ver, uint32 crc);
int v4id_game(uint32 crc);
uint32 match_crc(uint32 crc, char *name, int len) {
char *c, *t, buf[256];
uint32 id, ver;
Common::MemoryReadStream f((const byte *)ids_database, strlen(ids_database));
while (!f.eos()) {
f.readLine(buf, 256);
c = strchr(buf, '#');
if (c)
*c = 0;
/* Remove spaces at end of line */
if (strlen(buf)) {
for (c = buf + strlen(buf) - 1;
*c == ' ' || *c == '\t'; *c-- = 0) {
}
}
t = strtok(buf, " \t\r\n");
if (t == NULL)
continue;
id = strtoul(t, NULL, 0);
t = strtok(NULL, " \t\r\n");
if (t == NULL)
continue;
ver = strtoul(t, NULL, 0);
t = strtok(NULL, "\n\r");
for (; *t == ' ' || *t == '\t'; t++);
if (id == crc) {
/* Now we must check options enclosed in brackets
* like [A] for Amiga
*/
if (*t == '[') {
while (*t != ']') {
switch (*t++) {
case 'A':
opt.amiga = true;
break;
case 'a':
opt.agds = true;
break;
#ifdef USE_MOUSE
case 'm':
opt.agimouse = true;
break;
#endif
}
}
t++;
for (; (*t == ' ' || *t == '\t') && *t; t++) {
}
}
strncpy(name, t, len);
return ver;
}
}
return 0;
}
static uint32 match_version(uint32 crc) {
int ver;
char name[80];
if ((ver = match_crc(crc, name, 80)) > 0)
report("AGI game detected: %s\n\n", name);
return ver;
}
int v2id_game() {
int y, ver;
uint32 len, c, crc;
uint8 *buff;
Common::File fp;
char *fn[] = { "viewdir", "logdir", "picdir", "snddir", "words.tok", "object", "" };
buff = (uint8 *)malloc(8192);
for (crc = y = 0; fn[y][0]; y++) {
char *path = fn[y];
if (fp.open(path)) {
for (len = 1; len > 0;) {
memset(buff, 0, 8192);
len = fp.read(buff, 8000);
for (c = 0; c < len; c++)
crc += *(buff + c);
}
fp.close();
}
}
free(buff);
report("Computed CRC: 0x%05x\n", crc);
ver = match_version(crc);
game.crc = crc;
game.ver = ver;
debugC(2, kDebugLevelMain, "game.ver = 0x%x", game.ver);
agi_set_release(ver);
return setup_v2_game(ver, crc);
}
/*
* Currently, there is no known difference between v3.002.098 -> v3.002.149
* So version emulated;
*
* 0x0086,
* 0x0149
*/
int v3id_game() {
int ec = err_OK, y, ver;
uint32 len, c, crc;
uint8 *buff;
Common::File fp;
char *fn[] = { "words.tok", "object", "" };
Common::String path;
buff = (uint8 *)malloc(8192);
for (crc = 0, y = 0; fn[y][0] != 0x0; y++) {
path = fn[y];
if (fp.open(path)) {
len = 1;
while (len > 0) {
memset(buff, 0, 8192);
len = fp.read(buff, 8000);
for (c = 0; c < len; c++)
crc += *(buff + c);
}
fp.close();
}
}
/* now do the directory file */
path = Common::String(game.name) + DIR_;
if (fp.open(path)) {
for (len = 1; len > 0;) {
memset(buff, 0, 8192);
len = fp.read(buff, 8000);
for (c = 0; c < len; c++)
crc += *(buff + c);
}
fp.close();
}
free(buff);
report("Computed CRC: 0x%05x\n", crc);
ver = match_version(crc);
game.crc = crc;
game.ver = ver;
agi_set_release(ver);
ec = setup_v3_game(ver, crc);
return ec;
}
/**
*
*/
int setup_v2_game(int ver, uint32 crc) {
int ec = err_OK;
if (ver == 0) {
report("Unknown v2 Sierra game: %08x\n\n", crc);
agi_set_release(0x2917);
}
/* setup the differences in the opcodes and other bits in the
* AGI v2 specs
*/
if (opt.emuversion)
agi_set_release(opt.emuversion);
if (opt.agds)
agi_set_release(0x2440); /* ALL AGDS games built for 2.440 */
switch (agi_get_release()) {
case 0x2089:
logic_names_cmd[0x86].num_args = 0; /* quit: 0 args */
logic_names_cmd[0x97].num_args = 3; /* print.at: 3 args */
logic_names_cmd[0x98].num_args = 3; /* print.at.v: 3 args */
break;
case 0x2272:
/* KQ3 0x88673 (2.272) requires print.at with 4 arguments */
break;
case 0x2440:
break;
case 0x2917:
break;
case 0x2936:
break;
default:
report("** Cannot setup for unknown version\n");
ec = err_UnknownAGIVersion;
break;
}
return ec;
}
/**
*
*/
int setup_v3_game(int ver, uint32 crc) {
int ec = err_OK;
if (ver == 0) {
report("Unknown v3 Sierra game: %08x\n\n", crc);
agi_set_release(ver = 0x3149);
}
if (opt.emuversion)
agi_set_release(ver = opt.emuversion);
switch (ver) {
case 0x3086:
logic_names_cmd[0xad].num_args = 1; /* 173 : 1 args */
break;
case 0x3149:
logic_names_cmd[0xad].num_args = 0; /* 173 : 0 args */
break;
default:
report("Error: cannot setup for unknown version\n");
ec = err_UnknownAGIVersion;
break;
}
return ec;
}
} // End of namespace Agi

212
engines/agi/inv.cpp Normal file
View File

@ -0,0 +1,212 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/sprite.h"
#include "agi/graphics.h"
#include "agi/keyboard.h"
#include "agi/text.h"
#include "agi/keyboard.h"
namespace Agi {
/*
* Messages and coordinates
*/
#define NOTHING_X 16
#define NOTHING_Y 3
#define NOTHING_MSG "nothing"
#define ANY_KEY_X 4
#define ANY_KEY_Y 24
#define ANY_KEY_MSG "Press a key to return to the game"
#define YOUHAVE_X 11
#define YOUHAVE_Y 0
#define YOUHAVE_MSG "You are carrying:"
#define SELECT_X 2
#define SELECT_Y 24
#define SELECT_MSG "Press ENTER to select, ESC to cancel"
static uint8 *intobj = NULL;
static void print_item(int n, int fg, int bg)
{
print_text(object_name(intobj[n]), 0, n % 2 ? 39 - strlen(object_name(intobj[n])) : 1,
(n / 2) + 2, 40, fg, bg);
}
#ifdef USE_MOUSE
static int find_item() {
int r, c;
r = mouse.y / CHAR_LINES;
c = mouse.x / CHAR_COLS;
debugC(6, kDebugLevelInventory, "r = %d, c = %d", r, c);
if (r < 2)
return -1;
return (r - 2) * 2 + (c > 20);
}
#endif
static int show_items() {
unsigned int x, i;
for (x = i = 0; x < game.num_objects; x++) {
if (object_get_location(x) == EGO_OWNED) {
/* add object to our list! */
intobj[i] = x;
print_item(i, STATUS_FG, STATUS_BG);
i++;
}
}
if (i == 0) {
print_text(NOTHING_MSG, 0, NOTHING_X, NOTHING_Y, 40, STATUS_FG, STATUS_BG);
}
return i;
}
static void select_items(int n) {
int fsel = 0;
while (42) {
if (n > 0)
print_item(fsel, STATUS_BG, STATUS_FG);
switch (wait_any_key()) {
case KEY_ENTER:
setvar(V_sel_item, intobj[fsel]);
goto exit_select;
case KEY_ESCAPE:
setvar(V_sel_item, 0xff);
goto exit_select;
case KEY_UP:
if (fsel >= 2)
fsel -= 2;
break;
case KEY_DOWN:
if (fsel + 2 < n)
fsel += 2;
break;
case KEY_LEFT:
if (fsel % 2 == 1)
fsel--;
break;
case KEY_RIGHT:
if (fsel % 2 == 0 && fsel + 1 < n)
fsel++;
break;
#ifdef USE_MOUSE
case BUTTON_LEFT:{
int i = find_item();
if (i >= 0 && i < n) {
setvar(V_sel_item, intobj[fsel = i]);
debugC(6, kDebugLevelInventory, "item found: %d", fsel);
show_items();
print_item(fsel, STATUS_BG, STATUS_FG);
do_update();
goto exit_select;
}
break;
}
#endif
default:
break;
}
show_items();
do_update();
}
exit_select:
debugC(6, kDebugLevelInventory, "selected: %d", fsel);
}
/*
* Public functions
*/
/**
* Display inventory items.
*/
void inventory() {
int old_fg, old_bg;
int n;
/* screen is white with black text */
old_fg = game.color_fg;
old_bg = game.color_bg;
game.color_fg = 0;
game.color_bg = 15;
clear_screen(game.color_bg);
print_text(YOUHAVE_MSG, 0, YOUHAVE_X, YOUHAVE_Y, 40, STATUS_FG, STATUS_BG);
/* FIXME: doesn't check if objects overflow off screen... */
intobj = (uint8 *) malloc(4 + game.num_objects);
memset(intobj, 0, (4 + game.num_objects));
n = show_items();
if (getflag(F_status_selects_items)) {
print_text(SELECT_MSG, 0, SELECT_X, SELECT_Y, 40, STATUS_FG, STATUS_BG);
} else {
print_text(ANY_KEY_MSG, 0, ANY_KEY_X, ANY_KEY_Y, 40, STATUS_FG, STATUS_BG);
}
flush_screen();
/* If flag 13 is set, we want to highlight & select an item.
* opon selection, put objnum in var 25. Then on esc put in
* var 25 = 0xff.
*/
if (getflag(F_status_selects_items))
select_items(n);
free(intobj);
if (!getflag(F_status_selects_items))
wait_any_key();
clear_screen(0);
write_status();
show_pic();
game.color_fg = old_fg;
game.color_bg = old_bg;
game.has_prompt = 0;
flush_lines(game.line_user_input, 24);
}
} // End of namespace Agi

388
engines/agi/keyboard.cpp Normal file
View File

@ -0,0 +1,388 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/graphics.h"
#include "agi/keyboard.h"
#include "agi/menu.h"
#include "agi/text.h" /* remove later */
namespace Agi {
char last_sentence[40];
#ifdef USE_CONSOLE
extern struct agi_console console;
#endif
/* FIXME */
extern int open_dialogue;
struct string_data {
int x;
int y;
int len;
int str;
};
struct string_data stringdata;
/*
* IBM-PC keyboard scancodes
*/
uint8 scancode_table[26] = {
30, /* A */
48, /* B */
46, /* C */
32, /* D */
18, /* E */
33, /* F */
34, /* G */
35, /* H */
23, /* I */
36, /* J */
37, /* K */
38, /* L */
50, /* M */
49, /* N */
24, /* O */
25, /* P */
16, /* Q */
19, /* R */
31, /* S */
20, /* T */
22, /* U */
47, /* V */
17, /* W */
45, /* X */
21, /* Y */
44 /* Z */
};
void init_words() {
game.num_ego_words = 0;
}
void clean_input() {
while (game.num_ego_words)
free(game.ego_words[--game.num_ego_words].word);
}
void get_string(int x, int y, int len, int str) {
new_input_mode(INPUT_GETSTRING);
stringdata.x = x;
stringdata.y = y;
stringdata.len = len;
stringdata.str = str;
}
/**
* Raw key grabber.
* poll_keyboard() is the raw key grabber (above the gfx driver, that is).
* It handles console keys and insulates AGI from the console. In the main
* loop, handle_keys() handles keyboard input and ego movement.
*/
int do_poll_keyboard() {
int key = 0;
/* If a key is ready, rip it */
if (keypress()) {
key = get_key();
debugC(3, kDebugLevelInput, "key %02x pressed", key);
}
return key;
}
int handle_controller(int key) {
struct vt_entry *v = &game.view_table[0];
int i;
/* The Black Cauldron needs KEY_ESCAPE to use menus */
if (key == 0 /*|| key == KEY_ESCAPE */ )
return false;
debugC(3, kDebugLevelInput, "key = %04x", key);
for (i = 0; i < MAX_DIRS; i++) {
if (game.ev_keyp[i].data == key) {
debugC(3, kDebugLevelInput, "event %d: key press", i);
game.ev_keyp[i].occured = true;
report("event AC:%i occured\n", i);
return true;
}
}
#ifdef USE_MOUSE
if (key == BUTTON_LEFT) {
if (getflag(F_menus_work) && mouse.y <= CHAR_LINES) {
new_input_mode(INPUT_MENU);
return true;
}
}
#endif
if (game.player_control) {
int d = 0;
if (!KEY_ASCII(key)) {
switch (key) {
case KEY_UP:
d = 1;
break;
case KEY_DOWN:
d = 5;
break;
case KEY_LEFT:
d = 7;
break;
case KEY_RIGHT:
d = 3;
break;
case KEY_UP_RIGHT:
d = 2;
break;
case KEY_DOWN_RIGHT:
d = 4;
break;
case KEY_UP_LEFT:
d = 8;
break;
case KEY_DOWN_LEFT:
d = 6;
break;
}
}
#ifdef USE_MOUSE
if (!opt.agimouse) {
/* Handle mouse button events */
if (key == BUTTON_LEFT) {
v->flags |= ADJ_EGO_XY;
v->parm1 = WIN_TO_PIC_X(mouse.x);
v->parm2 = WIN_TO_PIC_Y(mouse.y);
return true;
}
}
#endif
v->flags &= ~ADJ_EGO_XY;
if (d || key == KEY_STATIONARY) {
v->direction = v->direction == d ? 0 : d;
return true;
}
}
return false;
}
void handle_getstring(int key) {
static int pos = 0; /* Cursor position */
static char buf[40];
if (KEY_ASCII(key) == 0)
return;
debugC(3, kDebugLevelInput, "handling key: %02x", key);
switch (key) {
case KEY_ENTER:
debugC(3, kDebugLevelInput, "KEY_ENTER");
game.has_prompt = 0;
buf[pos] = 0;
strcpy(game.strings[stringdata.str], buf);
debugC(3, kDebugLevelInput, "buffer=[%s]", buf);
buf[pos = 0] = 0;
new_input_mode(INPUT_NORMAL);
print_character(stringdata.x + strlen(game.strings[stringdata.str]) + 1,
stringdata.y, ' ', game.color_fg, game.color_bg);
return;
case KEY_ESCAPE:
debugC(3, kDebugLevelInput, "KEY_ESCAPE");
game.has_prompt = 0;
buf[pos = 0] = 0;
strcpy(game.strings[stringdata.str], buf);
new_input_mode(INPUT_NORMAL);
/* new_input_mode (INPUT_MENU); */
break;
case KEY_BACKSPACE: /*0x08: */
if (!pos)
break;
print_character(stringdata.x + (pos + 1), stringdata.y,
' ', game.color_fg, game.color_bg);
pos--;
buf[pos] = 0;
break;
default:
if (key < 0x20 || key > 0x7f)
break;
if (pos >= stringdata.len)
break;
buf[pos++] = key;
buf[pos] = 0;
/* Echo */
print_character(stringdata.x + pos, stringdata.y, buf[pos - 1],
game.color_fg, game.color_bg);
break;
}
/* print cursor */
print_character(stringdata.x + pos + 1, stringdata.y,
(char)game.cursor_char, game.color_fg, game.color_bg);
}
void handle_keys(int key) {
uint8 *p = NULL;
int c = 0;
static uint8 formated_entry[256];
int l = game.line_user_input;
int fg = game.color_fg, bg = game.color_bg;
setvar(V_word_not_found, 0);
debugC(3, kDebugLevelInput, "handling key: %02x", key);
switch (key) {
case KEY_ENTER:
debugC(3, kDebugLevelInput, "KEY_ENTER");
game.keypress = 0;
/* Remove all leading spaces */
for (p = game.input_buffer; *p && *p == 0x20; p++);
/* Copy to internal buffer */
for (; *p; p++) {
/* Squash spaces */
if (*p == 0x20 && *(p + 1) == 0x20) {
p++;
continue;
}
formated_entry[c++] = tolower(*p);
}
formated_entry[c++] = 0;
/* Handle string only if it's not empty */
if (formated_entry[0]) {
strcpy((char *)game.echo_buffer, (const char *)game.input_buffer);
strcpy(last_sentence, (const char *)formated_entry);
dictionary_words(last_sentence);
}
/* Clear to start a new line */
game.has_prompt = 0;
game.input_buffer[game.cursor_pos = 0] = 0;
debugC(3, kDebugLevelInput, "clear lines");
clear_lines(l, l + 1, bg);
flush_lines(l, l + 1);
break;
case KEY_ESCAPE:
debugC(3, kDebugLevelInput, "KEY_ESCAPE");
new_input_mode(INPUT_MENU);
break;
case KEY_BACKSPACE:
/* Ignore backspace at start of line */
if (game.cursor_pos == 0)
break;
/* erase cursor */
print_character(game.cursor_pos + 1, l, ' ', fg, bg);
game.input_buffer[--game.cursor_pos] = 0;
/* Print cursor */
print_character(game.cursor_pos + 1, l, game.cursor_char, fg, bg);
break;
default:
/* Ignore invalid keystrokes */
if (key < 0x20 || key > 0x7f)
break;
/* Maximum input size reached */
if (game.cursor_pos >= getvar(V_max_input_chars))
break;
game.input_buffer[game.cursor_pos++] = key;
game.input_buffer[game.cursor_pos] = 0;
/* echo */
print_character(game.cursor_pos, l, game.input_buffer[game.cursor_pos - 1], fg, bg);
/* Print cursor */
print_character(game.cursor_pos + 1, l, game.cursor_char, fg, bg);
break;
}
}
int wait_key() {
int key;
/* clear key queue */
while (keypress()) {
get_key();
}
debugC(3, kDebugLevelInput, "waiting...");
while (42) {
poll_timer(); /* msdos driver -> does nothing */
key = do_poll_keyboard();
if (!console_keyhandler(key)) {
if (key == KEY_ENTER || key == KEY_ESCAPE
#ifdef USE_MOUSE
|| key == BUTTON_LEFT
#endif
)
break;
}
console_cycle();
}
return key;
}
int wait_any_key() {
int key;
/* clear key queue */
while (keypress()) {
get_key();
}
debugC(3, kDebugLevelInput, "waiting...");
while (42) {
poll_timer(); /* msdos driver -> does nothing */
key = do_poll_keyboard();
if (!console_keyhandler(key) && key)
break;
console_cycle();
}
return key;
}
} // End of namespace Agi

94
engines/agi/keyboard.h Normal file
View File

@ -0,0 +1,94 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_KEYBOARD_H
#define __AGI_KEYBOARD_H
#include "common/stdafx.h"
#include "agi/agi.h"
namespace Agi {
/* QNX4 has a KEY_DOWN defined which we don't need to care about */
#undef KEY_DOWN
/* Allegro defines these */
#undef KEY_BACKSPACE
#undef KEY_ENTER
#undef KEY_LEFT
#undef KEY_RIGHT
#undef KEY_UP
#undef KEY_PGUP
#undef KEY_PGDN
#undef KEY_HOME
#undef KEY_END
#define KEY_BACKSPACE 0x08
#define KEY_ESCAPE 0x1B
#define KEY_ENTER 0x0D
#define KEY_UP 0x4800
#define KEY_DOWN 0x5000
#define KEY_LEFT 0x4B00
#define KEY_STATIONARY 0x4C00
#define KEY_RIGHT 0x4D00
#define KEY_DOWN_LEFT 0x4F00
#define KEY_DOWN_RIGHT 0x5100
#define KEY_UP_LEFT 0x4700
#define KEY_UP_RIGHT 0x4900
#define KEY_STATUSLN 0xd900 /* F11 */
#define KEY_PRIORITY 0xda00 /* F12 */
#define KEY_PGUP 0x4900 /* Page Up (fixed by Ziv Barber) */
#define KEY_PGDN 0x5100 /* Page Down */
#define KEY_HOME 0x4700 /* Home */
#define KEY_END 0x4f00 /* End * */
#ifdef USE_MOUSE
#define BUTTON_LEFT 0xF101 /* Left mouse button */
#define BUTTON_RIGHT 0xF202 /* Right mouse button */
#endif
#define KEY_SCAN(k) (k >> 8)
#define KEY_ASCII(k) (k & 0xff)
extern uint8 scancode_table[];
void init_words(void);
void clean_input(void);
int do_poll_keyboard(void);
void clean_keyboard(void);
void handle_keys(int);
void handle_getstring(int);
int handle_controller(int);
void get_string(int, int, int, int);
uint16 agi_get_keypress(void);
int wait_key(void);
int wait_any_key(void);
} // End of namespace Agi
#endif /* __AGI_KEYBOARD_H */

140
engines/agi/list.h Normal file
View File

@ -0,0 +1,140 @@
/*
* $Id$
*
* List management macros from the Linux kernel
*/
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
#include "agi/agi.h"
namespace Agi {
/**
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static INLINE void __list_add(struct list_head *tnew, struct list_head *prev, struct list_head *next) {
next->prev = tnew;
tnew->next = next;
tnew->prev = prev;
prev->next = tnew;
}
/**
* add a new entry
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*
* @param new new entry to be added
* @param head list head to add it after
*/
static INLINE void list_add(struct list_head *tnew, struct list_head *head) {
__list_add(tnew, head, head->next);
}
/**
* add a new entry
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*
* @new: new entry to be added
* @head: list head to add it before
*/
static INLINE void list_add_tail(struct list_head *tnew, struct list_head *head) {
__list_add(tnew, head->prev, head);
}
/**
* Delete a list entry (makes prev/next entries point to each other)
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static INLINE void __list_del(struct list_head *prev, struct list_head *next) {
next->prev = prev;
prev->next = next;
}
/**
* deletes entry from list.
* @param entry the element to delete from the list.
*/
static INLINE void list_del(struct list_head *entry) {
__list_del(entry->prev, entry->next);
}
/**
* tests whether a list is empty
* @param head the list to test.
*/
static INLINE int list_empty(struct list_head *head) {
return head->next == head;
}
/**
* join two lists
* @param list the new list to add.
* @param head the place to add it in the first list.
*/
static INLINE void list_splice(struct list_head *list, struct list_head *head) {
struct list_head *first = list->next;
if (first != list) {
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
}
/**
* get the struct for this entry
* @param ptr the &struct list_head pointer.
* @param type the type of the struct this is embedded in.
* @param member the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
/**
* iterate over a list
* @param pos the &struct list_head to use as a loop counter.
* @param head the head for your list.
*/
#define list_for_each(pos, head, next) \
for (pos = (head)->next; pos != (head); pos = pos->next)
} // End of namespace Agi
#endif

116
engines/agi/logic.cpp Normal file
View File

@ -0,0 +1,116 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
namespace Agi {
/**
* Decode logic resource
* This function decodes messages from the specified raw logic resource
* into a message list.
* @param n The number of the logic resource to decode.
*/
int decode_logic(int n) {
int ec = err_OK;
int mstart, mend, mc;
uint8 *m0;
/* decrypt messages at end of logic + build message list */
/* report ("decoding logic #%d\n", n); */
m0 = game.logics[n].data;
mstart = READ_LE_UINT16(m0) + 2;
mc = *(m0 + mstart);
mend = READ_LE_UINT16(m0 + mstart + 1);
m0 += mstart + 3; /* cover header info */
mstart = mc << 1;
/* if the logic was not compressed, decrypt the text messages
* only if there are more than 0 messages
*/
if ((~game.dir_logic[n].flags & RES_COMPRESSED) && mc > 0)
decrypt(m0 + mstart, mend - mstart); /* decrypt messages */
/* build message list */
m0 = game.logics[n].data;
mstart = READ_LE_UINT16(m0) + 2; /* +2 covers pointer */
game.logics[n].num_texts = *(m0 + mstart);
/* resetp logic pointers */
game.logics[n].sIP = 2;
game.logics[n].cIP = 2;
game.logics[n].size = READ_LE_UINT16(m0) + 2; /* logic end pointer */
/* allocate list of pointers to point into our data */
game.logics[n].texts = (char **)calloc(1 + game.logics[n].num_texts, sizeof(char *));
/* cover header info */
m0 += mstart + 3;
if (game.logics[n].texts != NULL) {
/* move list of strings into list to make real pointers */
for (mc = 0; mc < game.logics[n].num_texts; mc++) {
mend = READ_LE_UINT16(m0 + mc * 2);
game.logics[n].texts[mc] = mend ? (char *)m0 + mend - 2 : (char *)"";
}
/* set loaded flag now its all completly loaded */
game.dir_logic[n].flags |= RES_LOADED;
} else {
/* unload data
* blah DF YA WANKER!!@!@# frag. i'm so dumb. not every logic
* has text
*/
free(game.logics[n].data);
ec = err_NotEnoughMemory;
}
return ec;
}
/**
* Unload logic resource
* This function unloads the specified logic resource, freeing any
* memory chunks allocated for this resource.
* @param n The number of the logic resource to unload
*/
void unload_logic(int n) {
if (game.dir_logic[n].flags & RES_LOADED) {
free(game.logics[n].data);
if (game.logics[n].num_texts)
free(game.logics[n].texts);
game.logics[n].num_texts = 0;
game.dir_logic[n].flags &= ~RES_LOADED;
}
/* if cached, we end up here */
game.logics[n].sIP = 2;
game.logics[n].cIP = 2;
}
} // End of namespace Agi

49
engines/agi/logic.h Normal file
View File

@ -0,0 +1,49 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_LOGIC_H
#define __AGI_LOGIC_H
#include "agi/agi.h"
namespace Agi {
/**
* AGI logic resource structure.
*/
struct agi_logic {
uint8 *data; /**< raw resource data */
int size; /**< size of data */
int sIP; /**< saved IP */
int cIP; /**< current IP */
int num_texts; /**< number of messages */
char **texts; /**< message list */
};
int decode_logic(int);
void unload_logic(int);
} // End of namespace Agi
#endif /* __AGI_LOGIC_H */

191
engines/agi/lzw.cpp Normal file
View File

@ -0,0 +1,191 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
/***************************************************************************
** decomp.c
**
** Routines that deal with AGI version 3 specific features.
** The original LZW code is from DJJ, October 1989, p.86.
** It has been modified to handle AGI compression.
**
** (c) 1997 Lance Ewing
***************************************************************************/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/lzw.h"
namespace Agi {
#define MAXBITS 12
#define TABLE_SIZE 18041 /* strange number */
#define START_BITS 9
static int32 BITS, MAX_VALUE, MAX_CODE;
static uint32 *prefix_code;
static uint8 *append_character;
static uint8 *decode_stack;
static int32 input_bit_count = 0; /* Number of bits in input bit buffer */
static uint32 input_bit_buffer = 0L;
static void initLZW() {
decode_stack = (uint8 *)calloc(1, 8192);
prefix_code = (uint32 *)malloc(TABLE_SIZE * sizeof(uint32));
append_character = (uint8 *)malloc(TABLE_SIZE * sizeof(uint8));
input_bit_count = 0; /* Number of bits in input bit buffer */
input_bit_buffer = 0L;
}
static void closeLZW() {
free(decode_stack);
free(prefix_code);
free(append_character);
}
/***************************************************************************
** setBITS
**
** Purpose: To adjust the number of bits used to store codes to the value
** passed in.
***************************************************************************/
int setBITS(int32 value) {
if (value == MAXBITS)
return true;
BITS = value;
MAX_VALUE = (1 << BITS) - 1;
MAX_CODE = MAX_VALUE - 1;
return false;
}
/***************************************************************************
** decode_string
**
** Purpose: To return the string that the code taken from the input buffer
** represents. The string is returned as a stack, i.e. the characters are
** in reverse order.
***************************************************************************/
static uint8 *decode_string(uint8 *buffer, uint32 code) {
uint32 i;
for (i = 0; code > 255;) {
*buffer++ = append_character[code];
code = prefix_code[code];
if (i++ >= 4000) {
fprintf(stderr, "lzw: error in code expansion.\n");
abort();
}
}
*buffer = code;
return buffer;
}
/***************************************************************************
** input_code
**
** Purpose: To return the next code from the input buffer.
***************************************************************************/
static uint32 input_code(uint8 **input) {
uint32 r;
while (input_bit_count <= 24) {
input_bit_buffer |= (uint32) * (*input)++ << input_bit_count;
input_bit_count += 8;
}
r = (input_bit_buffer & 0x7FFF) % (1 << BITS);
input_bit_buffer >>= BITS;
input_bit_count -= BITS;
return r;
}
/***************************************************************************
** expand
**
** Purpose: To uncompress the data contained in the input buffer and store
** the result in the output buffer. The fileLength parameter says how
** many bytes to uncompress. The compression itself is a form of LZW that
** adjusts the number of bits that it represents its codes in as it fills
** up the available codes. Two codes have special meaning:
**
** code 256 = start over
** code 257 = end of data
***************************************************************************/
void LZW_expand(uint8 *in, uint8 *out, int32 len) {
int32 c, lzwnext, lzwnew, lzwold;
uint8 *s, *end;
initLZW();
setBITS(START_BITS); /* Starts at 9-bits */
lzwnext = 257; /* Next available code to define */
end = (unsigned char *)((long)out + (long)len);
lzwold = input_code(&in); /* Read in the first code */
c = lzwold;
lzwnew = input_code(&in);
while ((out < end) && (lzwnew != 0x101)) {
if (lzwnew == 0x100) {
/* Code to "start over" */
lzwnext = 258;
setBITS(START_BITS);
lzwold = input_code(&in);
c = lzwold;
*out++ = (char)c;
lzwnew = input_code(&in);
} else {
if (lzwnew >= lzwnext) {
/* Handles special LZW scenario */
*decode_stack = c;
s = decode_string(decode_stack + 1, lzwold);
} else
s = decode_string(decode_stack, lzwnew);
/* Reverse order of decoded string and
* store in out buffer
*/
c = *s;
while (s >= decode_stack)
*out++ = *s--;
if (lzwnext > MAX_CODE)
setBITS(BITS + 1);
prefix_code[lzwnext] = lzwold;
append_character[lzwnext] = c;
lzwnext++;
lzwold = lzwnew;
lzwnew = input_code(&in);
}
}
closeLZW();
}
} // End of namespace Agi

33
engines/agi/lzw.h Normal file
View File

@ -0,0 +1,33 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_LZW_H
#define __AGI_LZW_H
namespace Agi {
void LZW_expand(uint8 *, uint8 *, int32);
} // End of namespace Agi
#endif

503
engines/agi/menu.cpp Normal file
View File

@ -0,0 +1,503 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2002 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/sprite.h"
#include "agi/graphics.h"
#include "agi/keyboard.h"
#include "agi/menu.h"
#include "agi/text.h"
#include "agi/list.h"
namespace Agi {
struct agi_menu {
struct list_head list; /**< list head for menubar list */
struct list_head down; /**< list head for menu options */
int index; /**< number of menu in menubar */
int width; /**< width of menu in characters */
int height; /**< height of menu in characters */
int col; /**< column of menubar entry */
int wincol; /**< column of menu window */
char *text; /**< menu name */
};
struct agi_menu_option {
struct list_head list; /**< list head for menu options */
int enabled; /**< option is enabled or disabled */
int event; /**< menu event */
int index; /**< number of option in this menu */
char *text; /**< text of menu option */
};
static LIST_HEAD(menubar);
static int h_cur_menu;
static int v_cur_menu;
static struct agi_menu *get_menu(int i) {
struct list_head *h;
struct agi_menu *m;
list_for_each(h, &menubar, next) {
m = list_entry(h, struct agi_menu, list);
if (m->index == i)
return m;
}
return NULL;
}
static struct agi_menu_option *get_menu_option(int i, int j) {
struct list_head *h;
struct agi_menu *m;
struct agi_menu_option *d;
m = get_menu(i);
list_for_each(h, &m->down, next) {
d = list_entry(h, struct agi_menu_option, list);
if (d->index == j)
return d;
}
return NULL;
}
static void draw_menu_bar() {
struct list_head *h;
struct agi_menu *m;
clear_lines(0, 0, MENU_BG);
flush_lines(0, 0);
list_for_each(h, &menubar, next) {
m = list_entry(h, struct agi_menu, list);
print_text(m->text, 0, m->col, 0, 40, MENU_FG, MENU_BG);
}
}
static void draw_menu_hilite(int cur_menu) {
struct agi_menu *m;
m = get_menu(cur_menu);
debugC(6, kDebugLevelMenu, "[%s]", m->text);
print_text(m->text, 0, m->col, 0, 40, MENU_BG, MENU_FG);
flush_lines(0, 0);
}
/* draw box and pulldowns. */
static void draw_menu_option(int h_menu) {
struct list_head *h;
struct agi_menu *m = NULL;
struct agi_menu_option *d = NULL;
/* find which vertical menu it is */
m = get_menu(h_menu);
draw_box(m->wincol * CHAR_COLS, 1 * CHAR_LINES, (m->wincol + m->width + 2) * CHAR_COLS,
(1 + m->height + 2) * CHAR_LINES, MENU_BG, MENU_LINE, 0);
list_for_each(h, &m->down, next) {
d = list_entry(h, struct agi_menu_option, list);
print_text(d->text, 0, m->wincol + 1, d->index + 2, m->width + 2,
d->enabled ? MENU_FG : MENU_DISABLED, MENU_BG);
}
}
static void draw_menu_option_hilite(int h_menu, int v_menu) {
struct agi_menu *m;
struct agi_menu_option *d;
m = get_menu(h_menu);
d = get_menu_option(h_menu, v_menu);
print_text(d->text, 0, m->wincol + 1, v_menu + 2, m->width + 2,
MENU_BG, d->enabled ? MENU_FG : MENU_DISABLED);
}
static void new_menu_selected(int i) {
show_pic();
draw_menu_bar();
draw_menu_hilite(i);
draw_menu_option(i);
}
#ifdef USE_MOUSE
static int mouse_over_text(unsigned int line, unsigned int col, char *s) {
if (mouse.x < col * CHAR_COLS)
return false;
if (mouse.x > (col + strlen(s)) * CHAR_COLS)
return false;
if (mouse.y < line * CHAR_LINES)
return false;
if (mouse.y >= (line + 1) * CHAR_LINES)
return false;
return true;
}
#endif
static int h_index;
static int v_index;
static int h_col;
static int h_max_menu;
static int v_max_menu[10];
#if 0
static void add_about_option() {
struct agi_menu *m;
struct agi_menu_option *d;
char text[] = "About AGI engine";
d = malloc(sizeof(struct agi_menu_option));
d->text = strdup(text);
d->enabled = true;
d->event = 255;
d->index = (v_max_menu[0] += 1);
m = list_entry(menubar.next, struct agi_menu, list);
list_add_tail(&d->list, &m->down);
m->height++;
if (m->width < strlen(text))
m->width = strlen(text);
}
#endif
/*
* Public functions
*/
void menu_init() {
h_index = 0;
h_col = 1;
h_cur_menu = 0;
v_cur_menu = 0;
}
void menu_deinit() {
struct list_head *h, *h2, *v, *v2;
struct agi_menu *m = NULL;
struct agi_menu_option *d = NULL;
for (h = (&menubar)->prev; h != (&menubar); h = h2) {
m = list_entry(h, struct agi_menu, list);
h2 = h->prev;
debugC(3, kDebugLevelMenu, "deiniting hmenu %s", m->text);
for (v = (&m->down)->prev; v != (&m->down); v = v2) {
d = list_entry(v, struct agi_menu_option, list);
v2 = v->prev;
debugC(3, kDebugLevelMenu, " deiniting vmenu %s", d->text);
list_del(v);
free(d->text);
free(d);
}
list_del(h);
free(m->text);
free(m);
}
}
void menu_add(char *s) {
struct agi_menu *m;
m = (agi_menu *) malloc(sizeof(struct agi_menu));
m->text = strdup(s);
while (m->text[strlen(m->text) - 1] == ' ')
m->text[strlen(m->text) - 1] = 0;
m->down.next = &m->down;
m->down.prev = &m->down;
m->width = 0;
m->height = 0;
m->index = h_index++;
m->col = h_col;
m->wincol = h_col - 1;
v_index = 0;
v_max_menu[m->index] = 0;
h_col += strlen(m->text) + 1;
h_max_menu = m->index;
debugC(3, kDebugLevelMenu, "add menu: '%s' %02x", s, m->text[strlen(m->text)]);
list_add_tail(&m->list, &menubar);
}
void menu_add_item(char *s, int code) {
struct agi_menu *m;
struct agi_menu_option *d;
int l;
d = (agi_menu_option *) malloc(sizeof(struct agi_menu_option));
d->text = strdup(s);
d->enabled = true;
d->event = code;
d->index = v_index++;
m = list_entry(menubar.prev, struct agi_menu, list);
m->height++;
v_max_menu[m->index] = d->index;
l = strlen(d->text);
if (l > 40)
l = 38;
if (m->wincol + l > 38)
m->wincol = 38 - l;
if (l > m->width)
m->width = l;
debugC(3, kDebugLevelMenu, "Adding menu item: %s (size = %d)", s, m->height);
list_add_tail(&d->list, &m->down);
}
void menu_submit() {
struct list_head *h, *h2;
struct agi_menu *m = NULL;
debugC(3, kDebugLevelMenu, "Submitting menu");
/* add_about_option (); */
/* If a menu has no options, delete it */
for (h = (&menubar)->prev; h != (&menubar); h = h2) {
m = list_entry(h, struct agi_menu, list);
h2 = h->prev;
if ((&m->down)->prev == (&m->down)) {
list_del(h);
free(m->text);
free(m);
h_max_menu--;
}
}
}
int menu_keyhandler(int key) {
static int clock_val;
static int menu_active = false;
struct agi_menu_option *d;
struct list_head *h;
struct agi_menu *m;
static int button_used = 0;
if (!getflag(F_menus_work))
return false;
if (!menu_active) {
clock_val = game.clock_enabled;
game.clock_enabled = false;
draw_menu_bar();
}
#ifdef USE_MOUSE
/*
* Mouse handling
*/
if (mouse.button) {
int hmenu, vmenu;
button_used = 1; /* Button has been used at least once */
if (mouse.y <= CHAR_LINES) {
/* on the menubar */
hmenu = 0;
list_for_each(h, &menubar, next) {
m = list_entry(h, struct agi_menu, list);
if (mouse_over_text(0, m->col, m->text)) {
break;
} else {
hmenu++;
}
}
if (hmenu <= h_max_menu) {
if (h_cur_menu != hmenu) {
v_cur_menu = -1;
new_menu_selected(hmenu);
}
h_cur_menu = hmenu;
}
} else {
/* not in menubar */
struct agi_menu_option *do1;
vmenu = 0;
m = get_menu(h_cur_menu);
list_for_each(h, &m->down, next) {
do1 = list_entry(h, struct agi_menu_option, list);
if (mouse_over_text(2 + do1->index, m->wincol + 1, do1->text)) {
break;
} else {
vmenu++;
}
}
if (vmenu <= v_max_menu[h_cur_menu]) {
if (v_cur_menu != vmenu) {
draw_menu_option(h_cur_menu);
draw_menu_option_hilite(h_cur_menu, vmenu);
}
v_cur_menu = vmenu;
}
}
} else if (button_used) {
/* Button released */
button_used = 0;
debugC(6, kDebugLevelMenu | kDebugLevelInput, "button released!");
if (v_cur_menu < 0)
v_cur_menu = 0;
draw_menu_option_hilite(h_cur_menu, v_cur_menu);
if (mouse.y <= CHAR_LINES) {
/* on the menubar */
} else {
/* see which option we selected */
m = get_menu(h_cur_menu);
list_for_each(h, &m->down, next) {
d = list_entry(h, struct agi_menu_option, list);
if (mouse_over_text(2 + d->index,
m->wincol + 1, d->text)) {
/* activate that option */
if (d->enabled) {
debugC(6, kDebugLevelMenu | kDebugLevelInput, "event %d registered", d->event);
game.ev_keyp[d->event].occured = true;
game.ev_keyp[d->event].data = d->event;
goto exit_menu;
}
}
}
goto exit_menu;
}
}
#endif /* USE_MOUSE */
if (!menu_active) {
if (h_cur_menu >= 0) {
draw_menu_hilite(h_cur_menu);
draw_menu_option(h_cur_menu);
if (!button_used && v_cur_menu >= 0)
draw_menu_option_hilite(h_cur_menu, v_cur_menu);
}
menu_active = true;
}
switch (key) {
case KEY_ESCAPE:
debugC(6, kDebugLevelMenu | kDebugLevelInput, "KEY_ESCAPE");
goto exit_menu;
case KEY_ENTER:
debugC(6, kDebugLevelMenu | kDebugLevelInput, "KEY_ENTER");
d = get_menu_option(h_cur_menu, v_cur_menu);
if (d->enabled) {
debugC(6, kDebugLevelMenu | kDebugLevelInput, "event %d registered", d->event);
game.ev_keyp[d->event].occured = true;
goto exit_menu;
}
break;
case KEY_DOWN:
case KEY_UP:
v_cur_menu += key == KEY_DOWN ? 1 : -1;
if (v_cur_menu < 0)
v_cur_menu = 0;
if (v_cur_menu > v_max_menu[h_cur_menu])
v_cur_menu = v_max_menu[h_cur_menu];
draw_menu_option(h_cur_menu);
draw_menu_option_hilite(h_cur_menu, v_cur_menu);
break;
case KEY_RIGHT:
case KEY_LEFT:
h_cur_menu += key == KEY_RIGHT ? 1 : -1;
if (h_cur_menu < 0)
h_cur_menu = h_max_menu;
if (h_cur_menu > h_max_menu)
h_cur_menu = 0;
v_cur_menu = 0;
new_menu_selected(h_cur_menu);
draw_menu_option_hilite(h_cur_menu, v_cur_menu);
break;
}
return true;
exit_menu:
button_used = 0;
show_pic();
write_status();
setvar(V_key, 0);
game.keypress = 0;
game.clock_enabled = clock_val;
old_input_mode();
debugC(3, kDebugLevelMenu, "exit_menu: input mode reset to %d", game.input_mode);
menu_active = false;
return true;
}
void menu_set_item(int event, int state) {
struct list_head *h, *v;
struct agi_menu *m = NULL;
struct agi_menu_option *d = NULL;
/* scan all menus for event number # */
debugC(6, kDebugLevelMenu, "event = %d, state = %d", event, state);
list_for_each(h, &menubar, next) {
m = list_entry(h, struct agi_menu, list);
list_for_each(v, &m->down, next) {
d = list_entry(v, struct agi_menu_option, list);
if (d->event == event) {
d->enabled = state;
return;
}
}
}
}
void menu_enable_all() {
struct list_head *h, *v;
struct agi_menu *m = NULL;
struct agi_menu_option *d = NULL;
list_for_each(h, &menubar, next) {
m = list_entry(h, struct agi_menu, list);
list_for_each(v, &m->down, next) {
d = list_entry(v, struct agi_menu_option, list);
d->enabled = true;
}
}
}
} // End of namespace Agi

46
engines/agi/menu.h Normal file
View File

@ -0,0 +1,46 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_MENU_H
#define __AGI_MENU_H
namespace Agi {
#define MENU_BG 0x0f /* White */
#define MENU_DISABLED 0x07 /* Grey */
#define MENU_FG 0x00 /* Black */
#define MENU_LINE 0x00 /* Black */
void menu_init(void);
void menu_deinit(void);
void menu_add(char *);
void menu_add_item(char *, int);
void menu_submit(void);
void menu_set_item(int, int);
int menu_keyhandler(int);
void menu_enable_all(void);
} // End of namespace Agi
#endif

42
engines/agi/module.mk Normal file
View File

@ -0,0 +1,42 @@
MODULE := engines/agi
MODULE_OBJS = \
agi.o \
agi_v2.o \
agi_v3.o \
checks.o \
console.o \
cycle.o \
font.o \
global.o \
graphics.o \
id.o \
inv.o \
keyboard.o \
logic.o \
lzw.o \
menu.o \
motion.o \
objects.o \
op_cmd.o \
op_dbg.o \
op_test.o \
patches.o \
picture.o \
savegame.o \
sound.o \
sprite.o \
text.o \
view.o \
words.o
MODULE_DIRS += \
engines/agi
# This module can be built as a plugin
ifdef BUILD_PLUGINS
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/common.rules

232
engines/agi/motion.cpp Normal file
View File

@ -0,0 +1,232 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
namespace Agi {
static int check_step(int delta, int step) {
return (-step >= delta) ? 0 : (step <= delta) ? 2 : 1;
}
static int check_block(int x, int y) {
if (x <= game.block.x1 || x >= game.block.x2)
return false;
if (y <= game.block.y1 || y >= game.block.y2)
return false;
return true;
}
static void changepos(struct vt_entry *v) {
int b, x, y;
int dx[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
int dy[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };
x = v->x_pos;
y = v->y_pos;
b = check_block(x, y);
x += v->step_size * dx[v->direction];
y += v->step_size * dy[v->direction];
if (check_block(x, y) == b) {
v->flags &= ~MOTION;
} else {
v->flags |= MOTION;
v->direction = 0;
if /*_is_ego_view*/ (v)
game.vars[V_ego_dir] = 0;
}
}
static void motion_wander(struct vt_entry *v) {
if (v->parm1--) {
if (~v->flags & DIDNT_MOVE)
return;
}
v->direction = rnd->getRandomNumber(8);
if /*_is_ego_view */ (v) {
game.vars[V_ego_dir] = v->direction;
while (v->parm1 < 6) {
v->parm1 = rnd->getRandomNumber(50); /* huh? */
}
}
}
static void motion_followego(struct vt_entry *v) {
int ego_x, ego_y;
int obj_x, obj_y;
int dir;
ego_x = game.view_table[0].x_pos + game.view_table[0].x_size / 2;
ego_y = game.view_table[0].y_pos;
obj_x = v->x_pos + v->x_size / 2;
obj_y = v->y_pos;
/* Get direction to reach ego */
dir = get_direction(obj_x, obj_y, ego_x, ego_y, v->parm1);
/* Already at ego coordinates */
if (dir == 0) {
v->direction = 0;
v->motion = MOTION_NORMAL;
setflag(v->parm2, true);
return;
}
if (v->parm3 == 0xff) {
v->parm3 = 0;
} else if (v->flags & DIDNT_MOVE) {
int d;
while ((v->direction = rnd->getRandomNumber(8)) == 0) {
}
d = (abs(ego_y - obj_y) + abs(ego_x - obj_x)) / 2;
if (d < v->step_size) {
v->parm3 = v->step_size;
return;
}
while ((v->parm3 = rnd->getRandomNumber(d)) < v->step_size) {
}
return;
}
if (v->parm3 != 0) {
int k;
/* DF: this is ugly and I dont know why this works, but
* other line does not! (watcom complained about lvalue)
*
* if (((int8)v->parm3 -= v->step_size) < 0)
* v->parm3 = 0;
*/
k = v->parm3;
k -= v->step_size;
v->parm3 = k;
if ((int8) v->parm3 < 0)
v->parm3 = 0;
} else {
v->direction = dir;
}
}
static void motion_moveobj(struct vt_entry *v) {
v->direction = get_direction(v->x_pos, v->y_pos, v->parm1, v->parm2, v->step_size);
/* Update V6 if ego */
if (v == game.view_table)
game.vars[V_ego_dir] = v->direction;
if (v->direction == 0)
in_destination(v);
}
static void check_motion(struct vt_entry *v) {
switch (v->motion) {
case MOTION_WANDER:
motion_wander(v);
break;
case MOTION_FOLLOW_EGO:
motion_followego(v);
break;
case MOTION_MOVE_OBJ:
motion_moveobj(v);
break;
}
if ((game.block.active && (~v->flags & IGNORE_BLOCKS)) && v->direction)
changepos(v);
}
/*
* Public functions
*/
/**
*
*/
void check_all_motions() {
struct vt_entry *v;
for (v = game.view_table; v < &game.view_table[MAX_VIEWTABLE]; v++) {
if ((v->flags & (ANIMATED | UPDATE | DRAWN)) == (ANIMATED | UPDATE | DRAWN)
&& v->step_time_count == 1) {
check_motion(v);
}
}
}
/**
* Check if given entry is at destination point.
* This function is used to updated the flags of an object with move.obj
* type motion that * has reached its final destination coordinates.
* @param v Pointer to view table entry
*/
void in_destination(struct vt_entry *v) {
if (v->motion == MOTION_MOVE_OBJ) {
v->step_size = v->parm3;
setflag(v->parm4, true);
}
v->motion = MOTION_NORMAL;
if (v == game.view_table)
game.player_control = true;
}
/**
* Wrapper for static function motion_moveobj().
* This function is used by cmd_move_object() in the first motion cycle
* after setting the motion mode to MOTION_MOVE_OBJ.
* @param v Pointer to view table entry
*/
void move_obj(struct vt_entry *v) {
motion_moveobj(v);
}
/**
* Get direction from motion coordinates
* This function gets the motion direction from the current and previous
* object coordinates and the step size.
* @param x0 Original x coordinate of the object
* @param y0 Original y coordinate of the object
* @param x x coordinate of the object
* @param y y coordinate of the object
* @param s step size
*/
int get_direction(int x0, int y0, int x, int y, int s) {
int dir_table[9] = { 8, 1, 2, 7, 0, 3, 6, 5, 4 };
return dir_table[check_step(x - x0, s) + 3 * check_step(y - y0, s)];
}
} // End of namespace Agi

167
engines/agi/objects.cpp Normal file
View File

@ -0,0 +1,167 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
namespace Agi {
extern int decode_objects(uint8 *mem, uint32 flen);
static struct agi_object *objects; /* objects in the game */
int alloc_objects(int n) {
if ((objects = (agi_object *) calloc(n, sizeof(struct agi_object))) == NULL)
return err_NotEnoughMemory;
return err_OK;
}
int decode_objects(uint8 *mem, uint32 flen) {
unsigned int i, so, padsize;
padsize = game.game_flags & ID_AMIGA ? 4 : 3;
game.num_objects = 0;
objects = NULL;
/* check if first pointer exceeds file size
* if so, its encrypted, else it is not
*/
if (READ_LE_UINT16(mem) > flen) {
report("Decrypting objects... ");
decrypt(mem, flen);
report("done.\n");
}
/* alloc memory for object list
* byte 3 = number of animated objects. this is ignored.. ??
*/
if (READ_LE_UINT16(mem) / padsize >= 256) {
#ifdef AGDS_SUPPORT
/* die with no error! AGDS game needs not to die to work!! :( */
return err_OK;
#else
/* no AGDS support, die with error */
return err_BadResource;
#endif
}
game.num_objects = READ_LE_UINT16(mem) / padsize;
debugC(5, kDebugLevelResources, "num_objects = %d (padsize = %d)", game.num_objects, padsize);
if (alloc_objects(game.num_objects) != err_OK)
return err_NotEnoughMemory;
/* build the object list */
for (i = 0, so = padsize; i < game.num_objects; i++, so += padsize) {
int offset;
(objects + i)->location = *(mem + so + 2);
offset = READ_LE_UINT16(mem + so) + padsize;
if ((uint) offset < flen) {
(objects + i)->name = (char *)strdup((const char *)mem + offset);
} else {
printf("ERROR: object %i name beyond object filesize! "
"(%04x > %04x)\n", i, offset, flen);
(objects + i)->name = strdup("");
}
}
report("Reading objects: %d objects read.\n", game.num_objects);
return err_OK;
}
int load_objects(char *fname) {
Common::File fp;
uint32 flen;
uint8 *mem;
char *path;
objects = NULL;
game.num_objects = 0;
debugC(5, kDebugLevelResources, "(fname = %s)", fname);
path = fname;
report("Loading objects: %s\n", path);
if (!fp.open(path))
return err_BadFileOpen;
fp.seek(0, SEEK_END);
flen = fp.pos();
fp.seek(0, SEEK_SET);
if ((mem = (uint8 *) calloc(1, flen + 32)) == NULL) {
fp.close();
return err_NotEnoughMemory;
}
fp.read(mem, flen);
fp.close();
decode_objects(mem, flen);
free(mem);
return err_OK;
}
void unload_objects() {
unsigned int i;
if (objects != NULL) {
for (i = 0; i < game.num_objects; i++)
free(objects[i].name);
free(objects);
}
}
void object_set_location(unsigned int n, int i) {
if (n >= game.num_objects) {
report("Error: Can't access object %d.\n", n);
return;
}
objects[n].location = i;
}
int object_get_location(unsigned int n) {
if (n >= game.num_objects) {
report("Error: Can't access object %d.\n", n);
return 0;
}
return objects[n].location;
}
char *object_name(unsigned int n) {
if (n >= game.num_objects) {
report("Error: Can't access object %d.\n", n);
return "";
}
return objects[n].name;
}
} // End of namespace Agi

1513
engines/agi/op_cmd.cpp Normal file

File diff suppressed because it is too large Load Diff

353
engines/agi/op_dbg.cpp Normal file
View File

@ -0,0 +1,353 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/opcodes.h"
namespace Agi {
#define ip (game.logics[lognum].cIP)
#define code (game.logics[lognum].data)
#ifdef _L
#undef _L
#endif
#ifdef USE_CONSOLE
#define _L(a,b,c) { a, b, c }
#else
#define _L(a,b,c) { b, c }
#endif
struct agi_logicnames logic_names_test[] = {
_L("", 0, 0x00),
_L("equaln", 2, 0x80),
_L("equalv", 2, 0xC0),
_L("lessn", 2, 0x80),
_L("lessv", 2, 0xC0),
_L("greatern", 2, 0x80),
_L("greaterv", 2, 0xC0),
_L("isset", 1, 0x00),
_L("issetv", 1, 0x80),
_L("has", 1, 0x00),
_L("obj.in.room", 2, 0x40),
_L("posn", 5, 0x00),
_L("controller", 1, 0x00),
_L("have.key", 0, 0x00),
/* Not 0 args. Has variable number. */
_L("said", 0, 0x00),
_L("compare.strings", 2, 0x00),
_L("obj.in.box", 5, 0x00),
_L("center.posn", 5, 0x00),
_L("right.posn", 5, 0x00)
};
struct agi_logicnames logic_names_if[] = {
_L("OR", 0, 0x00),
_L("NOT", 0, 0x00),
_L("ELSE", 0, 0x00),
_L("IF", 0, 0x00)
};
struct agi_logicnames logic_names_cmd[] = {
_L("return", 0, 0x00), /* 00 */
_L("increment", 1, 0x80), /* 01 */
_L("decrement", 1, 0x80), /* 02 */
_L("assignn", 2, 0x80), /* 03 */
_L("assignv", 2, 0xC0), /* 04 */
_L("addn", 2, 0x80), /* 05 */
_L("addv", 2, 0xC0), /* 06 */
_L("subn", 2, 0x80), /* 07 */
_L("subv", 2, 0xC0), /* 08 */
_L("lindirectv", 2, 0xC0), /* 09 */
_L("rindirect", 2, 0xC0), /* 0A */
_L("lindirectn", 2, 0x80), /* 0B */
_L("set", 1, 0x00), /* 0C */
_L("reset", 1, 0x00), /* 0D */
_L("toggle", 1, 0x00), /* 0E */
_L("set.v", 1, 0x80), /* 0F */
_L("reset.v", 1, 0x80), /* 10 */
_L("toggle.v", 1, 0x80), /* 11 */
_L("new.room", 1, 0x00), /* 12 */
_L("new.room.v", 1, 0x80), /* 13 */
_L("load.logics", 1, 0x00), /* 14 */
_L("load.logics.v", 1, 0x80), /* 15 */
_L("call", 1, 0x00), /* 16 */
_L("call.v", 1, 0x80), /* 17 */
_L("load.pic", 1, 0x80), /* 18 */
_L("draw.pic", 1, 0x80), /* 19 */
_L("show.pic", 0, 0x00), /* 1A */
_L("discard.pic", 1, 0x80), /* 1B */
_L("overlay.pic", 1, 0x80), /* 1C */
_L("show.pri.screen", 0, 0x00), /* 1D */
_L("load.view", 1, 0x00), /* 1E */
_L("load.view.v", 1, 0x80), /* 1F */
_L("discard.view", 1, 0x00), /* 20 */
_L("animate.obj", 1, 0x00), /* 21 */
_L("unanimate.all", 0, 0x00), /* 22 */
_L("draw", 1, 0x00), /* 23 */
_L("erase", 1, 0x00), /* 24 */
_L("position", 3, 0x00), /* 25 */
_L("position.v", 3, 0x60), /* 26 */
_L("get.posn", 3, 0x60), /* 27 */
_L("reposition", 3, 0x60), /* 28 */
_L("set.view", 2, 0x00), /* 29 */
_L("set.view.v", 2, 0x40), /* 2A */
_L("set.loop", 2, 0x00), /* 2B */
_L("set.loop.v", 2, 0x40), /* 2C */
_L("fix.loop", 1, 0x00), /* 2D */
_L("release.loop", 1, 0x00), /* 2E */
_L("set.cel", 2, 0x00), /* 2F */
_L("set.cel.v", 2, 0x40), /* 30 */
_L("last.cel", 2, 0x40), /* 31 */
_L("current.cel", 2, 0x40), /* 32 */
_L("current.loop", 2, 0x40), /* 33 */
_L("current.view", 2, 0x40), /* 34 */
_L("number.of.loops", 2, 0x40), /* 35 */
_L("set.priority", 2, 0x00), /* 36 */
_L("set.priority.v", 2, 0x40), /* 37 */
_L("release.priority", 1, 0x00), /* 38 */
_L("get.priority", 2, 0x40), /* 39 */
_L("stop.update", 1, 0x00), /* 3A */
_L("start.update", 1, 0x00), /* 3B */
_L("force.update", 1, 0x00), /* 3C */
_L("ignore.horizon", 1, 0x00), /* 3D */
_L("observe.horizon", 1, 0x00), /* 3E */
_L("set.horizon", 1, 0x00), /* 3F */
_L("object.on.water", 1, 0x00), /* 40 */
_L("object.on.land", 1, 0x00), /* 41 */
_L("object.on.anything", 1, 0x00), /* 42 */
_L("ignore.objs", 1, 0x00), /* 43 */
_L("observe.objs", 1, 0x00), /* 44 */
_L("distance", 3, 0x20), /* 45 */
_L("stop.cycling", 1, 0x00), /* 46 */
_L("start.cycling", 1, 0x00), /* 47 */
_L("normal.cycle", 1, 0x00), /* 48 */
_L("end.of.loop", 2, 0x00), /* 49 */
_L("reverse.cycle", 1, 0x00), /* 4A */
_L("reverse.loop", 2, 0x00), /* 4B */
_L("cycle.time", 2, 0x40), /* 4C */
_L("stop.motion", 1, 0x00), /* 4D */
_L("start.motion", 1, 0x00), /* 4E */
_L("step.size", 2, 0x40), /* 4F */
_L("step.time", 2, 0x40), /* 50 */
_L("move.obj", 5, 0x00), /* 51 */
_L("move.obj.v", 5, 0x70), /* 52 */
_L("follow.ego", 3, 0x00), /* 53 */
_L("wander", 1, 0x00), /* 54 */
_L("normal.motion", 1, 0x00), /* 55 */
_L("set.dir", 2, 0x40), /* 56 */
_L("get.dir", 2, 0x40), /* 57 */
_L("ignore.blocks", 1, 0x00), /* 58 */
_L("observe.blocks", 1, 0x00), /* 59 */
_L("block", 4, 0x00), /* 5A */
_L("unblock", 0, 0x00), /* 5B */
_L("get", 1, 0x00), /* 5C */
_L("get.v", 1, 0x80), /* 5D */
_L("drop", 1, 0x00), /* 5E */
_L("put", 2, 0x00), /* 5F */
_L("put.v", 2, 0x40), /* 60 */
_L("get.room.v", 2, 0xC0), /* 61 */
_L("load.sound", 1, 0x00), /* 62 */
_L("sound", 2, 0x00), /* 63 */
_L("stop.sound", 0, 0x00), /* 64 */
_L("print", 1, 0x00), /* 65 */
_L("print.v", 1, 0x80), /* 66 */
_L("display", 3, 0x00), /* 67 */
_L("display.v", 3, 0xE0), /* 68 */
_L("clear.lines", 3, 0x00), /* 69 */
_L("text.screen", 0, 0x00), /* 6A */
_L("graphics", 0, 0x00), /* 6B */
_L("set.cursor.char", 1, 0x00), /* 6C */
_L("set.text.attribute", 2, 0x00), /* 6D */
_L("shake.screen", 1, 0x00), /* 6E */
_L("configure.screen", 3, 0x00), /* 6F */
_L("status.line.on", 0, 0x00), /* 70 */
_L("status.line.off", 0, 0x00), /* 71 */
_L("set.string", 2, 0x00), /* 72 */
_L("get.string", 5, 0x00), /* 73 */
_L("word.to.string", 2, 0x00), /* 74 */
_L("parse", 1, 0x00), /* 75 */
_L("get.num", 2, 0x40), /* 76 */
_L("prevent.input", 0, 0x00), /* 77 */
_L("accept.input", 0, 0x00), /* 78 */
_L("set.key", 3, 0x00), /* 79 */
_L("add.to.pic", 7, 0x00), /* 7A */
_L("add.to.pic.v", 7, 0xFE), /* 7B */
_L("status", 0, 0x00), /* 7C */
_L("save.game", 0, 0x00), /* 7D */
_L("restore.game", 0, 0x00), /* 7E */
_L("init.disk", 0, 0x00), /* 7F */
_L("restart.game", 0, 0x00), /* 80 */
_L("show.obj", 1, 0x00), /* 81 */
_L("random", 3, 0x20), /* 82 */
_L("program.control", 0, 0x00), /* 83 */
_L("player.control", 0, 0x00), /* 84 */
_L("obj.status.v", 1, 0x80), /* 85 */
/* 0 args for AGI version 2.089 */
_L("quit", 1, 0x00), /* 86 */
_L("show.mem", 0, 0x00), /* 87 */
_L("pause", 0, 0x00), /* 88 */
_L("echo.line", 0, 0x00), /* 89 */
_L("cancel.line", 0, 0x00), /* 8A */
_L("init.joy", 0, 0x00), /* 8B */
_L("toggle.monitor", 0, 0x00), /* 8C */
_L("version", 0, 0x00), /* 8D */
_L("script.size", 1, 0x00), /* 8E */
_L("set.game.id", 1, 0x00), /* 8F */
_L("log", 1, 0x00), /* 90 */
_L("set.scan.start", 0, 0x00), /* 91 */
_L("reset.scan.start", 0, 0x00), /* 92 */
_L("reposition.to", 3, 0x00), /* 93 */
_L("reposition.to.v", 3, 0x60), /* 94 */
_L("trace.on", 0, 0x00), /* 95 */
_L("trace.info", 3, 0x00), /* 96 */
/* 3 args for AGI versions before 2.440 */
_L("print.at", 4, 0x00), /* 97 */
_L("print.at.v", 4, 0x80), /* 98 */
_L("discard.view.v", 1, 0x80), /* 99 */
_L("clear.text.rect", 5, 0x00), /* 9A */
_L("set.upper.left", 2, 0x00), /* 9B */
_L("set.menu", 1, 0x00), /* 9C */
_L("set.menu.item", 2, 0x00), /* 9D */
_L("submit.menu", 0, 0x00), /* 9E */
_L("enable.item", 1, 0x00), /* 9F */
_L("disable.item", 1, 0x00), /* A0 */
_L("menu.input", 0, 0x00), /* A1 */
_L("show.obj.v", 1, 0x01), /* A2 */
_L("open.dialogue", 0, 0x00), /* A3 */
_L("close.dialogue", 0, 0x00), /* A4 */
_L("mul.n", 2, 0x80), /* A5 */
_L("mul.v", 2, 0xC0), /* A6 */
_L("div.n", 2, 0x80), /* A7 */
_L("div.v", 2, 0xC0), /* A8 */
_L("close.window", 0, 0x00), /* A9 */
_L("set.simple", 1, 0x00), /* AA */
_L("push.script", 0, 0x00), /* AB */
_L("pop.script", 0, 0x00), /* AC */
_L("hold.key", 0, 0x00), /* AD */
_L("set.pri.base", 1, 0x00), /* AE */
_L("discard.sound", 1, 0x00), /* AF */
/* 1 arg for AGI version 3.002.086 */
_L("hide.mouse", 0, 0x00), /* B0 */
_L("allow.menu", 1, 0x00), /* B1 */
_L("show.mouse", 0, 0x00), /* B2 */
_L("fence.mouse", 4, 0x00), /* B3 */
_L("mouse.posn", 2, 0x00), /* B4 */
_L("release.key", 0, 0x00), /* B5 */
_L("adj.ego.move.to.xy", 0, 0x00), /* B6 */
_L(NULL, 0, 0x00)
};
#ifdef USE_CONSOLE
void debug_console(int lognum, int mode, char *str) {
struct agi_logicnames *x;
uint8 a, c, z;
if (str) {
report(" %s\n", str);
return;
}
report("%03d:%04x ", lognum, ip);
switch (*(code + ip)) {
case 0xFC:
case 0xFD:
case 0xFE:
case 0xFF:
x = logic_names_if;
if (debug_.opcodes) {
report("%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
" ",
(uint8) * (code + (0 + ip)) & 0xFF,
(uint8) * (code + (1 + ip)) & 0xFF,
(uint8) * (code + (2 + ip)) & 0xFF,
(uint8) * (code + (3 + ip)) & 0xFF,
(uint8) * (code + (4 + ip)) & 0xFF,
(uint8) * (code + (5 + ip)) & 0xFF,
(uint8) * (code + (6 + ip)) & 0xFF,
(uint8) * (code + (7 + ip)) & 0xFF,
(uint8) * (code + (8 + ip)) & 0xFF);
}
report("%s ", (x + *(code + ip) - 0xFC)->name);
break;
default:
x = mode == lCOMMAND_MODE ? logic_names_cmd : logic_names_test;
a = (unsigned char)(x + *(code + ip))->num_args;
c = (unsigned char)(x + *(code + ip))->arg_mask;
if (debug_.opcodes) {
report("%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
" ",
(uint8) * (code + (0 + ip)) & 0xFF,
(uint8) * (code + (1 + ip)) & 0xFF,
(uint8) * (code + (2 + ip)) & 0xFF,
(uint8) * (code + (3 + ip)) & 0xFF,
(uint8) * (code + (4 + ip)) & 0xFF,
(uint8) * (code + (5 + ip)) & 0xFF,
(uint8) * (code + (6 + ip)) & 0xFF,
(uint8) * (code + (7 + ip)) & 0xFF,
(uint8) * (code + (8 + ip)) & 0xFF);
}
report("%s ", (x + *(code + ip))->name);
for (z = 1; a > 0;) {
if (~c & 0x80) {
report("%d", *(code + (ip + z)));
} else {
report("v%d[%d]", *(code + (ip + z)), getvar(*(code + (ip + z))));
}
c <<= 1;
z++;
if (--a > 0)
report(",");
}
break;
}
report("\n");
}
#else
void debug_console(int lognum, int mode, char *str) {
/* dummy */
}
#endif /* USE_CONSOLE */
} // End of namespace Agi

419
engines/agi/op_test.cpp Normal file
View File

@ -0,0 +1,419 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/keyboard.h"
#include "agi/opcodes.h"
namespace Agi {
static uint8 test_obj_right(uint8, uint8, uint8, uint8, uint8);
static uint8 test_obj_centre(uint8, uint8, uint8, uint8, uint8);
static uint8 test_obj_in_box(uint8, uint8, uint8, uint8, uint8);
static uint8 test_posn(uint8, uint8, uint8, uint8, uint8);
static uint8 test_said(uint8, uint8 *);
static uint8 test_controller(uint8);
static uint8 test_keypressed(void);
static uint8 test_compare_strings(uint8, uint8);
#define ip (game.logics[lognum].cIP)
#define code (game.logics[lognum].data)
#define test_equal(v1,v2) (getvar(v1) == (v2))
#define test_less(v1,v2) (getvar(v1) < (v2))
#define test_greater(v1,v2) (getvar(v1) > (v2))
#define test_isset(flag) (getflag (flag))
#define test_has(obj) (object_get_location (obj) == EGO_OWNED)
#define test_obj_in_room(obj,v) (object_get_location (obj) == getvar (v))
extern int timer_hack; /* For the timer loop in MH1 logic 153 */
static uint8 test_compare_strings(uint8 s1, uint8 s2) {
char ms1[MAX_STRINGLEN];
char ms2[MAX_STRINGLEN];
int j, k, l;
strcpy(ms1, game.strings[s1]);
strcpy(ms2, game.strings[s2]);
l = strlen(ms1);
for (k = 0, j = 0; k < l; k++) {
switch (ms1[k]) {
case 0x20:
case 0x09:
case '-':
case '.':
case ',':
case ':':
case ';':
case '!':
case '\'':
break;
default:
ms1[j++] = toupper(ms1[k]);
break;
}
}
ms1[j] = 0x0;
l = strlen(ms2);
for (k = 0, j = 0; k < l; k++) {
switch (ms2[k]) {
case 0x20:
case 0x09:
case '-':
case '.':
case ',':
case ':':
case ';':
case '!':
case '\'':
break;
default:
ms2[j++] = toupper(ms2[k]);
break;
}
}
ms2[j] = 0x0;
return !strcmp(ms1, ms2);
}
static uint8 test_keypressed() {
int x = game.keypress;
game.keypress = 0;
if (!x) {
int mode = game.input_mode;
game.input_mode = INPUT_NONE;
main_cycle();
game.input_mode = mode;
}
if (x)
debugC(5, kDebugLevelScripts | kDebugLevelInput, "keypress = %02x", x);
return x;
}
static uint8 test_controller(uint8 cont) {
return game.ev_keyp[cont].occured;
}
static uint8 test_posn(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
struct vt_entry *v = &game.view_table[n];
uint8 r;
r = v->x_pos >= x1 && v->y_pos >= y1 && v->x_pos <= x2 && v->y_pos <= y2;
debugC(7, kDebugLevelScripts, "(%d,%d) in (%d,%d,%d,%d): %s", v->x_pos, v->y_pos, x1, y1, x2, y2, r ? "true" : "false");
return r;
}
static uint8 test_obj_in_box(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
struct vt_entry *v = &game.view_table[n];
return v->x_pos >= x1 &&
v->y_pos >= y1 && v->x_pos + v->x_size - 1 <= x2 && v->y_pos <= y2;
}
/* if n is in centre of box */
static uint8 test_obj_centre(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
struct vt_entry *v = &game.view_table[n];
return v->x_pos + v->x_size / 2 >= x1 &&
v->x_pos + v->x_size / 2 <= x2 && v->y_pos >= y1 && v->y_pos <= y2;
}
/* if nect N is in right corner */
static uint8 test_obj_right(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
struct vt_entry *v = &game.view_table[n];
return v->x_pos + v->x_size - 1 >= x1 &&
v->x_pos + v->x_size - 1 <= x2 && v->y_pos >= y1 && v->y_pos <= y2;
}
/* When player has entered something, it is parsed elsewhere */
static uint8 test_said(uint8 nwords, uint8 *cc) {
int c, n = game.num_ego_words;
int z = 0;
if (getflag(F_said_accepted_input) || !getflag(F_entered_cli))
return false;
/* FR:
* I think the reason for the code below is to add some speed....
*
* if (nwords != num_ego_words)
* return false;
*
* In the disco scene in Larry 1 when you type "examine blonde",
* inside the logic is expected ( said("examine", "blonde", "rol") )
* where word("rol") = 9999
*
* According to the interpreter code 9999 means that whatever the
* user typed should be correct, but it looks like code 9999 means that
* if the string is empty at this point, the entry is also correct...
*
* With the removal of this code, the behaviour of the scene was
* corrected
*/
for (c = 0; nwords && n; c++, nwords--, n--) {
z = READ_LE_UINT16(cc);
cc += 2;
switch (z) {
case 9999: /* rest of line (empty string counts to...) */
nwords = 1;
break;
case 1: /* any word */
break;
default:
if (game.ego_words[c].id != z)
return false;
break;
}
}
/* The entry string should be entirely parsed, or last word = 9999 */
if (n && z != 9999)
return false;
/* The interpreter string shouldn't be entirely parsed, but next
* word must be 9999.
*/
if (nwords != 0 && READ_LE_UINT16(cc) != 9999)
return false;
setflag(F_said_accepted_input, true);
return true;
}
int test_if_code(int lognum) {
int ec = true;
int retval = true;
uint8 op = 0;
uint8 not_test = false;
uint8 or_test = false;
uint16 last_ip = ip;
uint8 p[16] = { 0 };
while (retval && !game.quit_prog_now) {
#ifdef USE_CONSOLE
if (debug_.enabled && (debug_.logic0 || lognum))
debug_console(lognum, lTEST_MODE, NULL);
#endif
last_ip = ip;
op = *(code + ip++);
memmove(p, (code + ip), 16);
switch (op) {
case 0xFF: /* END IF, TEST true */
goto end_test;
case 0xFD:
not_test = !not_test;
continue;
case 0xFC: /* OR */
/* if or_test is ON and we hit 0xFC, end of OR, then
* or is STILL false so break.
*/
if (or_test) {
ec = false;
retval = false;
goto end_test;
}
or_test = true;
continue;
case 0x00:
/* return true? */
goto end_test;
case 0x01:
ec = test_equal(p[0], p[1]);
if (p[0] == 11)
timer_hack++;
break;
case 0x02:
ec = test_equal(p[0], getvar(p[1]));
if (p[0] == 11 || p[1] == 11)
timer_hack++;
break;
case 0x03:
ec = test_less(p[0], p[1]);
if (p[0] == 11)
timer_hack++;
break;
case 0x04:
ec = test_less(p[0], getvar(p[1]));
if (p[0] == 11 || p[1] == 11)
timer_hack++;
break;
case 0x05:
ec = test_greater(p[0], p[1]);
if (p[0] == 11)
timer_hack++;
break;
case 0x06:
ec = test_greater(p[0], getvar(p[1]));
if (p[0] == 11 || p[1] == 11)
timer_hack++;
break;
case 0x07:
ec = test_isset(p[0]);
break;
case 0x08:
ec = test_isset(getvar(p[0]));
break;
case 0x09:
ec = test_has(p[0]);
break;
case 0x0A:
ec = test_obj_in_room(p[0], p[1]);
break;
case 0x0B:
ec = test_posn(p[0], p[1], p[2], p[3], p[4]);
break;
case 0x0C:
ec = test_controller(p[0]);
break;
case 0x0D:
ec = test_keypressed();
break;
case 0x0E:
ec = test_said(p[0], (uint8 *) code + (ip + 1));
ip = last_ip;
ip++; /* skip opcode */
ip += p[0] * 2; /* skip num_words * 2 */
ip++; /* skip num_words opcode */
break;
case 0x0F:
debugC(7, kDebugLevelScripts, "comparing [%s], [%s]", game.strings[p[0]], game.strings[p[1]]);
ec = test_compare_strings(p[0], p[1]);
break;
case 0x10:
ec = test_obj_in_box(p[0], p[1], p[2], p[3], p[4]);
break;
case 0x11:
ec = test_obj_centre(p[0], p[1], p[2], p[3], p[4]);
break;
case 0x12:
ec = test_obj_right(p[0], p[1], p[2], p[3], p[4]);
break;
default:
ec = false;
goto end_test;
}
if (op <= 0x12)
ip += logic_names_test[op].num_args;
/* exchange ec value */
if (not_test)
ec = !ec;
/* not is only enabled for 1 test command */
not_test = false;
if (or_test && ec) {
/* a true inside an OR statement passes
* ENTIRE statement scan for end of OR
*/
/* CM: test for opcode < 0xfc changed from 'op' to
* '*(code+ip)', to avoid problem with the 0xfd (NOT)
* opcode byte. Changed a bad ip += ... ip++ construct.
* This should fix the crash with Larry's logic.0 code:
*
* if ((isset(4) ||
* !isset(2) ||
* v30 == 2 ||
* v30 == 1)) {
* goto Label1;
* }
*
* The bytecode is:
* ff fc 07 04 fd 07 02 01 1e 02 01 1e 01 fc ff
*/
/* find end of OR */
while (*(code + ip) != 0xFC) {
if (*(code + ip) == 0x0E) { /* said */
ip++;
/* cover count + ^words */
ip += 1 + ((*(code + ip)) * 2);
continue;
}
if (*(code + ip) < 0xFC)
ip += logic_names_test[*(code + ip)].num_args;
ip++;
}
ip++;
or_test = false;
retval = true;
} else {
retval = or_test ? retval || ec : retval && ec;
}
}
end_test:
/* if false, scan for end of IP? */
if (retval)
ip += 2;
else {
ip = last_ip;
while (*(code + ip) != 0xff) {
if (*(code + ip) == 0x0e) {
ip++;
ip += (*(code + ip)) * 2 + 1;
} else if (*(code + ip) < 0xfc) {
ip += logic_names_test[*(code + ip)].num_args;
ip++;
} else {
ip++;
}
}
ip++; /* skip over 0xFF */
ip += READ_LE_UINT16(code + ip) + 2;
}
#ifdef USE_CONSOLE
if (debug_.enabled && (debug_.logic0 || lognum))
debug_console(lognum, 0xFF, retval ? (char *)"=true" : (char *)"=false");
#endif
return retval;
}
} // End of namespace Agi

55
engines/agi/opcodes.h Normal file
View File

@ -0,0 +1,55 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_OPCODES_H
#define __AGI_OPCODES_H
#include "agi/agi.h"
namespace Agi {
struct agi_logicnames {
#ifdef USE_CONSOLE /* ifndef NO_DEBUG */
char *name;
#endif
uint16 num_args;
uint16 arg_mask;
};
extern struct agi_logicnames logic_names_test[];
extern struct agi_logicnames logic_names_cmd[];
extern struct agi_logicnames logic_names_if[];
void debug_console(int, int, char *);
int test_if_code(int);
void new_room(int);
void execute_agi_command(uint8, uint8 *);
#ifdef PATCH_LOGIC
void patch_logic(int);
#endif
} // End of namespace Agi
#endif /* __AGI_OPCODES_H */

144
engines/agi/patches.cpp Normal file
View File

@ -0,0 +1,144 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/opcodes.h"
namespace Agi {
#ifdef PATCH_LOGIC
#define ip (game.logics[n].cIP)
#define code (game.logics[n].data)
#define size (game.logics[n].size)
/*
* Patches
*/
static uint8 kq4data_find[] = {
0x0C, 0x04, 0xFF, 0x07, 0x05, 0xFF, 0x15, 0x00,
0x03, 0x0A, 0x00, 0x77, 0x83, 0x71, 0x0D, 0x97,
0x03, 0x98, 0xCE, 0x18, 0x98, 0x19, 0x98, 0x1B,
0x98, 0x0C, 0x5A, 0x1A, 0x00
};
static uint8 kq4data_fix[] = {
/* v19 = 0
* new.room(96)
* return
*/
0x03, 0x13, 0x0, 0x12, 0x60, 0x00
};
static uint8 grdata_find[] = {
0x0C, 0x04, 0xFF, 0x07, 0x05, 0xFF, 0x16, 0x00,
0x0C, 0x96, 0x03, 0x0A, 0x00, 0x77, 0x83, 0x71,
0x0D, 0xD9, 0x03, 0xDC, 0xBF, 0x18, 0xDC, 0x19,
0xDC, 0x1B, 0xDC, 0x0C, 0x95, 0x1A
};
static uint8 grdata_fix[] = {
/* reset(227)
* v19 = 0
* v246 = 1
* set(15)
* new.room(73)
*/
0x0D, 0xE3, 0x03, 0x13, 0x00, 0x03, 0xF6, 0x01,
0x0C, 0x0F, 0x12, 0x49
};
#if 0
static uint8 lsl1data_find[] = {
0xFF, 0xFD, 0x07, 0x1E, 0xFC, 0x07, 0x6D, 0x01,
0x5F, 0x03, 0xFC, 0xFF, 0x12, 0x00, 0x0C, 0x6D,
0x78, 0x8A, 0x77, 0x69, 0x16, 0x18, 0x00, 0x0D,
0x30, 0x0D, 0x55, 0x78, 0x65, 0x0A
};
static uint8 lsl1data_fix[] = {
/* set(109)
* reset(48)
* reset(85)
* accept.input()
* new.room(11)
*/
0x0C, 0x6D, 0x0D, 0x30, 0x0D, 0x55, 0x78, 0x12,
0x0B
};
#endif
static uint8 mh1data_find[] = {
0xFF, 0x07, 0x05, 0xFF, 0xE6, 0x00,
0x03, 0x0A, 0x02, 0x77, 0x83, 0x71,
0x6F, 0x01, 0x17, 0x00, 0x03, 0x00,
0x9F, 0x03, 0x37, 0x00, 0x03, 0x32,
0x03, 0x03, 0x3B, 0x00, 0x6C, 0x03
};
static uint8 mh1data_fix[] = {
0x0C, 0x05, 0x16, 0x5A, 0x12, 0x99
};
void patch_logic(int n) {
switch (n) {
#if 0
/* ALT-X in the questions takes care of that */
case 6:
/* lsl1 bypass questions */
if (!strcmp(game.id, "LLLLL")) {
if (!memcmp(lsl1data_find, (code + ip), 30))
memmove((code + ip), lsl1data_fix, 9);
}
break;
#endif
case 125:
/* gold rush code break */
if (!strcmp(game.id, "GR")) {
if (!memcmp(grdata_find, (code + ip), 30))
memmove((code + ip), grdata_fix, 12);
}
break;
case 140:
/* kings quest 4 code break */
if (!strcmp(game.id, "KQ4")) {
if (memcmp(kq4data_find, (code + ip), 29) == 0)
memmove((code + ip), kq4data_fix, 6);
}
break;
case 159:
/* manhunter 1 amiga */
if (ip + 30 < size && !memcmp(mh1data_find, (code + ip), 30)) {
memmove((code + ip), mh1data_fix, 6);
}
break;
}
}
#endif
} // End of namespace Agi

1034
engines/agi/picture.cpp Normal file

File diff suppressed because it is too large Load Diff

47
engines/agi/picture.h Normal file
View File

@ -0,0 +1,47 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_PICTURE_H
#define __AGI_PICTURE_H
#include "agi/agi.h"
namespace Agi {
/**
* AGI picture resource.
*/
struct agi_picture {
uint32 flen; /**< size of raw data */
uint8 *rdata; /**< raw vector image data */
};
int decode_picture(int, int);
int unload_picture(int);
void show_pic(void);
uint8 *convert_v3_pic(uint8 *data, uint32 len);
} // End of namespace Agi
#endif /* __AGI_PICTURE_H */

790
engines/agi/savegame.cpp Normal file
View File

@ -0,0 +1,790 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
/*
* Savegame support by Vasyl Tsvirkunov <vasyl@pacbell.net>
* Multi-slots by Claudio Matsuoka <claudio@helllabs.org>
*/
#include "common/stdafx.h"
#include "agi/agi.h"
#include "agi/graphics.h"
#include "agi/sprite.h"
#include "agi/text.h"
#include "agi/savegame.h"
#include "agi/keyboard.h"
#include "agi/menu.h"
namespace Agi {
#if defined(WIN32)
#define MKDIR(a,b) mkdir(a)
#else
#define MKDIR(a,b) mkdir(a,b)
#endif
/* Image stack support */
struct image_stack_element {
uint8 type;
uint8 pad;
int16 parm1;
int16 parm2;
int16 parm3;
int16 parm4;
int16 parm5;
int16 parm6;
int16 parm7;
};
#define INITIAL_IMAGE_STACK_SIZE 32
static int stack_size = 0;
static struct image_stack_element *image_stack = NULL;
static int image_stack_pointer = 0;
void clear_image_stack(void) {
image_stack_pointer = 0;
}
void release_image_stack(void) {
if (image_stack)
free(image_stack);
image_stack = NULL;
stack_size = image_stack_pointer = 0;
}
void record_image_stack_call(uint8 type, int16 p1, int16 p2, int16 p3,
int16 p4, int16 p5, int16 p6, int16 p7) {
struct image_stack_element *pnew;
if (image_stack_pointer == stack_size) {
if (stack_size == 0) { /* first call */
image_stack = (struct image_stack_element *)
malloc(INITIAL_IMAGE_STACK_SIZE * sizeof(struct image_stack_element));
stack_size = INITIAL_IMAGE_STACK_SIZE;
} else { /* has to grow */
struct image_stack_element *new_stack;
new_stack = (struct image_stack_element *)
malloc(2 * stack_size * sizeof(struct image_stack_element));
memcpy(new_stack, image_stack, stack_size * sizeof(struct image_stack_element));
free(image_stack);
image_stack = new_stack;
stack_size *= 2;
}
}
pnew = &image_stack[image_stack_pointer];
image_stack_pointer++;
pnew->type = type;
pnew->parm1 = p1;
pnew->parm2 = p2;
pnew->parm3 = p3;
pnew->parm4 = p4;
pnew->parm5 = p5;
pnew->parm6 = p6;
pnew->parm7 = p7;
}
void replay_image_stack_call(uint8 type, int16 p1, int16 p2, int16 p3,
int16 p4, int16 p5, int16 p6, int16 p7) {
switch (type) {
case ADD_PIC:
debugC(8, kDebugLevelMain, "--- decoding picture %d ---", p1);
agi_load_resource(rPICTURE, p1);
decode_picture(p1, p2);
break;
case ADD_VIEW:
agi_load_resource(rVIEW, p1);
add_to_pic(p1, p2, p3, p4, p5, p6, p7);
break;
}
}
/* */
static char *strSig = "AGI:";
static void write_uint8(Common::File *f, int8 val) {
f->write(&val, 1);
}
static void write_sint16(Common::File *f, int16 val) {
static uint8 buf[2];
buf[0] = (uint8) ((val >> 8) & 255);
buf[1] = (uint8) (val & 255);
f->write(buf, 2);
}
static void write_uint16(Common::File *f, uint16 val) {
static uint8 buf[2];
buf[0] = (uint8) ((val >> 8) & 255);
buf[1] = (uint8) (val & 255);
f->write(buf, 2);
}
static uint8 read_uint8(Common::File *f) {
static uint8 buf[1];
f->read(buf, 1);
return buf[0];
}
static uint16 read_uint16(Common::File *f) {
static uint8 buf[2];
f->read(buf, 2);
return (buf[0] << 8) | buf[1];
}
static int16 read_sint16(Common::File *f) {
static uint8 buf[2];
f->read(buf, 2);
return (int16) ((buf[0] << 8) | buf[1]);
}
static void write_string(Common::File *f, char *s) {
write_sint16(f, (int16) strlen(s));
f->write(s, strlen(s));
}
static void read_string(Common::File *f, char *s) {
int16 size = read_sint16(f);
f->read(s, size);
s[size] = (char)0;
}
static void write_bytes(Common::File *f, char *s, int16 size) {
f->write(s, size);
}
static void read_bytes(Common::File *f, char *s, int16 size) {
f->read(s, size);
}
/*
* Version 0: view table has 64 entries
* Version 1: view table has 256 entries (needed in KQ3)
*/
#define SAVEGAME_VERSION 1
int save_game(char *s, char *d) {
int16 i;
struct image_stack_element *ptr = image_stack;
Common::File f;
f.open(s, Common::File::kFileWriteMode);
if (!f.isOpen())
return err_BadFileOpen;
write_bytes(&f, strSig, 8);
write_string(&f, d);
write_uint8(&f, (uint8) SAVEGAME_VERSION);
write_uint8(&f, (uint8) game.state);
/* game.name */
write_string(&f, game.id);
/* game.crc */
for (i = 0; i < MAX_FLAGS; i++)
write_uint8(&f, game.flags[i]);
for (i = 0; i < MAX_VARS; i++)
write_uint8(&f, game.vars[i]);
write_sint16(&f, (int8) game.horizon);
write_sint16(&f, (int16) game.line_status);
write_sint16(&f, (int16) game.line_user_input);
write_sint16(&f, (int16) game.line_min_print);
/* game.cursor_pos */
/* game.input_buffer */
/* game.echo_buffer */
/* game.keypress */
write_sint16(&f, (int16) game.input_mode);
write_sint16(&f, (int16) game.lognum);
write_sint16(&f, (int16) game.player_control);
write_sint16(&f, (int16) game.quit_prog_now);
write_sint16(&f, (int16) game.status_line);
write_sint16(&f, (int16) game.clock_enabled);
write_sint16(&f, (int16) game.exit_all_logics);
write_sint16(&f, (int16) game.picture_shown);
write_sint16(&f, (int16) game.has_prompt);
write_sint16(&f, (int16) game.game_flags);
/* Reversed to keep compatibility with old savegames */
write_sint16(&f, (int16)!game.input_enabled);
for (i = 0; i < _HEIGHT; i++)
write_uint8(&f, game.pri_table[i]);
/* game.msg_box_ticks */
/* game.block */
/* game.window */
/* game.has_window */
write_sint16(&f, (int16)game.gfx_mode);
write_uint8(&f, game.cursor_char);
write_sint16(&f, (int16)game.color_fg);
write_sint16(&f, (int16)game.color_bg);
/* game.hires (#ifdef USE_HIRES) */
/* game.sbuf */
/* game.ego_words */
/* game.num_ego_words */
write_sint16(&f, (int16)game.num_objects);
for (i = 0; i < (int16)game.num_objects; i++)
write_sint16(&f, (int16)object_get_location(i));
/* game.ev_keyp */
for (i = 0; i < MAX_STRINGS; i++)
write_string(&f, game.strings[i]);
/* record info about loaded resources */
for (i = 0; i < MAX_DIRS; i++) {
write_uint8(&f, game.dir_logic[i].flags);
write_sint16(&f, (int16)game.logics[i].sIP);
write_sint16(&f, (int16)game.logics[i].cIP);
}
for (i = 0; i < MAX_DIRS; i++)
write_uint8(&f, game.dir_pic[i].flags);
for (i = 0; i < MAX_DIRS; i++)
write_uint8(&f, game.dir_view[i].flags);
for (i = 0; i < MAX_DIRS; i++)
write_uint8(&f, game.dir_sound[i].flags);
/* game.pictures */
/* game.logics */
/* game.views */
/* game.sounds */
for (i = 0; i < MAX_VIEWTABLE; i++) {
struct vt_entry *v = &game.view_table[i];
write_uint8(&f, v->step_time);
write_uint8(&f, v->step_time_count);
write_uint8(&f, v->entry);
write_sint16(&f, v->x_pos);
write_sint16(&f, v->y_pos);
write_uint8(&f, v->current_view);
/* v->view_data */
write_uint8(&f, v->current_loop);
write_uint8(&f, v->num_loops);
/* v->loop_data */
write_uint8(&f, v->current_cel);
write_uint8(&f, v->num_cels);
/* v->cel_data */
/* v->cel_data_2 */
write_sint16(&f, v->x_pos2);
write_sint16(&f, v->y_pos2);
/* v->s */
write_sint16(&f, v->x_size);
write_sint16(&f, v->y_size);
write_uint8(&f, v->step_size);
write_uint8(&f, v->cycle_time);
write_uint8(&f, v->cycle_time_count);
write_uint8(&f, v->direction);
write_uint8(&f, v->motion);
write_uint8(&f, v->cycle);
write_uint8(&f, v->priority);
write_uint16(&f, v->flags);
write_uint8(&f, v->parm1);
write_uint8(&f, v->parm2);
write_uint8(&f, v->parm3);
write_uint8(&f, v->parm4);
}
/* Save image stack */
for (i = 0; i < image_stack_pointer; i++) {
ptr = &image_stack[i];
write_uint8(&f, ptr->type);
write_sint16(&f, ptr->parm1);
write_sint16(&f, ptr->parm2);
write_sint16(&f, ptr->parm3);
write_sint16(&f, ptr->parm4);
write_sint16(&f, ptr->parm5);
write_sint16(&f, ptr->parm6);
write_sint16(&f, ptr->parm7);
}
write_uint8(&f, 0);
f.close();
return err_OK;
}
int load_game(char *s) {
int i, ver, vt_entries = MAX_VIEWTABLE;
uint8 t;
int16 parm[7];
char sig[8];
char id[8];
char description[256];
Common::File f;
f.open(s);
if (!f.isOpen())
return err_BadFileOpen;
read_bytes(&f, sig, 8);
if (strncmp(sig, strSig, 8)) {
f.close();
return err_BadFileOpen;
}
read_string(&f, description);
ver = read_uint8(&f);
if (ver == 0)
vt_entries = 64;
game.state = read_uint8(&f);
/* game.name - not saved */
read_string(&f, id);
if (strcmp(id, game.id)) {
f.close();
return err_BadFileOpen;
}
/* game.crc - not saved */
for (i = 0; i < MAX_FLAGS; i++)
game.flags[i] = read_uint8(&f);
for (i = 0; i < MAX_VARS; i++)
game.vars[i] = read_uint8(&f);
game.horizon = read_sint16(&f);
game.line_status = read_sint16(&f);
game.line_user_input = read_sint16(&f);
game.line_min_print = read_sint16(&f);
/* These are never saved */
game.cursor_pos = 0;
game.input_buffer[0] = 0;
game.echo_buffer[0] = 0;
game.keypress = 0;
game.input_mode = read_sint16(&f);
game.lognum = read_sint16(&f);
game.player_control = read_sint16(&f);
game.quit_prog_now = read_sint16(&f);
game.status_line = read_sint16(&f);
game.clock_enabled = read_sint16(&f);
game.exit_all_logics = read_sint16(&f);
game.picture_shown = read_sint16(&f);
game.has_prompt = read_sint16(&f);
game.game_flags = read_sint16(&f);
game.input_enabled = !read_sint16(&f);
for (i = 0; i < _HEIGHT; i++)
game.pri_table[i] = read_uint8(&f);
if (game.has_window)
close_window();
game.msg_box_ticks = 0;
game.block.active = false;
/* game.window - fixed by close_window() */
/* game.has_window - fixed by close_window() */
game.gfx_mode = read_sint16(&f);
game.cursor_char = read_uint8(&f);
game.color_fg = read_sint16(&f);
game.color_bg = read_sint16(&f);
/* game.hires (#ifdef USE_HIRES) - rebuilt from image stack */
/* game.sbuf - rebuilt from image stack */
/* game.ego_words - fixed by clean_input */
/* game.num_ego_words - fixed by clean_input */
game.num_objects = read_sint16(&f);
for (i = 0; i < (int16) game.num_objects; i++)
object_set_location(i, read_sint16(&f));
/* Those are not serialized */
for (i = 0; i < MAX_DIRS; i++) {
game.ev_keyp[i].occured = false;
}
for (i = 0; i < MAX_STRINGS; i++)
read_string(&f, game.strings[i]);
for (i = 0; i < MAX_DIRS; i++) {
if (read_uint8(&f) & RES_LOADED)
agi_load_resource(rLOGIC, i);
else
agi_unload_resource(rLOGIC, i);
game.logics[i].sIP = read_sint16(&f);
game.logics[i].cIP = read_sint16(&f);
}
for (i = 0; i < MAX_DIRS; i++) {
if (read_uint8(&f) & RES_LOADED)
agi_load_resource(rPICTURE, i);
else
agi_unload_resource(rPICTURE, i);
}
for (i = 0; i < MAX_DIRS; i++) {
if (read_uint8(&f) & RES_LOADED)
agi_load_resource(rVIEW, i);
else
agi_unload_resource(rVIEW, i);
}
for (i = 0; i < MAX_DIRS; i++) {
if (read_uint8(&f) & RES_LOADED)
agi_load_resource(rSOUND, i);
else
agi_unload_resource(rSOUND, i);
}
/* game.pictures - loaded above */
/* game.logics - loaded above */
/* game.views - loaded above */
/* game.sounds - loaded above */
for (i = 0; i < vt_entries; i++) {
struct vt_entry *v = &game.view_table[i];
v->step_time = read_uint8(&f);
v->step_time_count = read_uint8(&f);
v->entry = read_uint8(&f);
v->x_pos = read_sint16(&f);
v->y_pos = read_sint16(&f);
v->current_view = read_uint8(&f);
/* v->view_data - fixed below */
v->current_loop = read_uint8(&f);
v->num_loops = read_uint8(&f);
/* v->loop_data - fixed below */
v->current_cel = read_uint8(&f);
v->num_cels = read_uint8(&f);
/* v->cel_data - fixed below */
/* v->cel_data_2 - fixed below */
v->x_pos2 = read_sint16(&f);
v->y_pos2 = read_sint16(&f);
/* v->s - fixed below */
v->x_size = read_sint16(&f);
v->y_size = read_sint16(&f);
v->step_size = read_uint8(&f);
v->cycle_time = read_uint8(&f);
v->cycle_time_count = read_uint8(&f);
v->direction = read_uint8(&f);
v->motion = read_uint8(&f);
v->cycle = read_uint8(&f);
v->priority = read_uint8(&f);
v->flags = read_uint16(&f);
v->parm1 = read_uint8(&f);
v->parm2 = read_uint8(&f);
v->parm3 = read_uint8(&f);
v->parm4 = read_uint8(&f);
}
for (i = vt_entries; i < MAX_VIEWTABLE; i++) {
memset(&game.view_table[i], 0, sizeof(struct vt_entry));
}
/* Fix some pointers in viewtable */
for (i = 0; i < MAX_VIEWTABLE; i++) {
struct vt_entry *v = &game.view_table[i];
if (game.dir_view[v->current_view].offset == _EMPTY)
continue;
if (!(game.dir_view[v->current_view].flags & RES_LOADED))
agi_load_resource(rVIEW, v->current_view);
set_view(v, v->current_view); /* Fix v->view_data */
set_loop(v, v->current_loop); /* Fix v->loop_data */
set_cel(v, v->current_cel); /* Fix v->cel_data */
v->cel_data_2 = v->cel_data;
v->s = NULL; /* not sure if it is used... */
}
erase_both();
/* Clear input line */
clear_screen(0);
write_status();
/* Recreate background from saved image stack */
clear_image_stack();
while ((t = read_uint8(&f)) != 0) {
for (i = 0; i < 7; i++)
parm[i] = read_sint16(&f);
replay_image_stack_call(t, parm[0], parm[1], parm[2],
parm[3], parm[4], parm[5], parm[6]);
}
f.close();
setflag(F_restore_just_ran, true);
game.has_prompt = 0; /* force input line repaint if necessary */
clean_input();
erase_both();
blit_both();
commit_both();
show_pic();
do_update();
return err_OK;
}
#define NUM_SLOTS 12
static int select_slot() {
int i, key, active = 0;
int rc = -1;
int hm = 2, vm = 3; /* box margins */
char desc[NUM_SLOTS][40];
for (i = 0; i < NUM_SLOTS; i++) {
char name[MAX_PATH];
Common::File f;
char sig[8];
sprintf(name, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, i);
f.open(name);
if (!f.isOpen()) {
strcpy(desc[i], " (empty slot)");
} else {
read_bytes(&f, sig, 8);
if (strncmp(sig, strSig, 8)) {
strcpy(desc[i], "(corrupt file)");
} else {
read_string(&f, desc[i]);
}
f.close();
}
}
while (42) {
char dstr[64];
for (i = 0; i < NUM_SLOTS; i++) {
sprintf(dstr, "[%-32.32s]", desc[i]);
print_text(dstr, 0, hm + 1, vm + 4 + i,
(40 - 2 * hm) - 1, i == active ? MSG_BOX_COLOUR : MSG_BOX_TEXT,
i == active ? MSG_BOX_TEXT : MSG_BOX_COLOUR);
}
poll_timer(); /* msdos driver -> does nothing */
key = do_poll_keyboard();
if (!console_keyhandler(key)) {
switch (key) {
case KEY_ENTER:
rc = active;
strncpy(game.strings[MAX_STRINGS], desc[i], MAX_STRINGLEN);
goto press;
case KEY_ESCAPE:
rc = -1;
goto getout;
#ifdef USE_MOUSE
case BUTTON_LEFT:
break;
#endif
case KEY_DOWN:
active++;
active %= NUM_SLOTS;
break;
case KEY_UP:
active--;
if (active < 0)
active = NUM_SLOTS - 1;
break;
}
}
console_cycle();
}
press:
debugC(8, kDebugLevelMain | kDebugLevelInput, "Button pressed: %d", rc);
getout:
close_window();
return rc;
}
int savegame_simple() {
char path[MAX_PATH];
sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, 0);
save_game(path, "Default savegame");
return err_OK;
}
int savegame_dialog() {
char path[MAX_PATH];
char *desc;
char *buttons[] = { "Do as I say!", "I regret", NULL };
char dstr[200];
int rc, slot = 0;
int hm, vm, hp, vp; /* box margins */
int w;
hm = 2;
vm = 3;
hp = hm * CHAR_COLS;
vp = vm * CHAR_LINES;
w = (40 - 2 * hm) - 1;
sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot);
draw_window(hp, vp, GFX_WIDTH - hp, GFX_HEIGHT - vp);
print_text("Select a slot in which you wish to save the game:",
0, hm + 1, vm + 1, w, MSG_BOX_TEXT, MSG_BOX_COLOUR);
print_text("Press ENTER to select, ESC cancels",
0, hm + 1, vm + 17, w, MSG_BOX_TEXT, MSG_BOX_COLOUR);
slot = select_slot();
if (slot < 0) /* ESC pressed */
return err_OK;
/* Get savegame description */
draw_window(hp, vp + 5 * CHAR_LINES, GFX_WIDTH - hp,
GFX_HEIGHT - vp - 9 * CHAR_LINES);
print_text("Enter a description for this game:",
0, hm + 1, vm + 6, w, MSG_BOX_TEXT, MSG_BOX_COLOUR);
draw_rectangle(3 * CHAR_COLS, 11 * CHAR_LINES - 1,
37 * CHAR_COLS, 12 * CHAR_LINES, MSG_BOX_TEXT);
flush_block(3 * CHAR_COLS, 11 * CHAR_LINES - 1,
37 * CHAR_COLS, 12 * CHAR_LINES);
get_string(2, 11, 33, MAX_STRINGS);
print_character(3, 11, game.cursor_char, MSG_BOX_COLOUR, MSG_BOX_TEXT);
do {
main_cycle();
} while (game.input_mode == INPUT_GETSTRING);
close_window();
desc = game.strings[MAX_STRINGS];
sprintf(dstr, "Are you sure you want to save the game "
"described as:\n\n%s\n\nin slot %d?\n\n\n", desc, slot);
rc = selection_box(dstr, buttons);
if (rc != 0) {
message_box("Game NOT saved.");
return err_OK;
}
sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot);
debugC(8, kDebugLevelMain | kDebugLevelResources, "file is [%s]", path);
save_game(path, desc);
message_box("Game saved.");
return err_OK;
}
int loadgame_simple() {
char path[MAX_PATH];
int rc = 0;
sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, 0);
erase_both();
stop_sound();
close_window();
if ((rc = load_game(path)) == err_OK) {
message_box("Game restored.");
game.exit_all_logics = 1;
menu_enable_all();
} else {
message_box("Error restoring game.");
}
return rc;
}
int loadgame_dialog() {
char path[MAX_PATH];
int rc, slot = 0;
int hm, vm, hp, vp; /* box margins */
int w;
hm = 2;
vm = 3;
hp = hm * CHAR_COLS;
vp = vm * CHAR_LINES;
w = (40 - 2 * hm) - 1;
sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot);
erase_both();
stop_sound();
draw_window(hp, vp, GFX_WIDTH - hp, GFX_HEIGHT - vp);
print_text("Select a game which you wish to\nrestore:",
0, hm + 1, vm + 1, w, MSG_BOX_TEXT, MSG_BOX_COLOUR);
print_text("Press ENTER to select, ESC cancels",
0, hm + 1, vm + 17, w, MSG_BOX_TEXT, MSG_BOX_COLOUR);
slot = select_slot();
if (slot < 0) {
message_box("Game NOT restored.");
return err_OK;
}
sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot);
if ((rc = load_game(path)) == err_OK) {
message_box("Game restored.");
game.exit_all_logics = 1;
menu_enable_all();
} else {
message_box("Error restoring game.");
}
return rc;
}
} // End of namespace Agi

50
engines/agi/savegame.h Normal file
View File

@ -0,0 +1,50 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_SAVEGAME_H
#define __AGI_SAVEGAME_H
#include "agi/agi.h"
namespace Agi {
int savegame_dialog(void);
int loadgame_dialog(void);
int savegame_simple(void);
int loadgame_simple(void);
/* Image stack support */
#define ADD_PIC 1
#define ADD_VIEW 2
void clear_image_stack(void);
void record_image_stack_call(uint8 type, int16 p1, int16 p2, int16 p3,
int16 p4, int16 p5, int16 p6, int16 p7);
void replay_image_stack_call(uint8 type, int16 p1, int16 p2, int16 p3,
int16 p4, int16 p5, int16 p6, int16 p7);
void release_image_stack(void);
} // End of namespace Agi
#endif

772
engines/agi/sound.cpp Normal file
View File

@ -0,0 +1,772 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "sound/mixer.h"
#include "agi/agi.h"
namespace Agi {
#define USE_INTERPOLATION
#define USE_CHORUS
/* TODO: add support for variable sampling rate in the output device
*/
#ifdef USE_IIGS_SOUND
/**
* AGI engine sound envelope structure.
*/
struct sound_envelope {
uint8 bp;
uint8 inc_hi;
uint8 inc_lo;
};
struct sound_wavelist {
uint8 top;
uint8 addr;
uint8 size;
uint8 mode;
uint8 rel_hi;
uint8 rel_lo;
};
struct sound_instrument {
struct sound_envelope env[8];
uint8 relseg;
uint8 priority;
uint8 bendrange;
uint8 vibdepth;
uint8 vibspeed;
uint8 spare;
uint8 wac;
uint8 wbc;
struct sound_wavelist wal[8];
struct sound_wavelist wbl[8];
};
struct sound_iigs_sample {
uint8 type_lo;
uint8 type_hi;
uint8 srate_lo;
uint8 srate_hi;
uint16 unknown[2];
uint8 size_lo;
uint8 size_hi;
uint16 unknown2[13];
};
#if 0
static struct sound_instrument *instruments;
static int num_instruments;
static uint8 *wave;
#endif
#endif
static int playing;
static struct channel_info chn[NUM_CHANNELS];
static int endflag = -1;
static int playing_sound = -1;
static uint8 *song;
static uint8 env;
struct sound_driver *snd;
extern struct sound_driver sound_dummy;
static void stop_note(int i);
static void play_note(int i, int freq, int vol);
#ifdef USE_PCM_SOUND
int16 *snd_buffer;
static int16 *waveform;
static int16 waveform_ramp[WAVEFORM_SIZE] = {
0, 8, 16, 24, 32, 40, 48, 56,
64, 72, 80, 88, 96, 104, 112, 120,
128, 136, 144, 152, 160, 168, 176, 184,
192, 200, 208, 216, 224, 232, 240, 255,
0, -248, -240, -232, -224, -216, -208, -200,
-192, -184, -176, -168, -160, -152, -144, -136,
-128, -120, -112, -104, -96, -88, -80, -72,
-64, -56, -48, -40, -32, -24, -16, -8 /* Ramp up */
};
static int16 waveform_square[WAVEFORM_SIZE] = {
255, 230, 220, 220, 220, 220, 220, 220,
220, 220, 220, 220, 220, 220, 220, 220,
220, 220, 220, 220, 220, 220, 220, 220,
220, 220, 220, 220, 220, 220, 220, 110,
-255, -230, -220, -220, -220, -220, -220, -220,
-220, -220, -220, -220, -220, -220, -220, -220,
-220, -220, -220, -220, -220, -220, -220, -220,
-220, -220, -220, -110, 0, 0, 0, 0 /* Square */
};
static int16 waveform_mac[WAVEFORM_SIZE] = {
45, 110, 135, 161, 167, 173, 175, 176,
156, 137, 123, 110, 91, 72, 35, -2,
-60, -118, -142, -165, -170, -176, -177, -179,
-177, -176, -164, -152, -117, -82, -17, 47,
92, 137, 151, 166, 170, 173, 171, 169,
151, 133, 116, 100, 72, 43, -7, -57,
-99, -141, -156, -170, -174, -177, -178, -179,
-175, -172, -165, -159, -137, -114, -67, -19
};
#endif /* USE_PCM_SOUND */
#ifdef USE_IIGS_SOUND
static uint16 period[] = {
1024, 1085, 1149, 1218, 1290, 1367,
1448, 1534, 1625, 1722, 1825, 1933
};
static struct agi_note play_sample[] = {
{0xff, 0x7f, 0x18, 0x00, 0x7f},
{0xff, 0xff, 0x00, 0x00, 0x00},
{0xff, 0xff, 0x00, 0x00, 0x00},
{0xff, 0xff, 0x00, 0x00, 0x00}
};
static int note_to_period(int note) {
return 10 * (period[note % 12] >> (note / 12 - 3));
}
#endif /* USE_IIGS_SOUND */
void unload_sound(int resnum) {
if (game.dir_sound[resnum].flags & RES_LOADED) {
if (game.sounds[resnum].flags & SOUND_PLAYING)
/* FIXME: Stop playing */
;
/* Release RAW data for sound */
free(game.sounds[resnum].rdata);
game.sounds[resnum].rdata = NULL;
game.dir_sound[resnum].flags &= ~RES_LOADED;
}
}
void decode_sound(int resnum) {
#ifdef USE_IIGS_SOUND
int type, size;
int16 *buf;
uint8 *src;
struct sound_iigs_sample *smp;
debugC(3, kDebugLevelSound, "(%d)", resnum);
type = READ_LE_UINT16(game.sounds[resnum].rdata);
if (type == AGI_SOUND_SAMPLE) {
/* Convert sample data to 16 bit signed format
*/
smp = (struct sound_iigs_sample *)game.sounds[resnum].rdata;
size = ((int)smp->size_hi << 8) + smp->size_lo;
src = (uint8 *) game.sounds[resnum].rdata;
buf = (int16 *) calloc(1, 54 + (size << 1) + 100); /* FIXME */
memcpy(buf, src, 54);
for (; size--; buf[size + 54] = ((int16) src[size + 54] - 0x80) << 4); /* FIXME */
game.sounds[resnum].rdata = (uint8 *) buf;
free(src);
}
#endif /* USE_IIGS_SOUND */
}
void start_sound(int resnum, int flag) {
int i, type;
#ifdef USE_IIGS_SOUND
struct sound_iigs_sample *smp;
#endif
if (game.sounds[resnum].flags & SOUND_PLAYING)
return;
stop_sound();
if (game.sounds[resnum].rdata == NULL)
return;
type = READ_LE_UINT16(game.sounds[resnum].rdata);
if (type != AGI_SOUND_SAMPLE && type != AGI_SOUND_MIDI && type != AGI_SOUND_4CHN)
return;
game.sounds[resnum].flags |= SOUND_PLAYING;
game.sounds[resnum].type = type;
playing_sound = resnum;
song = (uint8 *) game.sounds[resnum].rdata;
switch (type) {
#ifdef USE_IIGS_SOUND
case AGI_SOUND_SAMPLE:
debugC(3, kDebugLevelSound, "IIGS sample");
smp = (struct sound_iigs_sample *)game.sounds[resnum].rdata;
for (i = 0; i < NUM_CHANNELS; i++) {
chn[i].type = type;
chn[i].flags = 0;
chn[i].ins = (int16 *) & game.sounds[resnum].rdata[54];
chn[i].size = ((int)smp->size_hi << 8) + smp->size_lo;
chn[i].ptr = &play_sample[i];
chn[i].timer = 0;
chn[i].vol = 0;
chn[i].end = 0;
}
break;
case AGI_SOUND_MIDI:
debugC(3, kDebugLevelSound, "IIGS MIDI sequence");
for (i = 0; i < NUM_CHANNELS; i++) {
chn[i].type = type;
chn[i].flags = AGI_SOUND_LOOP | AGI_SOUND_ENVELOPE;
chn[i].ins = waveform;
chn[i].size = WAVEFORM_SIZE;
chn[i].vol = 0;
chn[i].end = 0;
}
chn[0].timer = *(song + 2);
chn[0].ptr = (struct agi_note *)(song + 3);
break;
#endif
case AGI_SOUND_4CHN:
/* Initialize channel info */
for (i = 0; i < NUM_CHANNELS; i++) {
chn[i].type = type;
chn[i].flags = AGI_SOUND_LOOP;
if (env) {
chn[i].flags |= AGI_SOUND_ENVELOPE;
chn[i].adsr = AGI_SOUND_ENV_ATTACK;
}
#ifdef USE_PCM_SOUND
chn[i].ins = waveform;
chn[i].size = WAVEFORM_SIZE;
#endif
chn[i].ptr = (struct agi_note *)(song + (song[i << 1] | (song[(i << 1) + 1] << 8)));
chn[i].timer = 0;
chn[i].vol = 0;
chn[i].end = 0;
}
break;
}
#ifdef USE_PCM_SOUND
memset(snd_buffer, 0, BUFFER_SIZE << 1);
#endif
endflag = flag;
/* Nat Budin reports that the flag should be reset when sound starts
*/
setflag(endflag, false);
/* FIXME: should wait for sound time instead of setting the flag
* immediately
*/
if (opt.nosound) {
setflag(endflag, true);
stop_sound();
}
}
void stop_sound() {
int i;
endflag = -1;
for (i = 0; i < NUM_CHANNELS; i++)
stop_note(i);
if (playing_sound != -1) {
game.sounds[playing_sound].flags &= ~SOUND_PLAYING;
playing_sound = -1;
}
}
static int16 *buffer;
int init_sound() {
int r = -1;
#ifdef USE_PCM_SOUND
buffer = snd_buffer = (int16 *) calloc(2, BUFFER_SIZE);
#endif
env = false;
#ifdef USE_PCM_SOUND
switch (opt.soundemu) {
case SOUND_EMU_NONE:
waveform = waveform_ramp;
env = true;
break;
case SOUND_EMU_AMIGA:
case SOUND_EMU_PC:
waveform = waveform_square;
break;
case SOUND_EMU_MAC:
waveform = waveform_mac;
break;
}
#endif
report("Initializing sound:\n");
report("sound: envelopes ");
if (env) {
report("enabled (decay=%d, sustain=%d)\n", ENV_DECAY, ENV_SUSTAIN);
} else {
report("disabled\n");
}
#ifdef USE_IIGS_SOUND
/*load_instruments ("demo.sys"); */
#endif
return r;
}
void deinit_sound(void) {
debugC(3, kDebugLevelSound, "()");
if (snd)
snd->deinit();
#ifdef USE_PCM_SOUND
free(snd_buffer);
#endif
}
static void stop_note(int i) {
chn[i].adsr = AGI_SOUND_ENV_RELEASE;
#ifdef USE_CHORUS
/* Stop chorus ;) */
if (chn[i].type == AGI_SOUND_4CHN &&
opt.soundemu == SOUND_EMU_NONE && i < 3) {
stop_note(i + 4);
}
#endif
#ifdef __TURBOC__
if (i == 0)
nosound();
#endif
}
static void play_note(int i, int freq, int vol) {
if (!getflag(F_sound_on))
vol = 0;
else if (vol && opt.soundemu == SOUND_EMU_PC)
vol = 160;
#ifdef USE_PCM_SOUND
chn[i].phase = 0;
#endif
chn[i].freq = freq;
chn[i].vol = vol;
chn[i].env = 0x10000;
chn[i].adsr = AGI_SOUND_ENV_ATTACK;
#ifdef USE_CHORUS
/* Add chorus ;) */
if (chn[i].type == AGI_SOUND_4CHN &&
opt.soundemu == SOUND_EMU_NONE && i < 3) {
int newfreq = freq * 1007 / 1000;
if (freq == newfreq)
newfreq++;
play_note(i + 4, newfreq, vol * 2 / 3);
}
#endif
#ifdef __TURBOC__
if (i == 0)
sound(freq);
#endif
}
#ifdef USE_IIGS_SOUND
void play_midi_sound() {
uint8 *p;
uint8 parm1, parm2;
static uint8 cmd, ch;
playing = 1;
if (chn[0].timer > 0) {
chn[0].timer -= 2;
return;
}
p = (uint8 *) chn[0].ptr;
if (*p & 0x80) {
cmd = *p++;
ch = cmd & 0x0f;
cmd >>= 4;
}
switch (cmd) {
case 0x08:
parm1 = *p++;
parm2 = *p++;
if (ch < NUM_CHANNELS)
stop_note(ch);
break;
case 0x09:
parm1 = *p++;
parm2 = *p++;
if (ch < NUM_CHANNELS)
play_note(ch, note_to_period(parm1), 127);
break;
case 0x0b:
parm1 = *p++;
parm2 = *p++;
debugC(3, kDebugLevelSound, "controller %02x, ch %02x, val %02x", parm1, ch, parm2);
break;
case 0x0c:
parm1 = *p++;
#if 0
if (ch < NUM_CHANNELS) {
chn[ch].ins = (uint16 *) & wave[waveaddr[parm1]];
chn[ch].size = wavesize[parm1];
}
debugC(3, kDebugLevelSound, "set patch %02x (%d,%d), ch %02x",
parm1, waveaddr[parm1], wavesize[parm1], ch);
#endif
break;
}
chn[0].timer = *p++;
chn[0].ptr = (struct agi_note *)p;
if (*p >= 0xfc) {
debugC(3, kDebugLevelSound, "end of sequence");
playing = 0;
return;
}
}
void play_sample_sound() {
play_note(0, 11025 * 10, 200);
playing = 1;
}
#endif /* USE_IIGS_SOUND */
void play_agi_sound() {
int i, freq;
for (playing = i = 0; i < (opt.soundemu == SOUND_EMU_PC ? 1 : 4); i++) {
playing |= !chn[i].end;
if (chn[i].end)
continue;
if ((--chn[i].timer) <= 0) {
stop_note(i);
freq = ((chn[i].ptr->frq_0 & 0x3f) << 4) | (int)(chn[i].ptr->frq_1 & 0x0f);
if (freq) {
uint8 v = chn[i].ptr->vol & 0x0f;
play_note(i, freq * 10, v == 0xf ? 0 : 0xff - (v << 1));
}
chn[i].timer = ((int)chn[i].ptr->dur_hi << 8) | chn[i].ptr->dur_lo;
if (chn[i].timer == 0xffff) {
chn[i].end = 1;
chn[i].vol = 0;
chn[i].env = 0;
#ifdef USE_CHORUS
/* chorus */
if (chn[i].type == AGI_SOUND_4CHN && opt.soundemu == SOUND_EMU_NONE && i < 3) {
chn[i + 4].vol = 0;
chn[i + 4].env = 0;
}
#endif
}
chn[i].ptr++;
}
}
}
void play_sound() {
int i;
if (endflag == -1)
return;
#ifdef USE_IIGS_SOUND
if (chn[0].type == AGI_SOUND_MIDI) {
/* play_midi_sound (); */
playing = 0;
} else if (chn[0].type == AGI_SOUND_SAMPLE) {
play_sample_sound();
} else
#endif
play_agi_sound();
if (!playing) {
for (i = 0; i < NUM_CHANNELS; chn[i++].vol = 0);
if (endflag != -1)
setflag(endflag, true);
if (playing_sound != -1)
game.sounds[playing_sound].flags &= ~SOUND_PLAYING;
playing_sound = -1;
endflag = -1;
}
}
#ifdef USE_PCM_SOUND
uint32 mix_sound(void) {
register int i, p;
int16 *src;
int c, b, m;
memset(snd_buffer, 0, BUFFER_SIZE << 1);
for (c = 0; c < NUM_CHANNELS; c++) {
if (!chn[c].vol)
continue;
m = chn[c].flags & AGI_SOUND_ENVELOPE ?
chn[c].vol * chn[c].env >> 16 : chn[c].vol;
if (chn[c].type != AGI_SOUND_4CHN || c != 3) {
src = chn[c].ins;
p = chn[c].phase;
for (i = 0; i < BUFFER_SIZE; i++) {
b = src[p >> 8];
#ifdef USE_INTERPOLATION
b += ((src[((p >> 8) + 1) % chn[c].size] - src[p >> 8]) * (p & 0xff)) >> 8;
#endif
snd_buffer[i] += (b * m) >> 4;
p += (uint32) 118600 *4 / chn[c].freq;
/* FIXME */
if (chn[c].flags & AGI_SOUND_LOOP) {
p %= chn[c].size << 8;
} else {
if (p >= chn[c].size << 8) {
p = chn[c].vol = 0;
chn[c].end = 1;
break;
}
}
}
chn[c].phase = p;
} else {
/* Add white noise */
for (i = 0; i < BUFFER_SIZE; i++) {
b = rnd->getRandomNumber(255) - 128;
snd_buffer[i] += (b * m) >> 4;
}
}
switch (chn[c].adsr) {
case AGI_SOUND_ENV_ATTACK:
/* not implemented */
chn[c].adsr = AGI_SOUND_ENV_DECAY;
break;
case AGI_SOUND_ENV_DECAY:
if (chn[c].env > chn[c].vol * ENV_SUSTAIN + ENV_DECAY) {
chn[c].env -= ENV_DECAY;
} else {
chn[c].env = chn[c].vol * ENV_SUSTAIN;
chn[c].adsr = AGI_SOUND_ENV_SUSTAIN;
}
break;
case AGI_SOUND_ENV_SUSTAIN:
break;
case AGI_SOUND_ENV_RELEASE:
if (chn[c].env >= ENV_RELEASE) {
chn[c].env -= ENV_RELEASE;
} else {
chn[c].env = 0;
}
}
}
return BUFFER_SIZE;
}
#ifdef USE_IIGS_SOUND
#if 0
int load_instruments(char *fname) {
Common::File fp;
int i, j, k;
struct sound_instrument ai;
int num_wav;
char *path;
path = "sierrast";
if (!fp.open(path))
return err_BadFileOpen;
report("Loading samples: %s\n", path);
if ((wave = malloc(0x10000 * 2)) == NULL)
return err_NotEnoughMemory;
fp.read(wave, 0x10000);
fp.close();
for (i = 0x10000; i--;) {
((int16 *) wave)[i] = 2 * ((int16) wave[i] - 128);
}
fp = fopen("bla", "w");
fwrite(wave, 2, 0x10000, fp);
fclose(fp);
report("Loading instruments: %s\n", path);
if ((fp = fopen(path, "rb")) == NULL)
return err_BadFileOpen;
fseek(fp, 0x8469, SEEK_SET);
for (num_wav = j = 0; j < 40; j++) {
fread(&ai, 1, 32, fp);
if (ai.env[0].bp > 0x7f)
break;
#if 0
printf("Instrument %d loaded ----------------\n", j);
printf("Envelope:\n");
for (i = 0; i < 8; i++)
printf("[seg %d]: BP %02x Inc %04x\n", i, ai.env[i].bp,
((int)ai.env[i].inc_hi << 8) | ai.env[i].inc_lo);
printf("rel seg: %d, pri inc: %d, bend range: %d, vib dep: %d, "
"vib spd: %d\n", ai.relseg, ai.priority,
ai.bendrange, ai.vibdepth, ai.vibspeed);
printf("A wave count: %d, B wave count: %d\n", ai.wac, ai.wbc);
#endif
for (k = 0; k < ai.wac; k++, num_wav++) {
fread(&ai.wal[k], 1, 6, fp);
#if 0
printf("[A %d of %d] top: %02x, wave address: %02x, "
"size: %02x, mode: %02x, relPitch: %04x\n", k + 1,
ai.wac, ai.wal[k].top, ai.wal[k].addr, ai.wal[k].size,
ai.wal[k].mode, ((int)ai.wal[k].rel_hi << 8) | ai.wal[k].rel_lo);
#endif
}
for (k = 0; k < ai.wbc; k++, num_wav++) {
fread(&ai.wbl[k], 1, 6, fp);
#if 0
printf("[B %d of %d] top: %02x, wave address: %02x, "
"size: %02x, mode: %02x, relPitch: %04x\n", k + 1, ai.wbc,
ai.wbl[k].top, ai.wbl[k].addr, ai.wbl[k].size,
ai.wbl[k].mode, ((int)ai.wbl[k].rel_hi << 8) | ai.wbl[k].rel_lo);
#endif
}
waveaddr[j] = 256 * ai.wal[0].addr;
wavesize[j] = 256 * (1 << ((ai.wal[0].size) & 0x07));
#if 1
printf("%d addr = %d\n", j, waveaddr[j]);
printf(" size = %d\n", wavesize[j]);
#endif
}
num_instruments = j;
printf("%d Ensoniq 5503 instruments loaded. (%d waveforms)\n", num_instruments, num_wav);
fclose(fp);
return err_OK;
}
void unload_instruments() {
free(instruments);
}
#endif
#endif /* USE_IIGS_SOUND */
static void fill_audio(void *udata, int16 * stream, uint len) {
int16 *origData = stream;
len <<= 2;
uint origLen = len;
uint32 p = 0;
static uint32 n = 0, s = 0;
debugC(5, kDebugLevelSound, "(%p, %p, %d)", udata, stream, len);
memcpy(stream, (uint8 *) buffer + s, p = n);
for (n = 0, len -= p; n < len; p += n, len -= n) {
play_sound();
n = mix_sound() << 1;
if (len < n) {
memcpy((uint8 *) stream + p, buffer, len);
s = len;
n -= s;
return;
} else {
memcpy((uint8 *) stream + p, buffer, n);
}
}
play_sound();
n = mix_sound() << 1;
memcpy((uint8 *) stream + p, buffer, s = len);
n -= s;
}
AGIMusic::AGIMusic(Audio::Mixer *pMixer) {
_mixer = pMixer;
_sampleRate = pMixer->getOutputRate();
_mixer->setupPremix(this);
}
void AGIMusic::premixerCall(int16 *data, uint len) {
Agi::fill_audio(NULL, data, len);
}
void AGIMusic::setVolume(uint8 volume) {
// TODO
}
AGIMusic::~AGIMusic(void) {
_mixer->setupPremix(NULL);
}
AGIMusic *g_agi_music;
#endif /* USE_PCM_SOUND */
} // End of namespace Agi

167
engines/agi/sound.h Normal file
View File

@ -0,0 +1,167 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_SOUND_H
#define __AGI_SOUND_H
#include "agi/agi.h"
namespace Agi {
#define BUFFER_SIZE 410
#define SOUND_EMU_NONE 0
#define SOUND_EMU_PC 1
#define SOUND_EMU_TANDY 2
#define SOUND_EMU_MAC 3
#define SOUND_EMU_AMIGA 4
#define SOUND_PLAYING 0x01
#define WAVEFORM_SIZE 64
#define ENV_ATTACK 10000 /**< envelope attack rate */
#define ENV_DECAY 1000 /**< envelope decay rate */
#define ENV_SUSTAIN 100 /**< envelope sustain level */
#define ENV_RELEASE 7500 /**< envelope release rate */
#define NUM_CHANNELS 7 /**< number of sound channels */
/**
* AGI engine sound driver structure.
*/
struct sound_driver {
char *description;
int (*init) (int16 * buffer);
void (*deinit) (void);
};
/**
* AGI sound resource structure.
*/
struct agi_sound {
uint32 flen; /**< size of raw data */
uint8 *rdata; /**< raw sound data */
uint8 flags; /**< sound flags */
uint16 type; /**< sound resource type */
};
/**
* AGI sound note structure.
*/
struct agi_note {
uint8 dur_lo; /**< LSB of note duration */
uint8 dur_hi; /**< MSB of note duration */
uint8 frq_0; /**< LSB of note frequency */
uint8 frq_1; /**< MSB of note frequency */
uint8 vol; /**< note volume */
};
/**
* AGI engine sound channel structure.
*/
struct channel_info {
#define AGI_SOUND_SAMPLE 0x0001
#define AGI_SOUND_MIDI 0x0002
#define AGI_SOUND_4CHN 0x0008
uint32 type;
struct agi_note *ptr;
#ifdef USE_PCM_SOUND
int16 *ins;
int32 size;
uint32 phase;
#endif
#define AGI_SOUND_LOOP 0x0001
#define AGI_SOUND_ENVELOPE 0x0002
uint32 flags;
#define AGI_SOUND_ENV_ATTACK 3
#define AGI_SOUND_ENV_DECAY 2
#define AGI_SOUND_ENV_SUSTAIN 1
#define AGI_SOUND_ENV_RELEASE 0
uint32 adsr;
int32 timer;
uint32 end;
uint32 freq;
uint32 vol;
uint32 env;
};
void decode_sound(int);
void unload_sound(int);
void play_sound(void);
int init_sound(void);
void deinit_sound(void);
void start_sound(int, int);
void stop_sound(void);
uint32 mix_sound(void);
void __init_sound(void);
int load_instruments(char *fname);
extern struct sound_driver *snd;
#endif /* __AGI_SOUND_H */
#ifdef USE_PCM_SOUND
} // End of namespace Agi
#include "sound/audiostream.h"
namespace Audio {
class Mixer;
} // End of namespace Audio
namespace Agi {
class AGIMusic : public Audio::AudioStream {
public:
AGIMusic(Audio::Mixer * pMixer);
~AGIMusic(void);
virtual void setVolume(uint8 volume);
// AudioStream API
int readBuffer(int16 * buffer, const int numSamples) {
premixerCall(buffer, numSamples / 2);
return numSamples;
}
bool isStereo() const {
return false;
}
bool endOfData() const {
return false;
}
int getRate() const {
return _sampleRate;
}
private:
Audio::Mixer * _mixer;
uint32 _sampleRate;
void premixerCall(int16 * buf, uint len);
};
} // End of namespace Agi
#endif

868
engines/agi/sprite.cpp Normal file
View File

@ -0,0 +1,868 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "agi/agi.h"
#include "agi/list.h"
#include "agi/sprite.h"
#include "agi/graphics.h"
#include "agi/text.h"
#include "agi/savegame.h"
namespace Agi {
/**
* Sprite structure.
* This structure holds information on visible and priority data of
* a rectangular area of the AGI screen. Sprites are chained in two
* circular lists, one for updating and other for non-updating sprites.
*/
struct sprite {
struct list_head list;
struct vt_entry *v; /**< pointer to view table entry */
int16 x_pos; /**< x coordinate of the sprite */
int16 y_pos; /**< y coordinate of the sprite */
int16 x_size; /**< width of the sprite */
int16 y_size; /**< height of the sprite */
uint8 *buffer; /**< buffer to store background data */
#ifdef USE_HIRES
uint8 *hires; /**< buffer for hi-res background */
#endif
};
/*
* Sprite pool replaces dynamic allocation
*/
#undef ALLOC_DEBUG
#ifdef USE_HIRES
#define POOL_SIZE 68000 /* Gold Rush mine room needs > 50000 */
/* Speeder bike challenge needs > 67000 */
#else
#define POOL_SIZE 25000
#endif
static uint8 *sprite_pool;
static uint8 *pool_top;
static void *pool_alloc(int size) {
uint8 *x;
/* Adjust size to 32-bit boundary to prevent data misalignment
* errors.
*/
size = (size + 3) & ~3;
x = pool_top;
pool_top += size;
if (pool_top >= (uint8 *)sprite_pool + POOL_SIZE) {
debugC(1, kDebugLevelMain | kDebugLevelResources, "not enough memory");
pool_top = x;
return NULL;
}
return x;
}
static void pool_release(void *s) {
pool_top = (uint8 *)s;
}
/*
* Blitter functions
*/
/* Blit one pixel considering the priorities */
static void blit_pixel(uint8 *p, uint8 *end, uint8 col, int spr, int width, int *hidden) {
int epr = 0, pr = 0; /* effective and real priorities */
/* CM: priority 15 overrides control lines and is ignored when
* tracking effective priority. This tweak is needed to fix
* bug #451768, and should not affect Sierra games because
* sprites shouldn't have priority 15 (like the AGI Mouse
* demo "mouse pointer")
*
* Update: this solution breaks other games, and can't be used.
*/
if (p >= end)
return;
/* Check if we're on a control line */
if ((pr = *p & 0xf0) < 0x30) {
uint8 *p1;
/* Yes, get effective priority going down */
for (p1 = p; p1 < end && (epr = *p1 & 0xf0) < 0x30;
p1 += width);
if (p1 >= end)
epr = 0x40;
} else {
epr = pr;
}
if (spr >= epr) {
/* Keep control line information visible, but put our
* priority over water (0x30) surface
*/
*p = (pr < 0x30 ? pr : spr) | col;
*hidden = false;
/* Except if our priority is 15, which should never happen
* (fixes bug #451768)
*
* Update: breaks other games, can't be used
*
* if (spr == 0xf0)
* *p = spr | col;
*/
}
}
#ifdef USE_HIRES
#define X_FACT 2 /* Horizontal hires factor */
static int blit_hires_cel(int x, int y, int spr, struct view_cel *c) {
uint8 *q = NULL;
uint8 *h0, *h, *end;
int i, j, t, m, col;
int hidden = true;
q = c->data;
t = c->transparency;
m = c->mirror;
spr <<= 4;
h0 = &game.hires[(x + y * _WIDTH + m * (c->width - 1)) * X_FACT];
end = game.hires + _WIDTH * X_FACT * _HEIGHT;
for (i = 0; i < c->height; i++) {
h = h0;
while (*q) {
col = (*q & 0xf0) >> 4;
for (j = *q & 0x0f; j; j--, h += X_FACT * (1 - 2 * m)) {
if (col != t) {
blit_pixel(h, end, col, spr, _WIDTH * X_FACT, &hidden);
blit_pixel(h + 1, end, col, spr, _WIDTH * X_FACT, &hidden);
}
}
q++;
}
h0 += _WIDTH * X_FACT;
q++;
}
return hidden;
}
#endif
static int blit_cel(int x, int y, int spr, struct view_cel *c) {
uint8 *p0, *p, *q = NULL, *end;
int i, j, t, m, col;
int hidden = true;
/* Fixes bug #477841 (crash in PQ1 map C4 when y == -2) */
if (y < 0)
y = 0;
if (x < 0)
x = 0;
if (y >= _HEIGHT)
y = _HEIGHT - 1;
if (x >= _WIDTH)
x = _WIDTH - 1;
#ifdef USE_HIRES
if (opt.hires)
blit_hires_cel(x, y, spr, c);
#endif
q = c->data;
t = c->transparency;
m = c->mirror;
spr <<= 4;
p0 = &game.sbuf[x + y * _WIDTH + m * (c->width - 1)];
end = game.sbuf + _WIDTH * _HEIGHT;
for (i = 0; i < c->height; i++) {
p = p0;
while (*q) {
col = (*q & 0xf0) >> 4;
for (j = *q & 0x0f; j; j--, p += 1 - 2 * m) {
if (col != t) {
blit_pixel(p, end, col, spr, _WIDTH, &hidden);
}
}
q++;
}
p0 += _WIDTH;
q++;
}
return hidden;
}
static void objs_savearea(struct sprite *s) {
int y;
int16 x_pos = s->x_pos, y_pos = s->y_pos;
int16 x_size = s->x_size, y_size = s->y_size;
uint8 *p0, *q;
#ifdef USE_HIRES
uint8 *h0, *k;
#endif
if (x_pos + x_size > _WIDTH)
x_size = _WIDTH - x_pos;
if (x_pos < 0) {
x_size += x_pos;
x_pos = 0;
}
if (y_pos + y_size > _HEIGHT)
y_size = _HEIGHT - y_pos;
if (y_pos < 0) {
y_size += y_pos;
y_pos = 0;
}
if (x_size <= 0 || y_size <= 0)
return;
p0 = &game.sbuf[x_pos + y_pos * _WIDTH];
q = s->buffer;
#ifdef USE_HIRES
h0 = &game.hires[(x_pos + y_pos * _WIDTH) * 2];
k = s->hires;
#endif
for (y = 0; y < y_size; y++) {
memcpy(q, p0, x_size);
q += x_size;
p0 += _WIDTH;
#ifdef USE_HIRES
memcpy(k, h0, x_size * 2);
k += x_size * 2;
h0 += _WIDTH * 2;
#endif
}
}
static void objs_restorearea(struct sprite *s) {
int y, offset;
int16 x_pos = s->x_pos, y_pos = s->y_pos;
int16 x_size = s->x_size, y_size = s->y_size;
uint8 *p0, *q;
#ifdef USE_HIRES
uint8 *h0, *k;
#endif
if (x_pos + x_size > _WIDTH)
x_size = _WIDTH - x_pos;
if (x_pos < 0) {
x_size += x_pos;
x_pos = 0;
}
if (y_pos + y_size > _HEIGHT)
y_size = _HEIGHT - y_pos;
if (y_pos < 0) {
y_size += y_pos;
y_pos = 0;
}
if (x_size <= 0 || y_size <= 0)
return;
p0 = &game.sbuf[x_pos + y_pos * _WIDTH];
q = s->buffer;
#ifdef USE_HIRES
h0 = &game.hires[(x_pos + y_pos * _WIDTH) * 2];
k = s->hires;
#endif
offset = game.line_min_print * CHAR_LINES;
for (y = 0; y < y_size; y++) {
memcpy(p0, q, x_size);
put_pixels_a(x_pos, y_pos + y + offset, x_size, p0);
q += x_size;
p0 += _WIDTH;
#ifdef USE_HIRES
memcpy(h0, k, x_size * 2);
if (opt.hires) {
put_pixels_hires(x_pos * 2, y_pos + y + offset, x_size * 2, h0);
}
k += x_size * 2;
h0 += _WIDTH * 2;
#endif
}
}
/*
* Sprite management functions
*/
static LIST_HEAD(spr_upd_head);
static LIST_HEAD(spr_nonupd_head);
/**
* Condition to determine whether a sprite will be in the 'updating' list.
*/
static int test_updating(struct vt_entry *v) {
/* Sanity check (see bug #779302) */
if (~game.dir_view[v->current_view].flags & RES_LOADED)
return 0;
return (v->flags & (ANIMATED | UPDATE | DRAWN)) == (ANIMATED | UPDATE | DRAWN);
}
/**
* Condition to determine whether a sprite will be in the 'non-updating' list.
*/
static int test_not_updating(struct vt_entry *v) {
/* Sanity check (see bug #779302) */
if (~game.dir_view[v->current_view].flags & RES_LOADED)
return 0;
return (v->flags & (ANIMATED | UPDATE | DRAWN)) == (ANIMATED | DRAWN);
}
/**
* Convert sprite priority to y value.
*/
static INLINE int prio_to_y(int p) {
int i;
if (p == 0)
return -1;
for (i = 167; i >= 0; i--) {
if (game.pri_table[i] < p)
return i;
}
return -1; /* (p - 5) * 12 + 48; */
}
/**
* Create and initialize a new sprite structure.
*/
static struct sprite *new_sprite(struct vt_entry *v) {
struct sprite *s;
s = (struct sprite *)pool_alloc(sizeof(struct sprite));
if (s == NULL)
return NULL;
s->v = v; /* link sprite to associated view table entry */
s->x_pos = v->x_pos;
s->y_pos = v->y_pos - v->y_size + 1;
s->x_size = v->x_size;
s->y_size = v->y_size;
s->buffer = (uint8 *) pool_alloc(s->x_size * s->y_size);
#ifdef USE_HIRES
s->hires = (uint8 *) pool_alloc(s->x_size * s->y_size * 2);
#endif
v->s = s; /* link view table entry to this sprite */
return s;
}
/**
* Insert sprite in the specified sprite list.
*/
static void spr_addlist(struct list_head *head, struct vt_entry *v) {
struct sprite *s;
s = new_sprite(v);
list_add_tail(&s->list, head);
}
/**
* Sort sprites from lower y values to build a sprite list.
*/
static struct list_head *build_list(struct list_head *head,
int (*test) (struct vt_entry *)) {
int i, j, k;
struct vt_entry *v;
struct vt_entry *entry[0x100];
int y_val[0x100];
int min_y = 0xff, min_index = 0;
/* fill the arrays with all sprites that satisfy the 'test'
* condition and their y values
*/
i = 0;
for (v = game.view_table; v < &game.view_table[MAX_VIEWTABLE]; v++) {
if (test(v)) {
entry[i] = v;
y_val[i] = v->flags & FIXED_PRIORITY ? prio_to_y(v->priority) : v->y_pos;
i++;
}
}
/* now look for the smallest y value in the array and put that
* sprite in the list
*/
for (j = 0; j < i; j++) {
min_y = 0xff;
for (k = 0; k < i; k++) {
if (y_val[k] < min_y) {
min_index = k;
min_y = y_val[k];
}
}
y_val[min_index] = 0xff;
spr_addlist(head, entry[min_index]);
}
return head;
}
/**
* Build list of updating sprites.
*/
static struct list_head *build_upd_blitlist() {
return build_list(&spr_upd_head, test_updating);
}
/**
* Build list of non-updating sprites.
*/
static struct list_head *build_nonupd_blitlist() {
return build_list(&spr_nonupd_head, test_not_updating);
}
/**
* Clear the given sprite list.
*/
static void free_list(struct list_head *head) {
struct list_head *h;
struct sprite *s;
list_for_each(h, head, prev) {
s = list_entry(h, struct sprite, list);
list_del(h);
#ifdef USE_HIRES
pool_release(s->hires);
#endif
pool_release(s->buffer);
pool_release(s);
}
}
/**
* Copy sprites from the pic buffer to the screen buffer, and check if
* sprites of the given list have moved.
*/
static void commit_sprites(struct list_head *head) {
struct list_head *h;
list_for_each(h, head, next) {
struct sprite *s = list_entry(h, struct sprite, list);
int x1, y1, x2, y2, w, h;
w = (s->v->cel_data->width > s->v->cel_data_2->width) ?
s->v->cel_data->width : s->v->cel_data_2->width;
h = (s->v->cel_data->height >
s->v->cel_data_2->height) ? s->v->cel_data->
height : s->v->cel_data_2->height;
s->v->cel_data_2 = s->v->cel_data;
if (s->v->x_pos < s->v->x_pos2) {
x1 = s->v->x_pos;
x2 = s->v->x_pos2 + w - 1;
} else {
x1 = s->v->x_pos2;
x2 = s->v->x_pos + w - 1;
}
if (s->v->y_pos < s->v->y_pos2) {
y1 = s->v->y_pos - h + 1;
y2 = s->v->y_pos2;
} else {
y1 = s->v->y_pos2 - h + 1;
y2 = s->v->y_pos;
}
commit_block(x1, y1, x2, y2);
if (s->v->step_time_count != s->v->step_time)
continue;
if (s->v->x_pos == s->v->x_pos2 && s->v->y_pos == s->v->y_pos2) {
s->v->flags |= DIDNT_MOVE;
continue;
}
s->v->x_pos2 = s->v->x_pos;
s->v->y_pos2 = s->v->y_pos;
s->v->flags &= ~DIDNT_MOVE;
}
#ifdef USE_CONSOLE
if (debug_.statusline)
write_status();
#endif
}
/**
* Erase all sprites in the given list.
*/
static void erase_sprites(struct list_head *head) {
struct list_head *h;
list_for_each(h, head, prev) {
struct sprite *s = list_entry(h, struct sprite, list);
objs_restorearea(s);
}
free_list(head);
}
/**
* Blit all sprites in the given list.
*/
static void blit_sprites(struct list_head *head) {
struct list_head *h = NULL;
int hidden;
list_for_each(h, head, next) {
struct sprite *s = list_entry(h, struct sprite, list);
objs_savearea(s);
debugC(8, kDebugLevelSprites, "s->v->entry = %d (prio %d)", s->v->entry, s->v->priority);
hidden = blit_cel(s->x_pos, s->y_pos, s->v->priority, s->v->cel_data);
if (s->v->entry == 0) { /* if ego, update f1 */
setflag(F_ego_invisible, hidden);
}
}
}
/*
* Public functions
*/
void commit_upd_sprites() {
commit_sprites(&spr_upd_head);
}
void commit_nonupd_sprites() {
commit_sprites(&spr_nonupd_head);
}
/* check moves in both lists */
void commit_both() {
commit_upd_sprites();
commit_nonupd_sprites();
}
/**
* Erase updating sprites.
* This function follows the list of all updating sprites and restores
* the visible and priority data of their background buffers back to
* the AGI screen.
*
* @see erase_nonupd_sprites()
* @see erase_both()
*/
void erase_upd_sprites() {
erase_sprites(&spr_upd_head);
}
/**
* Erase non-updating sprites.
* This function follows the list of all non-updating sprites and restores
* the visible and priority data of their background buffers back to
* the AGI screen.
*
* @see erase_upd_sprites()
* @see erase_both()
*/
void erase_nonupd_sprites() {
erase_sprites(&spr_nonupd_head);
}
/**
* Erase all sprites.
* This function follows the lists of all updating and non-updating
* sprites and restores the visible and priority data of their background
* buffers back to the AGI screen.
*
* @see erase_upd_sprites()
* @see erase_nonupd_sprites()
*/
void erase_both() {
erase_upd_sprites();
erase_nonupd_sprites();
}
/**
* Blit updating sprites.
* This function follows the list of all updating sprites and blits
* them on the AGI screen.
*
* @see blit_nonupd_sprites()
* @see blit_both()
*/
void blit_upd_sprites() {
debugC(7, kDebugLevelSprites, "blit updating");
blit_sprites(build_upd_blitlist());
}
/**
* Blit non-updating sprites.
* This function follows the list of all non-updating sprites and blits
* them on the AGI screen.
*
* @see blit_upd_sprites()
* @see blit_both()
*/
void blit_nonupd_sprites() {
debugC(7, kDebugLevelSprites, "blit non-updating");
blit_sprites(build_nonupd_blitlist());
}
/**
* Blit all sprites.
* This function follows the lists of all updating and non-updating
* sprites and blits them on the AGI screen.
*
* @see blit_upd_sprites()
* @see blit_nonupd_sprites()
*/
void blit_both() {
blit_nonupd_sprites();
blit_upd_sprites();
}
/**
* Add view to picture.
* This function is used to implement the add.to.pic AGI command. It
* copies the specified cel from a view resource on the current picture.
* This cel is not a sprite, it can't be moved or removed.
* @param view number of view resource
* @param loop number of loop in the specified view resource
* @param cel number of cel in the specified loop
* @param x x coordinate to place the view
* @param y y coordinate to place the view
* @param pri priority to use
* @param mar if < 4, create a margin around the the base of the cel
*/
void add_to_pic(int view, int loop, int cel, int x, int y, int pri, int mar) {
struct view_cel *c = NULL;
int x1, y1, x2, y2, y3;
uint8 *p1, *p2;
debugC(3, kDebugLevelSprites, "v=%d, l=%d, c=%d, x=%d, y=%d, p=%d, m=%d", view, loop, cel, x, y, pri, mar);
record_image_stack_call(ADD_VIEW, view, loop, cel, x, y, pri, mar);
/*
* Was hardcoded to 8, changed to pri_table[y] to fix Gold
* Rush (see bug #587558)
*/
if (pri == 0)
pri = game.pri_table[y];
c = &game.views[view].loop[loop].cel[cel];
x1 = x;
y1 = y - c->height + 1;
x2 = x + c->width - 1;
y2 = y;
if (x1 < 0) {
x2 -= x1;
x1 = 0;
}
if (y1 < 0) {
y2 -= y1;
y1 = 0;
}
if (x2 >= _WIDTH)
x2 = _WIDTH - 1;
if (y2 >= _HEIGHT)
y2 = _HEIGHT - 1;
erase_both();
debugC(4, kDebugLevelSprites, "blit_cel (%d, %d, %d, c)", x, y, pri);
blit_cel(x1, y1, pri, c);
/* If margin is 0, 1, 2, or 3, the base of the cel is
* surrounded with a rectangle of the corresponding priority.
* If margin >= 4, this extra margin is not shown.
*/
if (mar < 4) {
/* add rectangle around object, don't clobber control
* info in priority data. The box extends to the end of
* its priority band!
*
* SQ1 needs +1 (see bug #810331)
*/
y3 = (y2 / 12) * 12 + 1;
p1 = &game.sbuf[x1 + y3 * _WIDTH];
p2 = &game.sbuf[x2 + y3 * _WIDTH];
for (y = y3; y <= y2; y++) {
if ((*p1 >> 4) >= 4)
*p1 = (mar << 4) | (*p1 & 0x0f);
if ((*p2 >> 4) >= 4)
*p2 = (mar << 4) | (*p2 & 0x0f);
p1 += _WIDTH;
p2 += _WIDTH;
}
debugC(4, kDebugLevelSprites, "pri box: %d %d %d %d (%d)", x1, y3, x2, y2, mar);
p1 = &game.sbuf[x1 + y3 * _WIDTH];
p2 = &game.sbuf[x1 + y2 * _WIDTH];
for (x = x1; x <= x2; x++) {
if ((*p1 >> 4) >= 4)
*p1 = (mar << 4) | (*p1 & 0x0f);
if ((*p2 >> 4) >= 4)
*p2 = (mar << 4) | (*p2 & 0x0f);
p1++;
p2++;
}
}
blit_both();
debugC(4, kDebugLevelSprites, "commit_block (%d, %d, %d, %d)", x1, y1, x2, y2);
commit_block(x1, y1, x2, y2);
}
/**
* Show object and description
* This function shows an object from the player's inventory, displaying
* a message box with the object description.
* @param n Number of the object to show
*/
void show_obj(int n) {
struct view_cel *c;
struct sprite s;
int x1, y1, x2, y2;
agi_load_resource(rVIEW, n);
if (!(c = &game.views[n].loop[0].cel[0]))
return;
x1 = (_WIDTH - c->width) / 2;
y1 = 112;
x2 = x1 + c->width - 1;
y2 = y1 + c->height - 1;
s.x_pos = x1;
s.y_pos = y1;
s.x_size = c->width;
s.y_size = c->height;
s.buffer = (uint8 *)malloc(s.x_size * s.y_size);
#ifdef USE_HIRES
s.hires = (uint8 *)malloc(s.x_size * s.y_size * 2);
#endif
objs_savearea(&s);
blit_cel(x1, y1, s.x_size, c);
commit_block(x1, y1, x2, y2);
message_box(game.views[n].descr);
objs_restorearea(&s);
commit_block(x1, y1, x2, y2);
free(s.buffer);
/* Added to fix a memory leak --Vasyl */
#ifdef USE_HIRES
free(s.hires);
#endif
}
void commit_block(int x1, int y1, int x2, int y2) {
int i, w, offset;
uint8 *q;
#ifdef USE_HIRES
uint8 *h;
#endif
if (!game.picture_shown)
return;
/* Clipping */
if (x1 < 0)
x1 = 0;
if (x2 < 0)
x2 = 0;
if (y1 < 0)
y1 = 0;
if (y2 < 0)
y2 = 0;
if (x1 >= _WIDTH)
x1 = _WIDTH - 1;
if (x2 >= _WIDTH)
x2 = _WIDTH - 1;
if (y1 >= _HEIGHT)
y1 = _HEIGHT - 1;
if (y2 >= _HEIGHT)
y2 = _HEIGHT - 1;
debugC(7, kDebugLevelSprites, "%d, %d, %d, %d", x1, y1, x2, y2);
w = x2 - x1 + 1;
q = &game.sbuf[x1 + _WIDTH * y1];
#ifdef USE_HIRES
h = &game.hires[(x1 + _WIDTH * y1) * 2];
#endif
offset = game.line_min_print * CHAR_LINES;
for (i = y1; i <= y2; i++) {
put_pixels_a(x1, i + offset, w, q);
q += _WIDTH;
#ifdef USE_HIRES
if (opt.hires) {
put_pixels_hires(x1 * 2, i + offset, w * 2, h);
}
h += _WIDTH * 2;
#endif
}
flush_block_a(x1, y1 + offset, x2, y2 + offset);
}
int init_sprites() {
if ((sprite_pool = (uint8 *)malloc(POOL_SIZE)) == NULL)
return err_NotEnoughMemory;
pool_top = sprite_pool;
return err_OK;
}
void deinit_sprites() {
free(sprite_pool);
}
} // End of namespace Agi

47
engines/agi/sprite.h Normal file
View File

@ -0,0 +1,47 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_SPRITE_H
#define __AGI_SPRITE_H
namespace Agi {
int init_sprites(void);
void deinit_sprites(void);
void erase_upd_sprites(void);
void erase_nonupd_sprites(void);
void erase_both(void);
void blit_upd_sprites(void);
void blit_nonupd_sprites(void);
void blit_both(void);
void commit_upd_sprites(void);
void commit_nonupd_sprites(void);
void commit_both(void);
void add_to_pic(int, int, int, int, int, int, int);
void show_obj(int);
void commit_block(int, int, int, int);
} // End of namespace Agi
#endif /* __AGI_SPRITE_H */

677
engines/agi/text.cpp Normal file
View File

@ -0,0 +1,677 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "agi/agi.h"
#include "agi/sprite.h" /* for commit_both() */
#include "agi/graphics.h"
#include "agi/keyboard.h"
#include "agi/text.h"
namespace Agi {
static void print_text2(int l, char *msg, int foff, int xoff, int yoff,
int len, int fg, int bg) {
int x1, y1;
int maxx, minx, ofoff;
int update;
/* Note: Must be unsigned to use AGDS cyrillic characters! */
unsigned char *m;
/* kludge! */
update = 1;
if (l == 2) {
update = l = 0;
}
/* FR: strings with len == 1 were not printed
*/
if (len == 1) {
put_text_character(l, xoff + foff, yoff, *msg, fg, bg);
maxx = 1;
minx = 0;
ofoff = foff;
y1 = 0; /* Check this */
} else {
maxx = 0;
minx = GFX_WIDTH;
ofoff = foff;
for (m = (unsigned char *)msg, x1 = y1 = 0; *m; m++) {
if (*m >= 0x20 || *m == 1 || *m == 2 || *m == 3) {
/* FIXME */
int ypos;
ypos = (y1 * CHAR_LINES) + yoff;
if ((x1 != (len - 1) || x1 == 39) && (ypos <= (GFX_HEIGHT - CHAR_LINES))) {
int xpos;
xpos = (x1 * CHAR_COLS) + xoff + foff;
if (xpos >= GFX_WIDTH)
continue;
put_text_character(l, xpos, ypos, *m, fg, bg);
if (x1 > maxx)
maxx = x1;
if (x1 < minx)
minx = x1;
}
x1++;
/* DF: changed the len-1 to len... */
if (x1 == len && m[1] != '\n')
y1++, x1 = foff = 0;
} else {
y1++;
x1 = foff = 0;
}
}
}
if (l)
return;
if (maxx < minx)
return;
maxx *= CHAR_COLS;
minx *= CHAR_COLS;
if (update) {
schedule_update(foff + xoff + minx, yoff, ofoff + xoff + maxx + CHAR_COLS - 1,
yoff + y1 * CHAR_LINES + CHAR_LINES + 1);
/* Making synchronous text updates reduces CPU load
* when updating status line and input area
*/
do_update();
}
}
/* len is in characters, not pixels!!
*/
static void blit_textbox(char *p, int y, int x, int len) {
/* if x | y = -1, then centre the box */
int xoff, yoff, lin, h, w;
char *msg, *m;
debugC(3, kDebugLevelText, "x=%d, y=%d, len=%d", x, y, len);
if (game.window.active)
close_window();
if (x == 0 && y == 0 && len == 0)
x = y = -1;
if (len <= 0 || len >= 40)
len = 32;
xoff = x * CHAR_COLS;
yoff = y * CHAR_LINES;
len--;
m = msg = word_wrap_string(agi_sprintf(p), &len);
for (lin = 1; *m; m++) {
/* Test \r for MacOS 8 */
if (*m == '\n' || *m == '\r')
lin++;
}
if (lin * CHAR_LINES > GFX_HEIGHT)
lin = (GFX_HEIGHT / CHAR_LINES);
w = (len + 2) * CHAR_COLS;
h = (lin + 2) * CHAR_LINES;
if (xoff < 0)
xoff = (GFX_WIDTH - w - CHAR_COLS) / 2;
else
xoff -= CHAR_COLS;
if (yoff < 0)
yoff = (GFX_HEIGHT - 3 * CHAR_LINES - h) / 2;
draw_window(xoff, yoff, xoff + w - 1, yoff + h - 1);
print_text2(2, msg, 0, CHAR_COLS + xoff, CHAR_LINES + yoff,
len + 1, MSG_BOX_TEXT, MSG_BOX_COLOUR);
free(msg);
do_update();
}
static void erase_textbox() {
if (!game.window.active) {
debugC(3, kDebugLevelText, "no window active");
return;
}
debugC(4, kDebugLevelText, "x1=%d, y1=%d, x2=%d, y2=%d", game.window.x1,
game.window.y1, game.window.x2, game.window.y2);
restore_block(game.window.x1, game.window.y1,
game.window.x2, game.window.y2, game.window.buffer);
free(game.window.buffer);
game.window.active = false;
do_update();
}
/*
* Public functions
*/
/**
* Print text in the AGI engine screen.
*/
void print_text(char *msg, int f, int x, int y, int len, int fg, int bg) {
f *= CHAR_COLS;
x *= CHAR_COLS;
y *= CHAR_LINES;
debugC(4, kDebugLevelText, "%s, %d, %d, %d, %d, %d, %d", msg, f, x, y, len, fg, bg);
print_text2(0, agi_sprintf(msg), f, x, y, len, fg, bg);
}
/**
* Print text in the AGI engine console.
*/
void print_text_console(char *msg, int x, int y, int len, int fg, int bg) {
x *= CHAR_COLS;
y *= 10;
print_text2(1, msg, 0, x, y, len, fg, bg);
}
/**
* Wrap text line to the specified width.
* @param str String to wrap.
* @param len Length of line.
*/
char *word_wrap_string(char *str, int *len) {
/* If the message has a long word (longer than 31 character) then
* loop in line 239 (for (; *v != ' '; v--, c--);) can wrap
* around 0 and write large number in c. This causes returned
* length to be negative (!) and eventually crashes in calling
* code. The fix is simple -- remove unsigned in maxc, c, l
* declaration. --Vasyl
*/
char *msg, *v, *e;
int maxc, c, l = *len;
v = msg = strdup(str);
e = msg + strlen(msg);
maxc = 0;
while (42) {
debugC(3, kDebugLevelText, "[%s], %d", msg, maxc);
if (strchr(v, ' ') == NULL && (int)strlen(v) > l) {
debugC(1, kDebugLevelText | kDebugLevelMain, "Word too long in message");
l = strlen(v);
}
/* Must include \r for MacOS 8 */
while ((c = strcspn(v, "\n\r")) <= l) {
debugC(3, kDebugLevelText, "c = %d, maxc = %d", c, maxc);
if (c > maxc)
maxc = c;
if ((v += c + 1) >= e)
goto end;
}
c = l;
if ((v += l) >= e)
break;
/* The same line that caused that bug I mentioned
* should also do another check:
* for (; *v != ' ' && *v != '\n'; v--, c--);
* While this does not matter in most cases, in the case of
* long words it caused extra \n inserted in the line
* preceding long word. This one is definitely non-critical;
* one might argue that the function is not supposed to deal
* with long words. BTW, that condition at the beginning of
* the while loop that checks word length does not make much
* sense -- it verifies the length of the first word but for
* the rest it does something odd. Overall, even with these
* changes the function is still not completely robust.
* --Vasyl
*/
if (*v != ' ')
for (; *v != ' ' && *v != '\n' && *v != '\r';
v--, c--);
if (c > maxc)
maxc = c;
*v++ = '\n';
}
end:
*len = maxc;
return msg;
}
/**
* Remove existing window, if any.
*/
void close_window() {
debugC(4, kDebugLevelText, "close window");
erase_both();
erase_textbox(); /* remove window, if any */
blit_both();
commit_both(); /* redraw sprites */
game.has_window = false;
}
/**
* Display a message box.
* This function displays the specified message in a text box
* centered in the screen and waits until a key is pressed.
* @param p The text to be displayed
*/
int message_box(char *s) {
int k;
erase_both();
blit_textbox(s, -1, -1, -1);
blit_both();
k = wait_key();
debugC(4, kDebugLevelText, "wait_key returned %02x", k);
close_window();
return k;
}
/**
* Display a message box with buttons.
* This function displays the specified message in a text box
* centered in the screen and waits until a button is pressed.
* @param p The text to be displayed
* @param b NULL-terminated list of button labels
*/
int selection_box(char *m, char **b) {
int x, y, i, s;
int key, active = 0;
int rc = -1;
int bx[5], by[5];
erase_both();
blit_textbox(m, -1, -1, -1);
x = game.window.x1 + 5 * CHAR_COLS / 2;
y = game.window.y2 - 5 * CHAR_LINES / 2;
s = game.window.x2 - game.window.x1 + 1 - 5 * CHAR_COLS;
debugC(3, kDebugLevelText, "s = %d", s);
/* Automatically position buttons */
for (i = 0; b[i]; i++) {
s -= CHAR_COLS * strlen(b[i]);
}
if (i > 1) {
debugC(3, kDebugLevelText, "s / %d = %d", i - 1, s / (i - 1));
s /= (i - 1);
} else {
x += s / 2;
}
for (i = 0; b[i]; i++) {
bx[i] = x;
by[i] = y;
x += CHAR_COLS * strlen(b[i]) + s;
}
blit_both();
/* clear key queue */
while (keypress()) {
get_key();
}
debugC(4, kDebugLevelText, "waiting...");
while (42) {
for (i = 0; b[i]; i++)
draw_button(bx[i], by[i], b[i], i == active, 0);
poll_timer(); /* msdos driver -> does nothing */
key = do_poll_keyboard();
if (!console_keyhandler(key)) {
switch (key) {
case KEY_ENTER:
rc = active;
goto press;
case KEY_ESCAPE:
rc = -1;
goto getout;
#ifdef USE_MOUSE
case BUTTON_LEFT:
for (i = 0; b[i]; i++) {
if (test_button(bx[i], by[i], b[i])) {
rc = active = i;
goto press;
}
}
break;
#endif
case 0x09: /* Tab */
debugC(3, kDebugLevelText, "Focus change");
active++;
active %= i;
break;
}
}
console_cycle();
}
press:
debugC(4, kDebugLevelText, "Button pressed: %d", rc);
getout:
close_window();
debugC(2, kDebugLevelText, "Result = %d", rc);
return rc;
}
/**
*
*/
int print(char *p, int lin, int col, int len) {
if (p == NULL)
return 0;
debugC(4, kDebugLevelText, "lin = %d, col = %d, len = %d", lin, col, len);
if (col == 0 && lin == 0 && len == 0)
lin = col = -1;
if (len == 0)
len = 30;
blit_textbox(p, lin, col, len);
if (getflag(F_output_mode)) {
/* non-blocking window */
setflag(F_output_mode, false);
return 1;
}
/* blocking */
if (game.vars[V_window_reset] == 0) {
int k;
setvar(V_key, 0);
k = wait_key();
close_window();
return k;
}
/* timed window */
debugC(3, kDebugLevelText, "f15==0, v21==%d => timed", getvar(21));
game.msg_box_ticks = getvar(V_window_reset) * 10;
setvar(V_key, 0);
do {
main_cycle();
if (game.keypress == KEY_ENTER) {
debugC(4, kDebugLevelText, "KEY_ENTER");
setvar(V_window_reset, 0);
game.keypress = 0;
break;
}
} while (game.msg_box_ticks > 0);
setvar(V_window_reset, 0);
close_window();
return 0;
}
/**
*
*/
static void print_status(char *message, ...) {
char x[42];
va_list args;
va_start(args, message);
#ifdef HAVE_VSNPRINTF
vsnprintf(x, 41, message, args);
#else
vsprintf(x, message, args);
#endif
va_end(args);
debugC(4, kDebugLevelText, "fg=%d, bg=%d", STATUS_FG, STATUS_BG);
print_text(x, 0, 0, game.line_status, 40, STATUS_FG, STATUS_BG);
}
static char *safe_strcat(char *s, const char *t) {
if (t != NULL)
strcat(s, t);
return s;
}
/**
* Formats AGI string.
* This function turns a AGI string into a real string expanding values
* according to the AGI format specifiers.
* @param s string containing the format specifier
* @param n logic number
*/
#define MAX_LEN 768
char *agi_sprintf(char *s) {
static char y[MAX_LEN];
char x[MAX_LEN];
char z[16], *p;
debugC(3, kDebugLevelText, "logic %d, '%s'", game.lognum, s);
p = x;
for (*p = 0; *s;) {
switch (*s) {
case '\\':
s++;
goto literal;
case '%':
s++;
switch (*s++) {
int i;
case 'v':
i = strtoul(s, NULL, 10);
while (*s >= '0' && *s <= '9')
s++;
sprintf(z, "%015i", getvar(i));
i = 99;
if (*s == '|') {
s++;
i = strtoul(s, NULL, 10);
while (*s >= '0' && *s <= '9')
s++;
}
if (i == 99) {
/* remove all leading 0 */
/* don't remove the 3rd zero if 000 */
for (i = 0;
z[i] == '0' && i < 14; i++);
} else {
i = 15 - i;
}
safe_strcat(p, z + i);
break;
case '0':
i = strtoul(s, NULL, 10) - 1;
safe_strcat(p, object_name(i));
break;
case 'g':
i = strtoul(s, NULL, 10) - 1;
safe_strcat(p, game.logics[0].texts[i]);
break;
case 'w':
i = strtoul(s, NULL, 10) - 1;
safe_strcat(p, game.ego_words[i].word);
break;
case 's':
i = strtoul(s, NULL, 10);
safe_strcat(p, game.strings[i]);
break;
case 'm':
i = strtoul(s, NULL, 10) - 1;
if (game.logics[game.lognum].num_texts > i)
safe_strcat(p, agi_sprintf(game. logics[game.lognum].texts[i]));
break;
}
while (*s >= '0' && *s <= '9')
s++;
while (*p)
p++;
break;
default:
literal:
assert(p < x + MAX_LEN);
*p++ = *s++;
*p = 0;
break;
}
}
strcpy(y, x);
return y;
}
/**
* Write the status line.
*/
void write_status() {
char x[64];
#ifdef USE_CONSOLE
if (debug_.statusline) {
#ifdef USE_MOUSE
print_status("%3d(%03d) %3d,%3d(%3d,%3d) ",
getvar(0), getvar(1), game.view_table[0].x_pos,
game.view_table[0].y_pos, WIN_TO_PIC_X(mouse.x),
WIN_TO_PIC_Y(mouse.y));
#else
print_status("%3d(%03d) %3d,%3d ",
getvar(0), getvar(1), game.view_table[0].x_pos,
game.view_table[0].y_pos);
#endif
return;
}
#endif /* USE_CONSOLE */
if (!game.status_line) {
int l = game.line_status;
clear_lines(l, l, 0);
flush_lines(l, l);
return;
}
sprintf(x, " Score:%i of %-3i", game.vars[V_score], game.vars[V_max_score]);
print_status("%-17s Sound:%s ", x, getflag(F_sound_on) ? "on " : "off");
}
/**
* Print user input prompt.
*/
void write_prompt() {
int l, fg, bg, pos;
if (!game.input_enabled || game.input_mode != INPUT_NORMAL)
return;
l = game.line_user_input;
fg = game.color_fg;
bg = game.color_bg;
pos = game.cursor_pos;
debugC(4, kDebugLevelText, "erase line %d", l);
clear_lines(l, l, game.color_bg);
debugC(4, kDebugLevelText, "prompt = '%s'", agi_sprintf(game.strings[0]));
print_text(game.strings[0], 0, 0, l, 1, fg, bg);
print_text((char *)game.input_buffer, 0, 1, l, pos + 1, fg, bg);
print_character(pos + 1, l, game.cursor_char, fg, bg);
flush_lines(l, l);
do_update();
}
/**
* Clear text lines in the screen.
* @param l1 start line
* @param l2 end line
* @param c color
*/
void clear_lines(int l1, int l2, int c) {
/* do we need to adjust for +8 on topline?
* inc for endline so it matches the correct num
* ie, from 22 to 24 is 3 lines, not 2 lines.
*/
l1 *= CHAR_LINES;
l2 *= CHAR_LINES;
l2 += CHAR_LINES - 1;
draw_rectangle(0, l1, GFX_WIDTH - 1, l2, c);
}
/**
*
*/
void flush_lines(int l1, int l2) {
l1 *= CHAR_LINES;
l2 *= CHAR_LINES;
l2 += CHAR_LINES - 1;
flush_block(0, l1, GFX_WIDTH - 1, l2);
}
/**
*
*/
void draw_window(int x1, int y1, int x2, int y2) {
game.window.active = true;
game.window.x1 = x1;
game.window.y1 = y1;
game.window.x2 = x2;
game.window.y2 = y2;
game.window.buffer = (uint8 *) malloc((x2 - x1 + 1) * (y2 - y1 + 1));
debugC(4, kDebugLevelText, "x1=%d, y1=%d, x2=%d, y2=%d", x1, y1, x2, y2);
save_block(x1, y1, x2, y2, game.window.buffer);
draw_box(x1, y1, x2, y2, MSG_BOX_COLOUR, MSG_BOX_LINE, 2);
}
} // End of namespace Agi

48
engines/agi/text.h Normal file
View File

@ -0,0 +1,48 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_TEXT_H
#define __AGI_TEXT_H
#include "agi/agi.h"
namespace Agi {
int message_box(char *);
int selection_box(char *, char **);
void close_window(void);
void draw_window(int, int, int, int);
void print_text(char *, int, int, int, int, int, int);
void print_text_console(char *, int, int, int, int, int);
int print(char *, int, int, int);
char *word_wrap_string(char *, int *);
char *agi_sprintf(char *);
void write_status(void);
void write_prompt(void);
void clear_lines(int, int, int);
void flush_lines(int, int);
} // End of namespace Agi
#endif /* __AGI_TEXT_H */

396
engines/agi/view.cpp Normal file
View File

@ -0,0 +1,396 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2003 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "agi/agi.h"
#include "agi/sprite.h"
namespace Agi {
static void _set_cel(struct vt_entry *v, int n) {
struct view_loop *current_vl;
struct view_cel *current_vc;
v->current_cel = n;
current_vl = &game.views[v->current_view].loop[v->current_loop];
/* Added by Amit Vainsencher <amitv@subdimension.com> to prevent
* crash in KQ1 -- not in the Sierra interpreter
*/
if (current_vl->num_cels == 0)
return;
if (!(v->flags & UPDATE)
&& (agi_get_release() >= 0x3000))
return;
current_vc = &current_vl->cel[n];
v->cel_data = current_vc;
v->x_size = current_vc->width;
v->y_size = current_vc->height;
}
static void _set_loop(struct vt_entry *v, int n) {
struct view_loop *current_vl;
debugC(7, kDebugLevelResources, "vt entry #%d, loop = %d", v->entry, n);
/* Added to avoid crash when leaving the arcade machine in MH1
* -- not in AGI 2.917
*/
if (n >= v->num_loops)
n = 0;
v->current_loop = n;
current_vl = &game.views[v->current_view].loop[v->current_loop];
v->num_cels = current_vl->num_cels;
if (v->current_cel >= v->num_cels)
v->current_cel = 0;
if (!(v->flags & UPDATE) && (agi_get_release() >= 0x3000))
return;
v->loop_data = &game.views[v->current_view].loop[n];
}
static void update_view(struct vt_entry *v) {
int cel, last_cel;
if (v->flags & DONTUPDATE) {
v->flags &= ~DONTUPDATE;
return;
}
cel = v->current_cel;
last_cel = v->num_cels - 1;
switch (v->cycle) {
case CYCLE_NORMAL:
if (++cel > last_cel)
cel = 0;
break;
case CYCLE_END_OF_LOOP:
if (cel < last_cel) {
debugC(5, kDebugLevelResources, "cel %d (last = %d)", cel + 1, last_cel);
if (++cel != last_cel)
break;
}
setflag(v->parm1, true);
v->flags &= ~CYCLING;
v->direction = 0;
v->cycle = CYCLE_NORMAL;
break;
case CYCLE_REV_LOOP:
if (cel) {
if (--cel)
break;
}
setflag(v->parm1, true);
v->flags &= ~CYCLING;
v->direction = 0;
v->cycle = CYCLE_NORMAL;
break;
case CYCLE_REVERSE:
if (cel == 0) {
cel = last_cel;
} else {
cel--;
}
break;
}
set_cel(v, cel);
}
/*
* Public functions
*/
/**
* Decode an AGI view resource.
* This function decodes the raw data of the specified AGI view resource
* and fills the corresponding views array element.
* @param n number of view resource to decode
*/
int decode_view(int n) {
int loop, cel;
uint8 *v, *lptr;
uint16 lofs, cofs;
struct view_loop *vl;
struct view_cel *vc;
debugC(5, kDebugLevelResources, "decode_view(%d)", n);
v = game.views[n].rdata;
assert(v != NULL);
game.views[n].descr = READ_LE_UINT16(v + 3) ? (char *)(v + READ_LE_UINT16(v + 3)) : (char *)(v + 3);
/* if no loops exist, return! */
if ((game.views[n].num_loops = *(v + 2)) == 0)
return err_NoLoopsInView;
/* allocate memory for all views */
game.views[n].loop = (view_loop *)
calloc(game.views[n].num_loops, sizeof(struct view_loop));
if (game.views[n].loop == NULL)
return err_NotEnoughMemory;
/* decode all of the loops in this view */
lptr = v + 5; /* first loop address */
for (loop = 0; loop < game.views[n].num_loops; loop++, lptr += 2) {
lofs = READ_LE_UINT16(lptr); /* loop header offset */
vl = &game.views[n].loop[loop]; /* the loop struct */
vl->num_cels = *(v + lofs);
debugC(6, kDebugLevelResources, "view %d, num_cels = %d", n, vl->num_cels);
vl->cel = (view_cel *) calloc(vl->num_cels, sizeof(struct view_cel));
if (vl->cel == NULL) {
free(game.views[n].loop);
game.views[n].num_loops = 0;
return err_NotEnoughMemory;
}
/* decode the cells */
for (cel = 0; cel < vl->num_cels; cel++) {
cofs = lofs + READ_LE_UINT16(v + lofs + 1 + (cel * 2));
vc = &vl->cel[cel];
vc->width = *(v + cofs);
vc->height = *(v + cofs + 1);
vc->transparency = *(v + cofs + 2) & 0xf;
vc->mirror_loop = (*(v + cofs + 2) >> 4) & 0x7;
vc->mirror = (*(v + cofs + 2) >> 7) & 0x1;
/* skip over width/height/trans|mirror data */
cofs += 3;
vc->data = v + cofs;
/* If mirror_loop is pointing to the current loop,
* then this is the original.
*/
if (vc->mirror_loop == loop)
vc->mirror = 0;
} /* cel */
} /* loop */
return err_OK;
}
/**
* Unloads all data in a view resource
* @param n number of view resource
*/
void unload_view(int n) {
int x;
debugC(5, kDebugLevelResources, "discard view %d", n);
if (~game.dir_view[n].flags & RES_LOADED)
return;
/* Rebuild sprite list, see bug #779302 */
erase_both();
blit_both();
commit_both();
/* free all the loops */
for (x = 0; x < game.views[n].num_loops; x++)
free(game.views[n].loop[x].cel);
free(game.views[n].loop);
free(game.views[n].rdata);
game.dir_view[n].flags &= ~RES_LOADED;
}
/**
* Set a view table entry to use the specified cel of the current loop.
* @param v pointer to view table entry
* @param n number of cel
*/
void set_cel(struct vt_entry *v, int n) {
assert(v->view_data != NULL);
assert(v->num_cels >= n);
_set_cel(v, n);
/* If position isn't appropriate, update it accordingly */
if (v->x_pos + v->x_size > _WIDTH) {
v->flags |= UPDATE_POS;
v->x_pos = _WIDTH - v->x_size;
}
if (v->y_pos - v->y_size + 1 < 0) {
v->flags |= UPDATE_POS;
v->y_pos = v->y_size - 1;
}
if (v->y_pos <= game.horizon && (~v->flags & IGNORE_HORIZON)) {
v->flags |= UPDATE_POS;
v->y_pos = game.horizon + 1;
}
}
/**
* Set a view table entry to use the specified loop of the current view.
* @param v pointer to view table entry
* @param n number of loop
*/
void set_loop(struct vt_entry *v, int n) {
assert(v->view_data != NULL);
assert(v->num_loops >= n);
_set_loop(v, n);
set_cel(v, v->current_cel);
}
/**
* Set a view table entry to use the specified view resource.
* @param v pointer to view table entry
* @param n number of AGI view resource
*/
void set_view(struct vt_entry *v, int n) {
v->view_data = &game.views[n];
v->current_view = n;
v->num_loops = v->view_data->num_loops;
set_loop(v, v->current_loop >= v->num_loops ? 0 : v->current_loop);
}
/**
* Set the view table entry as updating.
* @param v pointer to view table entry
*/
void start_update(struct vt_entry *v) {
if (~v->flags & UPDATE) {
erase_both();
v->flags |= UPDATE;
blit_both();
}
}
/**
* Set the view table entry as non-updating.
* @param v pointer to view table entry
*/
void stop_update(struct vt_entry *v) {
if (v->flags & UPDATE) {
erase_both();
v->flags &= ~UPDATE;
blit_both();
}
}
/* loops to use according to direction and number of loops in
* the view resource
*/
static int loop_table_2[] = {
0x04, 0x04, 0x00, 0x00, 0x00, 0x04, 0x01, 0x01, 0x01
};
static int loop_table_4[] = {
0x04, 0x03, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x01
};
/**
* Update view table entries.
* This function is called at the end of each interpreter cycle
* to update the view table entries and blit the sprites.
*/
void update_viewtable() {
struct vt_entry *v;
int i, loop;
i = 0;
for (v = game.view_table; v < &game.view_table[MAX_VIEWTABLE]; v++) {
if ((v->flags & (ANIMATED | UPDATE | DRAWN)) != (ANIMATED | UPDATE | DRAWN)) {
continue;
}
i++;
loop = 4;
if (~v->flags & FIX_LOOP) {
switch (v->num_loops) {
case 2:
case 3:
loop = loop_table_2[v->direction];
break;
case 4:
loop = loop_table_4[v->direction];
break;
default:
/* for KQ4 */
if (agi_get_release() == 0x3086)
loop = loop_table_4[v->direction];
break;
}
}
/* AGI 2.272 (ddp, xmas) doesn't test step_time_count! */
if (loop != 4 && loop != v->current_loop) {
if (agi_get_release() <= 0x2272 ||
v->step_time_count == 1) {
set_loop(v, loop);
}
}
if (~v->flags & CYCLING)
continue;
if (v->cycle_time_count == 0)
continue;
if (--v->cycle_time_count == 0) {
update_view(v);
v->cycle_time_count = v->cycle_time;
}
}
if (i) {
#ifdef USE_CONSOLE
/* To correctly update sprites when we use the console
* we must work with all sprites.
*/
if (console.y > 0) {
erase_both();
update_position();
blit_both();
commit_both();
} else
#endif
/* If we're not using the console, updating only
* the active sprites lets us save some CPU cycles.
* This is how the original Sierra AGI works.
*/
{
erase_upd_sprites();
update_position();
blit_upd_sprites();
commit_upd_sprites();
}
game.view_table[0].flags &= ~(ON_WATER | ON_LAND);
}
}
} // End of namespace Agi

142
engines/agi/view.h Normal file
View File

@ -0,0 +1,142 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2001 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef __AGI_VIEW_H
#define __AGI_VIEW_H
namespace Agi {
struct view_cel {
uint8 height;
uint8 width;
uint8 transparency;
uint8 mirror_loop;
uint8 mirror;
uint8 *data;
};
struct view_loop {
int num_cels;
struct view_cel *cel;
};
/**
* AGI view resource structure.
*/
struct agi_view {
int num_loops;
struct view_loop *loop;
char *descr;
uint8 *rdata;
};
/**
* AGI view table entry
*/
struct vt_entry {
uint8 step_time;
uint8 step_time_count;
uint8 entry;
int16 x_pos;
int16 y_pos;
uint8 current_view;
struct agi_view *view_data;
uint8 current_loop;
uint8 num_loops;
struct view_loop *loop_data;
uint8 current_cel;
uint8 num_cels;
struct view_cel *cel_data;
struct view_cel *cel_data_2;
int16 x_pos2;
int16 y_pos2;
void *s;
int16 x_size;
int16 y_size;
uint8 step_size;
uint8 cycle_time;
uint8 cycle_time_count;
uint8 direction;
#define MOTION_NORMAL 0
#define MOTION_WANDER 1
#define MOTION_FOLLOW_EGO 2
#define MOTION_MOVE_OBJ 3
uint8 motion;
#define CYCLE_NORMAL 0
#define CYCLE_END_OF_LOOP 1
#define CYCLE_REV_LOOP 2
#define CYCLE_REVERSE 3
uint8 cycle;
uint8 priority;
#define DRAWN 0x0001
#define IGNORE_BLOCKS 0x0002
#define FIXED_PRIORITY 0x0004
#define IGNORE_HORIZON 0x0008
#define UPDATE 0x0010
#define CYCLING 0x0020
#define ANIMATED 0x0040
#define MOTION 0x0080
#define ON_WATER 0x0100
#define IGNORE_OBJECTS 0x0200
#define UPDATE_POS 0x0400
#define ON_LAND 0x0800
#define DONTUPDATE 0x1000
#define FIX_LOOP 0x2000
#define DIDNT_MOVE 0x4000
#define ADJ_EGO_XY 0x8000
uint16 flags;
uint8 parm1;
uint8 parm2;
uint8 parm3;
uint8 parm4;
}; /* struct vt_entry */
/* Motion */
void check_all_motions(void);
void move_obj(struct vt_entry *);
void in_destination(struct vt_entry *);
void fix_position(int);
void update_position(void);
/* View table management */
void set_cel(struct vt_entry *, int);
void set_loop(struct vt_entry *, int);
void set_view(struct vt_entry *, int);
void start_update(struct vt_entry *);
void stop_update(struct vt_entry *);
void update_viewtable(void);
void unload_view(int);
int decode_view(int);
void add_to_pic(int, int, int, int, int, int, int);
void draw_obj(int);
} // End of namespace Agi
#endif /* __AGI_VIEW_H */

213
engines/agi/words.cpp Normal file
View File

@ -0,0 +1,213 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2006 The ScummVM project
*
* Copyright (C) 1999-2002 Sarien Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
/*
* New find_word algorithm by Thomas Akesson <tapilot@home.se>
*/
#include "agi/agi.h"
#include "agi/keyboard.h" /* for clean_input() */
namespace Agi {
static uint8 *words; /* words in the game */
static uint32 words_flen; /* length of word memory */
/*
* Local implementation to avoid problems with strndup() used by
* gcc 3.2 Cygwin (see #635984)
*/
static char *my_strndup(char *src, int n) {
char *tmp = strncpy((char *)malloc(n + 1), src, n);
tmp[n] = 0;
return tmp;
}
int load_words(char *fname) {
Common::File fp;
uint32 flen;
uint8 *mem = NULL;
char *path = NULL;
words = NULL;
path = fname;
if (!fp.open(path)) {
report("Warning: can't open %s\n", path);
return err_OK /*err_BadFileOpen */ ;
}
report("Loading dictionary: %s\n", path);
fp.seek(0, SEEK_END);
flen = fp.pos();
words_flen = flen;
fp.seek(0, SEEK_SET);
if ((mem = (uint8 *)calloc(1, flen + 32)) == NULL) {
fp.close();
return err_NotEnoughMemory;
}
fp.read(mem, flen);
fp.close();
words = mem;
return err_OK;
}
void unload_words() {
if (words != NULL) {
free(words);
words = NULL;
}
}
/**
* Find a word in the dictionary
* Uses an algorithm hopefully like the one Sierra used. Returns the ID
* of the word and the length in flen. Returns -1 if not found.
*
* Thomas Åkesson, November 2001
*/
int find_word(char *word, int *flen) {
int mchr = 0; /* matched chars */
int len, fchr, id = -1;
uint8 *p = words;
uint8 *q = words + words_flen;
*flen = 0;
debugC(2, kDebugLevelScripts, "find_word(%s)", word);
if (word[0] >= 'a' && word[0] <= 'z')
fchr = word[0] - 'a';
else
return -1;
len = strlen(word);
/* Get the offset to the first word beginning with the
* right character
*/
p += READ_BE_UINT16(p + 2 * fchr);
while (p[0] >= mchr) {
if (p[0] == mchr) {
p++;
/* Loop through all matching characters */
while ((p[0] ^ word[mchr]) == 0x7F && mchr < len) {
mchr++;
p++;
}
/* Check if this is the last character of the word
* and if it matches
*/
if ((p[0] ^ word[mchr]) == 0xFF && mchr < len) {
mchr++;
if (word[mchr] == 0 || word[mchr] == 0x20) {
id = READ_BE_UINT16(p + 1);
*flen = mchr;
}
}
}
if (p >= q)
return -1;
/* Step to the next word */
while (p[0] < 0x80)
p++;
p += 3;
}
return id;
}
void dictionary_words(char *msg) {
char *p = NULL;
char *q = NULL;
int wid, wlen;
debugC(2, kDebugLevelScripts, "msg = \"%s\"", msg);
clean_input();
for (p = msg; p && *p && getvar(V_word_not_found) == 0;) {
if (*p == 0x20)
p++;
if (*p == 0)
break;
wid = find_word(p, &wlen);
debugC(2, kDebugLevelScripts, "find_word(p) == %d", wid);
switch (wid) {
case -1:
debugC(2, kDebugLevelScripts, "unknown word");
game.ego_words[game.num_ego_words].word = strdup(p);
q = game.ego_words[game.num_ego_words].word;
game.ego_words[game.num_ego_words].id = 19999;
setvar(V_word_not_found, 1 + game.num_ego_words);
game.num_ego_words++;
p += strlen(p);
break;
case 0:
/* ignore this word */
debugC(2, kDebugLevelScripts, "ignore word");
p += wlen;
q = NULL;
break;
default:
/* an OK word */
debugC(3, kDebugLevelScripts, "ok word (%d)", wid);
game.ego_words[game.num_ego_words].id = wid;
game.ego_words[game.num_ego_words].word = my_strndup(p, wlen);
game.num_ego_words++;
p += wlen;
break;
}
if (p != NULL && *p) {
debugC(2, kDebugLevelScripts, "p = %s", p);
*p = 0;
p++;
}
if (q != NULL) {
for (; (*q != 0 && *q != 0x20); q++);
if (*q) {
*q = 0;
q++;
}
}
}
debugC(4, kDebugLevelScripts, "num_ego_words = %d", game.num_ego_words);
if (game.num_ego_words > 0) {
setflag(F_entered_cli, true);
setflag(F_said_accepted_input, false);
}
}
} // End of namespace Agi

View File

@ -25,6 +25,10 @@ static const char *credits[] = {
"\\C\\c0""Gregory Montoir",
"\\C\\c0""Eugene Sandulenko",
"\\C\\c0""",
"\\C\\c1""AGI",
"\\C\\c0""Pawel Kolodziejski",
"\\C\\c0""Eugene Sandulenko",
"\\C\\c0""",
"\\C\\c1""BASS",
"\\C\\c0""Robert Goeffringmann",
"\\C\\c0""Oliver Kiehl",
@ -235,6 +239,8 @@ static const char *credits[] = {
"\\C\\c2""Various info on his FM-TOWNS/Marty SCUMM ports",
"\\C\\c0""lloyd",
"\\C\\c2""For deep tech details about C64 Zak & MM",
"\\C\\c0""Sarien Team",
"\\C\\c2""Original AGI engine code",
"\\C\\c0""Jimmi Thogersen",
"\\C\\c2""For ScummRev, and much obscure code/documentation",
"\\C\\c0""Tristan",

View File

@ -459,6 +459,11 @@ begin_credits("Credits");
add_person("Eugene Sandulenko", "sev", "");
end_section();
begin_section("AGI");
add_person("Pawe&#322; Ko&#322;odziejski", "aquadran", "");
add_person("Eugene Sandulenko", "sev", "");
end_section();
begin_section("BASS"); # Beneath a Steel Sky
add_person("Robert G&ouml;ffringmann", "lavosspawn", "");
add_person("Oliver Kiehl", "olki", "");
@ -669,6 +674,7 @@ begin_credits("Credits");
add_person("", "Jezar", "For his freeverb filter implementation");
add_person("Jim Leiterman", "", "Various info on his FM-TOWNS/Marty SCUMM ports");
add_person("", "lloyd", "For deep tech details about C64 Zak &amp; MM");
add_person("Sarien Team", "", "Original AGI engine code");
add_person("Jimmi Th&oslash;gersen", "", "For ScummRev, and much obscure code/documentation");
add_person("", "Tristan", "For additional work on the original MT-32 emulator");
end_persons();