Ticket #8550: agi.cpp

File agi.cpp, 14.4 KB (added by SF/metafox, 18 years ago)

AGI.CPP File with VIEWDIR replaced with OBJECT

Line 
1/* ScummVM - Scumm Interpreter
2 * Copyright (C) 2006 The ScummVM project
3 *
4 * Copyright (C) 1999-2003 Sarien Team
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 * $URL: https://svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/engines/agi/agi.cpp $
21 * $Id: agi.cpp 23285 2006-06-24 10:45:47Z fingolfin $
22 *
23 */
24
25#include "common/stdafx.h"
26#include "common/file.h"
27#include "common/fs.h"
28#include "common/savefile.h"
29#include "common/config-manager.h"
30
31#include "base/plugins.h"
32
33#include "graphics/cursorman.h"
34
35#include "sound/mididrv.h"
36#include "sound/mixer.h"
37
38#include "agi/agi.h"
39#include "agi/text.h"
40#include "agi/graphics.h"
41#include "agi/sprite.h"
42#include "agi/opcodes.h"
43#include "agi/keyboard.h"
44#include "agi/menu.h"
45#include "agi/savegame.h"
46
47namespace Agi {
48
49void gfx_set_palette();
50
51extern int optind;
52
53struct agi_options opt;
54struct game_id_list game_info;
55struct agi_game game;
56
57static struct agi_loader *loader; /* loader */
58
59extern struct agi_loader agi_v2;
60extern struct agi_loader agi_v3;
61
62static volatile uint32 tick_timer = 0;
63
64#define TICK_SECONDS 20
65
66static int key_control = 0;
67static int key_alt = 0;
68
69static Console *_console;
70
71#define KEY_QUEUE_SIZE 16
72
73static int key_queue[KEY_QUEUE_SIZE];
74static int key_queue_start = 0;
75static int key_queue_end = 0;
76
77#define key_enqueue(k) do { key_queue[key_queue_end++] = (k); \
78 key_queue_end %= KEY_QUEUE_SIZE; } while (0)
79#define key_dequeue(k) do { (k) = key_queue[key_queue_start++]; \
80 key_queue_start %= KEY_QUEUE_SIZE; } while (0)
81
82static void process_events() {
83 OSystem::Event event;
84 int key = 0;
85
86 while (g_system->pollEvent(event)) {
87 switch (event.type) {
88 case OSystem::EVENT_QUIT:
89 deinit_video();
90 deinit_machine();
91 g_system->quit();
92 break;
93 case OSystem::EVENT_LBUTTONDOWN:
94 key = BUTTON_LEFT;
95 mouse.button = 1;
96 key_enqueue(key);
97 mouse.x = event.mouse.x;
98 mouse.y = event.mouse.y;
99 break;
100 case OSystem::EVENT_RBUTTONDOWN:
101 key = BUTTON_RIGHT;
102 mouse.button = 2;
103 key_enqueue(key);
104 mouse.x = event.mouse.x;
105 mouse.y = event.mouse.y;
106 break;
107 case OSystem::EVENT_MOUSEMOVE:
108 mouse.x = event.mouse.x;
109 mouse.y = event.mouse.y;
110 break;
111 case OSystem::EVENT_LBUTTONUP:
112 case OSystem::EVENT_RBUTTONUP:
113 mouse.button = 0;
114 break;
115 case OSystem::EVENT_KEYDOWN:
116 key_control = 0;
117 key_alt = 0;
118
119 if (event.kbd.flags == OSystem::KBD_CTRL && event.kbd.keycode == 'd') {
120 _console->attach();
121 break;
122 }
123
124 if (event.kbd.flags & OSystem::KBD_CTRL)
125 key_control = 1;
126
127 if (event.kbd.flags & OSystem::KBD_ALT)
128 key_alt = 1;
129
130 switch (key = event.kbd.keycode) {
131 case 256 + 20: // left arrow
132 case 260: // key pad 4
133 key = KEY_LEFT;
134 break;
135 case 256 + 19: // right arrow
136 case 262: // key pad 6
137 key = KEY_RIGHT;
138 break;
139 case 256 + 17: // up arrow
140 case 264: // key pad 8
141 key = KEY_UP;
142 break;
143 case 256 + 18: // down arrow
144 case 258: // key pad 2
145 key = KEY_DOWN;
146 break;
147 case 256 + 24: // page up
148 case 265: // key pad 9
149 key = KEY_UP_RIGHT;
150 break;
151 case 256 + 25: // page down
152 case 259: // key pad 3
153 key = KEY_DOWN_RIGHT;
154 break;
155 case 256 + 22: // home
156 case 263: // key pad 7
157 key = KEY_UP_LEFT;
158 break;
159 case 256 + 23: // end
160 case 257: // key pad 1
161 key = KEY_DOWN_LEFT;
162 break;
163 case 261: // key pad 5
164 key = KEY_STATIONARY;
165 break;
166 case '+':
167 key = '+';
168 break;
169 case '-':
170 key = '-';
171 break;
172 case 9:
173 key = 0x0009;
174 break;
175 case 282:
176 key = 0x3b00;
177 break;
178 case 283:
179 key = 0x3c00;
180 break;
181 case 284:
182 key = 0x3d00;
183 break;
184 case 285:
185 key = 0x3e00;
186 break;
187 case 286:
188 key = 0x3f00;
189 break;
190 case 287:
191 key = 0x4000;
192 break;
193 case 288:
194 key = 0x4100;
195 break;
196 case 289:
197 key = 0x4200;
198 break;
199 case 290:
200 key = 0x4300;
201 break;
202 case 291:
203 key = 0x4400;
204 break;
205 case 292:
206 key = KEY_STATUSLN;
207 break;
208 case 293:
209 key = KEY_PRIORITY;
210 break;
211 case 27:
212 key = 0x1b;
213 break;
214 case '\n':
215 case '\r':
216 key = KEY_ENTER;
217 break;
218 default:
219 if (key < 256 && !isalpha(key)) {
220 key = event.kbd.ascii;
221 break;
222 }
223 if (key_control)
224 key = (key & ~0x20) - 0x40;
225 else if (key_alt)
226 key = scancode_table[(key & ~0x20) - 0x41] << 8;
227 else if (event.kbd.flags & OSystem::KBD_SHIFT)
228 key = event.kbd.ascii;
229 break;
230 }
231 if (key)
232 key_enqueue(key);
233 break;
234 default:
235 break;
236 }
237 }
238}
239
240int agi_is_keypress_low() {
241 process_events();
242 return key_queue_start != key_queue_end;
243}
244
245void agi_timer_low() {
246 static uint32 m = 0;
247 uint32 dm;
248
249 if (tick_timer < m)
250 m = 0;
251
252 while ((dm = tick_timer - m) < 5) {
253 process_events();
254 if (_console->isAttached())
255 _console->onFrame();
256 g_system->delayMillis(10);
257 g_system->updateScreen();
258 }
259 m = tick_timer;
260}
261
262int agi_get_keypress_low() {
263 int k;
264
265 while (key_queue_start == key_queue_end) /* block */
266 agi_timer_low();
267 key_dequeue(k);
268
269 return k;
270}
271
272static void agi_timer_function_low(void *refCon) {
273 tick_timer++;
274}
275
276static void init_pri_table() {
277 int i, p, y = 0;
278
279 for (p = 1; p < 15; p++) {
280 for (i = 0; i < 12; i++) {
281 game.pri_table[y++] = p < 4 ? 4 : p;
282 }
283 }
284}
285
286int agi_init() {
287 int ec, i;
288
289 debug(2, "initializing");
290 debug(2, "game.ver = 0x%x", game.ver);
291
292 /* reset all flags to false and all variables to 0 */
293 for (i = 0; i < MAX_FLAGS; i++)
294 game.flags[i] = 0;
295 for (i = 0; i < MAX_VARS; i++)
296 game.vars[i] = 0;
297
298 /* clear all resources and events */
299 for (i = 0; i < MAX_DIRS; i++) {
300 memset(&game.views[i], 0, sizeof(struct agi_view));
301 memset(&game.pictures[i], 0, sizeof(struct agi_picture));
302 memset(&game.logics[i], 0, sizeof(struct agi_logic));
303 memset(&game.sounds[i], 0, sizeof(struct agi_sound));
304 }
305
306 /* clear view table */
307 for (i = 0; i < MAX_VIEWTABLE; i++)
308 memset(&game.view_table[i], 0, sizeof(struct vt_entry));
309
310 init_words();
311
312 menu = new Menu();
313 init_pri_table();
314
315 /* clear string buffer */
316 for (i = 0; i < MAX_STRINGS; i++)
317 game.strings[i][0] = 0;
318
319 /* setup emulation */
320
321 switch (loader->int_version >> 12) {
322 case 2:
323 report("Emulating Sierra AGI v%x.%03x\n",
324 (int)(loader->int_version >> 12) & 0xF,
325 (int)(loader->int_version) & 0xFFF);
326 break;
327 case 3:
328 report("Emulating Sierra AGI v%x.002.%03x\n",
329 (int)(loader->int_version >> 12) & 0xF,
330 (int)(loader->int_version) & 0xFFF);
331 break;
332 }
333
334 game.game_flags |= opt.amiga ? ID_AMIGA : 0;
335 game.game_flags |= opt.agds ? ID_AGDS : 0;
336
337 if (game.game_flags & ID_AMIGA)
338 report("Amiga padded game detected.\n");
339
340 if (game.game_flags & ID_AGDS)
341 report("AGDS mode enabled.\n");
342
343 ec = loader->init(); /* load vol files, etc */
344
345 if (ec == err_OK)
346 ec = loader->load_objects(OBJECTS);
347
348 /* note: demogs has no words.tok */
349 if (ec == err_OK)
350 ec = loader->load_words(WORDS);
351
352 /* FIXME: load IIgs instruments and samples */
353 /* load_instruments("kq.sys16"); */
354
355 /* Load logic 0 into memory */
356 if (ec == err_OK)
357 ec = loader->load_resource(rLOGIC, 0);
358
359 return ec;
360}
361
362/*
363 * Public functions
364 */
365
366void agi_unload_resources() {
367 int i;
368
369 /* Make sure logic 0 is always loaded */
370 for (i = 1; i < MAX_DIRS; i++) {
371 loader->unload_resource(rLOGIC, i);
372 }
373 for (i = 0; i < MAX_DIRS; i++) {
374 loader->unload_resource(rVIEW, i);
375 loader->unload_resource(rPICTURE, i);
376 loader->unload_resource(rSOUND, i);
377 }
378}
379
380int agi_deinit() {
381 int ec;
382
383 clean_input(); /* remove all words from memory */
384 agi_unload_resources(); /* unload resources in memory */
385 loader->unload_resource(rLOGIC, 0);
386 ec = loader->deinit();
387 unload_objects();
388 unload_words();
389
390 clear_image_stack();
391
392 return ec;
393}
394
395int agi_detect_game() {
396 int ec = err_OK;
397
398 loader = &agi_v2;
399 ec = loader->detect_game();
400
401 if (ec != err_OK) {
402 loader = &agi_v3;
403 ec = loader->detect_game();
404 }
405
406 return ec;
407}
408
409int agi_version() {
410 return loader->version;
411}
412
413int agi_get_release() {
414 return loader->int_version;
415}
416
417void agi_set_release(int n) {
418 loader->int_version = n;
419}
420
421int agi_load_resource(int r, int n) {
422 int i;
423
424 i = loader->load_resource(r, n);
425#ifdef PATCH_LOGIC
426 if (r == rLOGIC)
427 patch_logic(n);
428#endif
429
430 return i;
431}
432
433int agi_unload_resource(int r, int n) {
434 return loader->unload_resource(r, n);
435}
436
437const char *_savePath; // FIXME: Get rid of this
438extern AGIMusic *g_agi_music;
439
440struct GameSettings {
441 const char *gameid;
442 const char *description;
443 byte id;
444 uint32 features;
445 const char *detectname;
446};
447
448static const GameSettings agi_settings[] = {
449 {"agi", "AGI game", GID_AGI, MDT_ADLIB, "OBJECT"},
450 {NULL, NULL, 0, 0, NULL}
451};
452
453Common::RandomSource * rnd;
454
455AgiEngine::AgiEngine(OSystem * syst) : Engine(syst) {
456
457 // Setup mixer
458 if (!_mixer->isReady()) {
459 warning("Sound initialization failed.");
460 }
461
462 _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
463 _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
464
465 _savePath = _saveFileMan->getSavePath(); // FIXME: Get rid of this
466
467 const GameSettings *g;
468
469 const char *gameid = ConfMan.get("gameid").c_str();
470 for (g = agi_settings; g->gameid; ++g)
471 if (!scumm_stricmp(g->gameid, gameid))
472 _gameId = g->id;
473
474 rnd = new Common::RandomSource();
475
476 Common::addSpecialDebugLevel(kDebugLevelMain, "Main", "Generic debug level");
477 Common::addSpecialDebugLevel(kDebugLevelResources, "Resources", "Resources debugging");
478 Common::addSpecialDebugLevel(kDebugLevelSprites, "Sprites", "Sprites debugging");
479 Common::addSpecialDebugLevel(kDebugLevelInventory, "Inventory", "Inventory debugging");
480 Common::addSpecialDebugLevel(kDebugLevelInput, "Input", "Input events debugging");
481 Common::addSpecialDebugLevel(kDebugLevelMenu, "Menu", "Menu debugging");
482 Common::addSpecialDebugLevel(kDebugLevelScripts, "Scripts", "Scripts debugging");
483 Common::addSpecialDebugLevel(kDebugLevelSound, "Sound", "Sound debugging");
484 Common::addSpecialDebugLevel(kDebugLevelText, "Text", "Text output debugging");
485
486 game.clock_enabled = false;
487 game.state = STATE_INIT;
488}
489
490void AgiEngine::initialize() {
491 memset(&opt, 0, sizeof(struct agi_options));
492 opt.gamerun = GAMERUN_RUNGAME;
493 opt.hires = true;
494
495 // TODO: Some sound emulation modes do not fit our current music
496 // drivers, and I'm not sure what they are. For now, they might
497 // as well be called "PC Speaker" and "Not PC Speaker".
498
499 switch (MidiDriver::detectMusicDriver(MDT_PCSPK)) {
500 case MD_PCSPK:
501 opt.soundemu = SOUND_EMU_PC;
502 break;
503 default:
504 opt.soundemu = SOUND_EMU_NONE;
505 break;
506 }
507
508 if (ConfMan.hasKey("render_mode"))
509 opt.renderMode = Common::parseRenderMode(ConfMan.get("render_mode").c_str());
510
511 _console = new Console(this);
512
513 init_machine();
514
515 game.color_fg = 15;
516 game.color_bg = 0;
517
518 *game.name = 0;
519
520 game.sbuf = (uint8 *) calloc(_WIDTH, _HEIGHT);
521 game.hires = (uint8 *) calloc(_WIDTH * 2, _HEIGHT);
522
523 _sprites = new SpritesMan;
524 _text = new TextMan;
525 init_video();
526
527 tick_timer = 0;
528 _timer->installTimerProc(agi_timer_function_low, 10 * 1000, NULL);
529
530 game.ver = -1; /* Don't display the conf file warning */
531
532 debugC(2, kDebugLevelMain, "Detect game");
533 if (agi_detect_game() == err_OK) {
534 game.state = STATE_LOADED;
535 debugC(2, kDebugLevelMain, "game loaded");
536 } else {
537 report("Could not open AGI game");
538 }
539
540 debugC(2, kDebugLevelMain, "Init sound");
541 init_sound();
542 g_agi_music = new AGIMusic(_mixer);
543}
544
545AgiEngine::~AgiEngine() {
546 agi_deinit();
547 delete g_agi_music;
548 deinit_sound();
549 deinit_video();
550 delete _sprites;
551 free(game.hires);
552 free(game.sbuf);
553 deinit_machine();
554 delete rnd;
555 delete _console;
556}
557
558void AgiEngine::errorString(const char *buf1, char *buf2) {
559 strcpy(buf2, buf1);
560}
561
562int AgiEngine::init() {
563 // Initialize backend
564 _system->beginGFXTransaction();
565 initCommonGFX(false);
566 _system->initSize(320, 200);
567 _system->endGFXTransaction();
568
569 initialize();
570
571 gfx_set_palette();
572
573 return 0;
574}
575
576int AgiEngine::go() {
577 CursorMan.showMouse(true);
578
579 report(" \nAGI engine " VERSION " is ready.\n");
580 if (game.state < STATE_LOADED) {
581 do {
582 main_cycle();
583 } while (game.state < STATE_RUNNING);
584 if (game.ver < 0)
585 game.ver = 0; /* Enable conf file warning */
586 }
587
588 run_game();
589
590 return 0;
591}
592
593} // End of namespace Agi
594
595GameList Engine_AGI_gameIDList() {
596 GameList games;
597 const Agi::GameSettings *g = Agi::agi_settings;
598
599 while (g->gameid) {
600 games.push_back(*g);
601 g++;
602 }
603
604 return games;
605}
606
607GameDescriptor Engine_AGI_findGameID(const char *gameid) {
608 const Agi::GameSettings *g = Agi::agi_settings;
609 while (g->gameid) {
610 if (0 == scumm_stricmp(gameid, g->gameid))
611 break;
612 g++;
613 }
614 return *g;
615}
616
617DetectedGameList Engine_AGI_detectGames(const FSList &fslist) {
618 DetectedGameList detectedGames;
619 const Agi::GameSettings * g;
620
621 for (g = Agi::agi_settings; g->gameid; ++g) {
622 // Iterate over all files in the given directory
623 for (FSList::const_iterator file = fslist.begin();
624 file != fslist.end(); ++file) {
625 const char *gameName = file->displayName().c_str();
626
627 if (0 == scumm_stricmp(g->detectname, gameName)) {
628 // Match found, add to list of candidates, then abort inner loop.
629 detectedGames.push_back(*g);
630 break;
631 }
632 }
633 }
634 return detectedGames;
635}
636
637PluginError Engine_AGI_create(OSystem *syst, Engine **engine) {
638 assert(engine);
639 *engine = new Agi::AgiEngine(syst);
640 return kNoError;
641}
642
643REGISTER_PLUGIN(AGI, "AGI Engine");