ScummVM API documentation
psx.h
1/* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 */
12
13#if !defined(SCUMM_INSANE_REBEL2_PSX_H) && defined(ENABLE_SCUMM_7_8) && defined(ENABLE_REBEL2_PSX)
14#define SCUMM_INSANE_REBEL2_PSX_H
15
16#include "common/array.h"
17#include "common/error.h"
18#include "common/hashmap.h"
19#include "common/path.h"
20#include "common/str.h"
21#include "common/types.h"
22
23#ifdef USE_TINYGL
24#include "graphics/surface.h"
25#include "graphics/tinygl/tinygl.h"
26#endif
27
28namespace Common {
29class SeekableReadStream;
30}
31
32namespace Audio {
33class RewindableAudioStream;
34}
35
36namespace Scumm {
37
38class ScummEngine_v7;
39class RA2PSXLevel1UI;
40class RA2PSXLevel2Scene;
41class RA2PSXMainMenuUI;
42class RA2PSXMovieText;
43class RA2PSXOptionsUI;
44class RA2PSXChapterSelectUI;
45class RA2PSXArchive;
46class RA2PSXModel;
47
48bool loadRA2PSXDebrisModels(const RA2PSXArchive &archive,
49 const Common::Array<byte> &textureData, Common::Array<RA2PSXModel> &models);
50
51enum RA2PSXMovieTextSequence {
52 kRA2PSXMovieTextNone,
53 kRA2PSXMovieTextOpening,
54 kRA2PSXMovieTextChapter1
55};
56
57class RA2PSXArchive {
58public:
59 bool load(Common::SeekableReadStream &stream);
60 bool getMember(const Common::String &path, Common::Array<byte> &data) const;
61
62private:
63 struct Entry {
64 Common::String name;
65 uint32 unpackedSize;
66 uint32 offset;
67 uint32 endOffset;
68 };
69
70 bool findTopLevelEntry(const Common::String &name, Entry &entry) const;
71 bool unpack(const Entry &entry, Common::Array<byte> &data) const;
72 bool findNestedMember(const Common::Array<byte> &container, const Common::String &path,
73 Common::Array<byte> &data) const;
74 static bool decompress(const byte *source, uint32 sourceSize, uint32 expectedSize,
76
78};
79
80class RA2PSXSoundBank {
81public:
82 bool load(const Common::Array<byte> &sampleData, const Common::Array<byte> &projectData);
83 Audio::RewindableAudioStream *makeStream(uint16 id, uint32 macroRate,
84 uint16 adsrId = 0xffff) const;
85 bool getSFX(uint16 id, uint16 &macro, byte &priority, byte &maxVoices) const;
86 bool getMacroCommand(uint16 macro, uint16 step, byte *command) const;
87
88private:
89 struct Sample {
90 uint32 offset;
91 uint16 id;
92 uint16 blocks;
93 uint16 rate;
94 };
95 struct SFX {
96 uint16 id;
97 uint16 macro;
98 byte priority;
99 byte maxVoices;
100 };
101 struct Macro {
102 uint32 id;
103 uint32 offset;
104 };
105 struct ADSR {
106 uint16 id;
107 uint16 attack;
108 uint16 decay;
109 uint16 sustain;
110 uint16 release;
111 };
112
113 const Sample *findSample(uint16 id) const;
114 const ADSR *findADSR(uint16 id) const;
115
117 Common::Array<byte> _projectData;
118 Common::Array<Sample> _samples;
120 Common::Array<Macro> _macros;
121 Common::Array<ADSR> _adsrs;
122};
123
124class RA2PSXSoundPlayer {
125public:
126 typedef uint32 SoundId;
127 enum { kInvalidSoundId = 0 };
128
129 RA2PSXSoundPlayer(ScummEngine_v7 *vm, const RA2PSXSoundBank &bank);
130 ~RA2PSXSoundPlayer();
131
132 // Pitch is a 14 bit bend; kNeutralBend leaves the sound at its own rate.
133 enum { kNeutralBend = 0x2000 };
134
135 SoundId play(uint16 sfx, int volume, int pan, int pitch = kNeutralBend);
136 void update();
137 void setPan(SoundId sound, int pan);
138 void setVolume(SoundId sound, int volume);
139 void setPitch(SoundId sound, int pitch);
140 void stop(SoundId sound);
141 void stopAll();
142
143private:
144 struct Impl;
145 Impl *_impl;
146};
147
148// Options menu settings; sfx steps by 7 up to 0x70, the CD volumes by 0x200 up to 0x1000.
149struct RA2PSXSettings {
150 enum {
151 kSFXMaximum = 0x70,
152 kSFXStep = 7,
153 kCDMaximum = 0x1000,
154 kCDStep = 0x200
155 };
156
157 RA2PSXSettings() { reset(); }
158
159 void reset();
160 void load();
161 void save() const;
162 void apply(ScummEngine_v7 *vm) const;
163 byte videoVolume() const;
164 // The original keeps a per-difficulty count of the chapters reached.
165 int unlockedChapters() const;
166
167 int difficulty;
168 int sfx;
169 int music;
170 int movies;
171 bool mono;
172 int unlocked[3];
173 bool unlockAll;
174};
175
176struct RA2PSXVertex {
177 int16 x;
178 int16 y;
179 int16 z;
180};
181
182// PlayStation matrix helpers: 4096 angle units per turn, 1/4096 fixed point scales,
183// and pre-rotating multiplies from the left.
184struct RA2PSXMatrix {
185 RA2PSXMatrix() { setIdentity(); }
186
187 void setIdentity();
188 void setScale(int x, int y, int z);
189 void setRotationX(int angle);
190 void setRotationY(int angle);
191 void setRotationZ(int angle);
192 void preRotateX(int angle);
193 void preRotateY(int angle);
194 void preRotateZ(int angle);
195 void setTranslation(int x, int y, int z);
196
197 float rotation[3][3];
198 float translation[3];
199};
200
201struct RA2PSXTexture {
202 Common::String name;
203 uint16 width;
204 uint16 height;
206 // The resolved CLUT, kept so sprites streamed into this texture's VRAM slot can
207 // borrow its colours the way the original's fixed page assignment does.
208 Common::Array<uint32> palette;
209};
210
211uint32 decodeRA2PSXColor(uint16 value);
212bool loadRA2PSXTextures(const Common::Array<byte> &data,
214
215// One frame of a playN script: where it lands on screen, its 8 bit pixels, and the flags
216// that say whether it can be shot, whether it hurts the player, and when it makes noise.
217struct RA2PSXPlayFrame {
218 RA2PSXPlayFrame() : x(0), y(0), width(0), height(0), boxLeft(0), boxTop(0),
219 boxRight(0), boxBottom(0), flags(0) {}
220
221 int16 x;
222 int16 y;
223 int16 width;
224 int16 height;
225 int16 boxLeft;
226 int16 boxTop;
227 int16 boxRight;
228 int16 boxBottom;
229 uint16 flags;
230 Common::Array<byte> pixels;
231};
232
233enum {
234 kRA2PSXPlayLastFrame = 0x0001,
235 kRA2PSXPlayCompressed = 0x0002,
236 kRA2PSXPlayTargetable = 0x0004,
237 kRA2PSXPlayHitsPlayer = 0x0008,
238 kRA2PSXPlaySound = 0x0010
239};
240
241typedef Common::Array<RA2PSXPlayFrame> RA2PSXPlayAnimation;
242
243// playN: a table of animation offsets, each opening a chain of frame records.
244bool loadRA2PSXPlayScript(const Common::Array<byte> &data,
246
247// The fireball frames are 68x56, the size the original's VRAM upload uses.
248enum { kRA2PSXExplosionHeight = 56 };
249
250// bigEx: a 4 byte format word, a 256 colour palette and equal sized 8 bit frames.
251bool loadRA2PSXSpriteAnimation(const Common::Array<byte> &data, uint16 frameHeight,
252 Common::Array<RA2PSXTexture> &frames, const Common::Array<uint32> *clut = nullptr);
253
254// Level 2's three parts, each a sheet of backdrop halves, parallax walls and actors.
255enum {
256 kRA2PSXLevel2PartCount = 3,
257 kRA2PSXLevel2LayerCount = 5,
258 kRA2PSXLevel2SceneTop = 28,
259 // Frames 0 to 4 are the rookie leaning out of cover; 5 to 29 are a five by five
260 // grid of aim poses picked from where the crosshair sits.
261 kRA2PSXLevel2CoverFrames = 5,
262 // Part three's rookie owns three more poses, and shifts its aim grid past them.
263 kRA2PSXLevel2FrameCount = 33,
264 kRA2PSXLevel2RookieDelay = 3,
265 kRA2PSXLevel2AimColumns = 5,
266 kRA2PSXLevel2AimRows = 5,
267 // The slide runs at a constant speed of distance/20 for 14 ticks, then decays by
268 // 0xe000/0x10000 a tick until it lands - ra2 eases out rather than lerping.
269 kRA2PSXLevel2ScrollSteps = 20,
270 kRA2PSXLevel2ScrollHold = 14,
271 kRA2PSXLevel2ScrollDamping = 0xe000
272};
273
274enum {
275 kRA2PSXLevel2TrooperCount = 3,
276 // The gameplay clock is the 60Hz vblank, and a play script steps every fourth tick.
277 kRA2PSXLevel2TickRate = 60,
278 kRA2PSXLevel2FrameTicks = 4,
279 // A part ends a second after its last trooper falls.
280 kRA2PSXLevel2ClearTicks = 60,
281 // A trooper hides behind its own sprite until its slot has counted down.
282 kRA2PSXLevel2SlotNever = 0x7fffffff,
283 // How often a held fire button repeats, in ticks.
284 kRA2PSXLevel2FireRepeat = 12,
285 // A player bolt runs a 3D line from the gun to the crosshair: the parameter steps
286 // by 300 of 4096 a tick, the bolt stops drawing at 4000 and is dropped past 0x112f.
287 // Two slots, the same as the DOS build's cover handler.
288 kRA2PSXLevel2ShotCount = 2,
289 kRA2PSXLevel2ShotStep = 300,
290 kRA2PSXLevel2ShotDraw = 4000,
291 kRA2PSXLevel2ShotEnd = 0x112f,
292 // The bolt leaves the muzzle at z 1000 and reaches the crosshair at z 18000.
293 kRA2PSXLevel2ShotNearZ = 1000,
294 kRA2PSXLevel2ShotFarZ = 18000
295};
296
297// The states a play script actor walks, named after the values the original stores.
298enum {
299 kRA2PSXLevel2StateIdle = 0,
300 kRA2PSXLevel2StateAppear = 1,
301 kRA2PSXLevel2StateAimed = 2,
302 kRA2PSXLevel2StateWild = 3,
303 kRA2PSXLevel2StateDie = 4,
304 kRA2PSXLevel2StateShot = 5,
305 kRA2PSXLevel2StateCover = 0x80
306};
307
308// One of the player's own bolts, travelling from the gun toward the crosshair.
309struct RA2PSXLevel2Shot {
310 RA2PSXLevel2Shot() : step(0), muzzleX(0), muzzleY(0), targetX(0), targetY(0) {}
311
312 // Zero when the slot is free; otherwise how far along the line the bolt is.
313 int step;
314 // Both ends in screen space: the gun this pose fires from, and the crosshair.
315 int muzzleX;
316 int muzzleY;
317 int targetX;
318 int targetY;
319};
320
321// One play script actor: a trooper, or the bolt it fires.
322struct RA2PSXLevel2Actor {
323 RA2PSXLevel2Actor() : state(kRA2PSXLevel2StateIdle), animation(-1), frame(0), hold(0),
324 hit(false), slotTick(0), fireNext(0), fireEnd(0) {}
325
326 int state;
327 int animation;
328 int frame;
329 int hold;
330 bool hit;
331 int slotTick;
332 int fireNext;
333 int fireEnd;
334};
335
336// The frame window a trooper shoots inside, per part, per trooper, per animation.
337struct RA2PSXLevel2FireWindow {
338 int16 start;
339 int16 end;
340};
341
342// Where each rookie frame is drawn, straight out of the original's per level table.
343struct RA2PSXLevel2Pose {
344 int16 x;
345 int16 y;
346 int16 width;
347 int16 height;
348};
349
350// One backdrop sprite, as the original's display list holds it: a fixed screen position
351// the scroll is subtracted from, and the size the SPRT draws rather than the sheet's.
352struct RA2PSXLevel2Layer {
353 const char *name;
354 int16 x;
355 int16 y;
356 int16 width;
357 int16 height;
358 // 0 rides the backdrop scroll, 1 the nearer parallax one.
359 byte plane;
360};
361
362struct RA2PSXLevel2PartInfo {
363 const char *sheet;
364 const char *anims;
365 // Where each plane sits in cover and out in the open, as 16.16 pixels.
366 int16 coverScroll[2][2];
367 int16 openScroll[2][2];
368 // Which axis eases out; the other one holds its speed all the way.
369 byte scrollAxis;
370 RA2PSXLevel2Layer layers[kRA2PSXLevel2LayerCount];
371 int rookieOffsetX;
372 int rookieOffsetY;
373 // Crosshair box, then the thresholds that split it into the aim grid.
374 int16 aimLeft, aimTop, aimRight, aimBottom;
375 int16 aimColumns[kRA2PSXLevel2AimColumns];
376 int16 aimRows[kRA2PSXLevel2AimRows];
377 // Where the aim grid starts; the cover run and the pose count shift with it.
378 int16 aimBase;
379 RA2PSXLevel2Pose poses[kRA2PSXLevel2FrameCount];
380 // How many trooper slots the part scripts, and the sheet sprites whose palettes the
381 // streamed trooper and bolt frames borrow.
382 int trooperCount;
383 const char *trooperPalettes[kRA2PSXLevel2TrooperCount];
384 const char *boltPalettes[kRA2PSXLevel2TrooperCount];
385 // Empty windows mean the part fires as it changes state instead of on a frame count.
386 RA2PSXLevel2FireWindow fireWindows[kRA2PSXLevel2TrooperCount][3];
387};
388
389// Waves per part by difficulty, and the timings and odds that drive a trooper's slot.
390extern const int16 kRA2PSXLevel2WaveTable[kRA2PSXLevel2PartCount][3][2];
391// Per slot: how long until it reappears, the odds it aims rather than sprays, and how
392// long a killed trooper's spot stays empty.
393extern const int16 kRA2PSXLevel2SlotTable[kRA2PSXLevel2PartCount][3][5];
394// How many animation frames pass between two shots. Part 3 fires on state changes only.
395extern const int16 kRA2PSXLevel2FireTable[2][3][2];
396// The odds an enemy bolt connects, and what it takes off the shield when it does.
397extern const int16 kRA2PSXLevel2BoltTable[kRA2PSXLevel2PartCount][3][2];
398extern const int16 kRA2PSXLevel2KillScore[3];
399
400extern const RA2PSXLevel2PartInfo kRA2PSXLevel2Parts[kRA2PSXLevel2PartCount];
401
402// How far the level 1 background is panned and tilted inside its oversized frame, and the
403// matching shift the 3D scene rides along with.
404struct RA2PSXBackgroundView {
405 RA2PSXBackgroundView() : panX(0), panY(0), tiltLeft(0), tiltRight(0),
406 sceneX(0), sceneY(0) {}
407
408 int panX;
409 int panY;
410 int tiltLeft;
411 int tiltRight;
412 int sceneX;
413 int sceneY;
414};
415
416struct RA2PSXFace {
417 uint16 vertex[4];
418 int16 normalX[4];
419 int16 normalY[4];
420 int16 normalZ[4];
421 byte u[4];
422 byte v[4];
423 byte vertexCount;
424 byte mode;
425 byte r[4];
426 byte g[4];
427 byte b[4];
428 int16 texture;
429};
430
431class RA2PSXModel {
432public:
433 RA2PSXModel();
434
435 bool load(const Common::Array<byte> &data);
436 bool loadTextures(const Common::Array<byte> &data);
437 const Common::Array<RA2PSXVertex> &vertices() const { return _vertices; }
438 const Common::Array<RA2PSXFace> &faces() const { return _faces; }
439 const RA2PSXTexture *texture(int index) const;
440 float radius() const { return _radius; }
441
442private:
443 bool parseModel(const Common::Array<byte> &data, uint32 modelOffset, int depth);
444 bool parseObject(const Common::Array<byte> &data, uint32 objectOffset);
445
449 float _radius;
450};
451
452#ifdef USE_TINYGL
453class RA2PSXTinyGLRenderer {
454public:
455 RA2PSXTinyGLRenderer();
456 ~RA2PSXTinyGLRenderer();
457
458 bool init(int width, int height);
459 void beginFrame(const Graphics::Surface &background);
460 void beginFrame(const Graphics::Surface &background, const RA2PSXBackgroundView &view);
461 // Shifts every world space draw, the way the original moves the GTE screen offset.
462 void setViewOffset(int x, int y) { _viewOffsetX = x; _viewOffsetY = y; }
463 // A camera facing quad, sized in world units like the original's explosion billboard.
464 void renderSprite(const RA2PSXTexture &texture, float x, float y, float z,
465 float halfWidth, float halfHeight, int rotation, int brightness = 0x80);
466 void renderModel(const RA2PSXModel &model, float x, float y, float size,
467 float pitch, float yaw, float roll, bool depthTest = true);
468 void renderPerspectiveModel(const RA2PSXModel &model, float x, float y, float z,
469 float directionX, float directionY, float directionZ, float roll,
470 bool depthTest = true, int scale = 0x1000);
471 void renderTransformedModel(const RA2PSXModel &model, const RA2PSXMatrix &transform,
472 bool depthTest = true);
473 void finishFrame(Graphics::Surface &surface);
474
475private:
476 struct TextureBinding {
477 const RA2PSXTexture *texture;
478 TGLuint id;
479 };
480
481 void setFaceState(const RA2PSXModel &model, const RA2PSXFace &face);
482 void setFaceColor(const RA2PSXFace &face, uint vertexIndex,
483 float normalX, float normalY, float normalZ, float depth);
484 TGLuint getTextureId(const RA2PSXTexture &texture);
485
486 TinyGL::ContextHandle *_context;
487 Common::Array<TextureBinding> _textureBindings;
488 const RA2PSXTexture *_activeTexture;
489 bool _textureEnabled;
490 bool _blendEnabled;
491 int _width;
492 int _height;
493 int _viewOffsetX;
494 int _viewOffsetY;
495};
496#endif
497
498class Rebel2PSX {
499public:
500 explicit Rebel2PSX(ScummEngine_v7 *vm);
501 ~Rebel2PSX() = default;
502
503 Common::Error runGame();
504
505private:
506 class Level1Handler;
507 class Level2Handler;
508
509 enum MenuResult {
510 kMenuStart,
511 kMenuQuit
512 };
513
514 enum Level1Result {
515 kLevel1Quit,
516 kLevel1Complete,
517 kLevel1Death,
518 kLevel1Error
519 };
520
521 Common::SeekableReadStream *openResource(int number);
522 Common::SeekableReadStream *openRawFile(const Common::Path &path, int discNumber);
523 bool playVideo(const Common::Path &path, int discNumber, bool version2,
524 const RA2PSXMovieText *movieText = nullptr,
525 RA2PSXMovieTextSequence textSequence = kRA2PSXMovieTextNone);
526 bool playIntroSequence(const RA2PSXMovieText &movieText);
527 MenuResult runMainMenu(const RA2PSXMainMenuUI &ui, const RA2PSXOptionsUI &options);
528#ifdef USE_TINYGL
529 // TinyGL tracks one context, so the options screen borrows the title screen's renderer.
530 void runOptionsMenu(const RA2PSXOptionsUI &ui, RA2PSXSoundPlayer &sound,
531 RA2PSXTinyGLRenderer &renderer);
532#endif
533 // Returns the chosen chapter, 1 to 16, or 0 when the player backs out.
534 int runChapterSelect(const RA2PSXChapterSelectUI &ui);
535 bool loadGlobalAssets(RA2PSXMainMenuUI &menu);
536 bool loadMovieTextAssets(RA2PSXMovieText &movieText);
537 bool loadLevel1Assets(RA2PSXModel &enemy, RA2PSXModel &ship,
538 RA2PSXModel &crosshair, RA2PSXModel &laser, RA2PSXModel &tieLaser,
539 Common::Array<RA2PSXModel> &debris, RA2PSXLevel1UI &ui);
540 Common::Error runLevel2();
541 Level1Result playLevel1(const RA2PSXModel &enemy, const RA2PSXModel &ship,
542 const RA2PSXModel &crosshair, const RA2PSXModel &laser,
543 const RA2PSXModel &tieLaser, const Common::Array<RA2PSXModel> &debris,
544 const RA2PSXLevel1UI &ui, int &lives, int &score);
545
546 ScummEngine_v7 *_vm;
547 RA2PSXSoundBank _soundBank;
548 RA2PSXSettings _settings;
549 // The crest on the title screen, the freighter behind the options list and
550 // the double sided crest on the chapter select.
551 RA2PSXModel _logoModel;
552 RA2PSXModel _cloakModel;
553 RA2PSXModel _crestModel;
554 // The 42 frame fireball the original streams from the bigEx resource, and the
555 // single SMALLEX cell it trails smoke with.
556 Common::Array<RA2PSXTexture> _explosionFrames;
557 RA2PSXTexture _smokeTexture;
558};
559
560} // End of namespace Scumm
561
562#endif
Definition: audiostream.h:109
Definition: error.h:81
Definition: path.h:52
Definition: stream.h:745
Definition: str.h:59
Definition: system.h:39
Definition: algorithm.h:29
Definition: actor.h:30
Definition: surface.h:67