Font patch from Andrea Corna - improved keyframe support - SMUSH looping support - improved IMUSE stability - additional error checking

This commit is contained in:
Erich Edgar Hoover 2005-07-17 23:40:22 +00:00
parent 6bebeb9fa2
commit b537213d94
30 changed files with 897 additions and 321 deletions

20
README
View file

@ -1,5 +1,5 @@
Residual: A LucasArts 3D game interpreter Version: 0.04-CVS Residual: A LucasArts 3D game interpreter Version: 0.04-CVS
(C) 2003-2005 The ScummVM-Residual team Last Updated: 03 Apr 2005 (C) 2003-2005 The ScummVM-Residual team Last Updated: 17 Jul 2005
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
What is Residual? What is Residual?
@ -55,8 +55,15 @@ renderer (-soft).
What is the state of Residual? What is the state of Residual?
------------------------------- -------------------------------
Basic gameplay works, including cutscenes. Some of the game is playable, Basic gameplay works, including cutscenes. Some of the game is playable,
but many features are either missing or unstable. There are no menus, but many features are either missing or unstable. There is no abilitity
save/load features, lighting, etc. Crashes are likely. to save/load, lighting, etc. Crashes are likely.
Game currently playable to:
Manny reaps Meche (Mercedes Colomar)
Caveats:
1) Random crash leaving the scene with the balloon handler
2) Must press "Esc" after listening to Meche, conversation doesn't
continue on its own
What are the default keys? What are the default keys?
-------------------------- --------------------------
@ -65,7 +72,8 @@ Arrow keys : Movement
Shift : Hold to run Shift : Hold to run
Enter : Selects items in inventory, conversation, etc Enter : Selects items in inventory, conversation, etc
Escape : Skips cutscenes, exits certain screens Escape : Skips cutscenes, exits certain screens
q : Quit q : Exit Dialog Menu
Ctrl + c : Force Quit (from command-line)
Development/debug keys from the original game Development/debug keys from the original game
--------------------------------------------- ---------------------------------------------
@ -95,7 +103,6 @@ Don't. Residual is very alpha and we KNOW it doesn't work right.
What else should I know? What else should I know?
------------------------ ------------------------
* Exit with 'q', and don't press f1, as things will go loopy.
* See TODO for other stuff * See TODO for other stuff
Credits: Credits:
@ -109,7 +116,8 @@ Contributors:
Vincent Hamm Various engine code Vincent Hamm Various engine code
Lionel 'bbrox' Ulmer OpenGL optimisations Lionel 'bbrox' Ulmer OpenGL optimisations
Ori 'salty-horse' Avtalion Lipsync, LAF support Ori 'salty-horse' Avtalion Lipsync, LAF support
Erich 'Compholio' Hoover x86-64, subtitles fixes, menu support Erich 'Compholio' Hoover x86-64, various fixes and comments, menu support, improved state support
Andrea 'Yak Bizzarro' Corna Improved font support
Special Thanks To: Special Thanks To:
------------------ ------------------

7
TODO
View file

@ -1,17 +1,22 @@
Residual TODO list (in rough order of priority): Residual TODO list (in rough order of priority):
------------------------------------------------ ------------------------------------------------
Assigned tasks: Assigned tasks:
* Make game playable to the petrified forest (compholio)
* Finish text drawing support (salty-horse, aquadran) * Finish text drawing support (salty-horse, aquadran)
* Cross platform GUI for debug input dialogs and path selection (ender) * Cross platform GUI for debug input dialogs and path selection (ender)
* Improved walk box code (frob) * Improved walk box code (frob)
* Implement FadeInChore and FadeOutChore (frob) * Implement FadeInChore and FadeOutChore (frob)
* Implement texture mapping with light shading in TinyGL (aquadran) * Implement texture mapping with light shading in TinyGL (aquadran)
* Improve menu support (compholio)
* Improve SMUSH looping support (compholio)
Unassigned (help wanted): Unassigned (help wanted):
* Fix random problem with Manny changing between scenes, switching between
fe.set and st.set is a good example of where it happens a lot
* Add configure script (Custom ala main ScummVM, NOT autoconf) * Add configure script (Custom ala main ScummVM, NOT autoconf)
* Proper light setup in drivers * Proper light setup in drivers
* Finish Save/Load support for rest of Engine (Lua and iMuse done) * Finish Save/Load support for rest of Engine (Lua and iMuse done)
* Implement 2D primitives * Finish 2D primitive code
* Proper vsscanf implementation in textsplit.cpp for platforms without it (MSVC, etc) * Proper vsscanf implementation in textsplit.cpp for platforms without it (MSVC, etc)
* Make SMUSH work on Linux/PPC (whats wrong with it, exactly? - ender :) * Make SMUSH work on Linux/PPC (whats wrong with it, exactly? - ender :)
* Finish panning in 3d position code * Finish panning in 3d position code

View file

@ -34,7 +34,7 @@
Actor::Actor(const char *name) : Actor::Actor(const char *name) :
_name(name), _talkColor(255, 255, 255), _pos(0, 0, 0), _name(name), _talkColor(255, 255, 255), _pos(0, 0, 0),
_pitch(0), _yaw(0), _roll(0), _walkRate(0), _turnRate(0), _pitch(0), _yaw(0), _roll(0), _walkRate(0), _turnRate(0),
_reflectionAngle(80), _reflectionAngle(80), _setName(""), _setNameTmp(""),
_visible(true), _lipSynch(NULL), _turning(false), _walking(false), _visible(true), _lipSynch(NULL), _turning(false), _walking(false),
_restCostume(NULL), _restChore(-1), _restCostume(NULL), _restChore(-1),
_walkCostume(NULL), _walkChore(-1), _walkedLast(false), _walkedCur(false), _walkCostume(NULL), _walkChore(-1), _walkedLast(false), _walkedCur(false),
@ -278,9 +278,11 @@ void Actor::sayLine(const char *msg, const char *msgId) {
return; return;
} }
// During movies, SayLine is called for text display only // During Fullscreen movies SayLine is called for text display only
if (!g_smush->isPlaying()) { // However, normal SMUSH movies may call SayLine, for example:
// When Domino yells at Manny (a SMUSH movie) he does it with
// a SayLine request rather than as part of the movie!
if (!g_smush->isPlaying() || g_engine->getMode() == ENGINE_MODE_NORMAL) {
std::string soundName = msgId; std::string soundName = msgId;
std::string soundLip = msgId; std::string soundLip = msgId;
soundName += ".wav"; soundName += ".wav";
@ -306,7 +308,11 @@ void Actor::sayLine(const char *msg, const char *msgId) {
// Also, some lip synch files have no entries // Also, some lip synch files have no entries
// In these cases, revert to using the mumble chore. // In these cases, revert to using the mumble chore.
_lipSynch = g_resourceloader->loadLipSynch(soundLip.c_str()); _lipSynch = g_resourceloader->loadLipSynch(soundLip.c_str());
// If there's no lip synch file then load the mumble chore if it exists
// (the mumble chore doesn't exist with the cat races announcer)
if (_lipSynch == NULL && _mumbleChore != -1)
_mumbleCostume->playChoreLooping(_mumbleChore);
_talkAnim = -1; _talkAnim = -1;
} }
} }
@ -325,7 +331,7 @@ void Actor::sayLine(const char *msg, const char *msgId) {
// of the screen // of the screen
if (!visible() || !inSet(g_engine->currScene()->name())) { if (!visible() || !inSet(g_engine->currScene()->name())) {
_sayLineText->setX(640 / 2); _sayLineText->setX(640 / 2);
_sayLineText->setY(440); _sayLineText->setY(420);
} else { } else {
// render at the top for active actors for now // render at the top for active actors for now
_sayLineText->setX(640 / 2); _sayLineText->setX(640 / 2);
@ -336,11 +342,16 @@ void Actor::sayLine(const char *msg, const char *msgId) {
} }
bool Actor::talking() { bool Actor::talking() {
// If there's no sound file then we're obviously not talking
if (strlen(_talkSoundName.c_str()) == 0)
return false;
return g_imuse->getSoundStatus(_talkSoundName.c_str()); return g_imuse->getSoundStatus(_talkSoundName.c_str());
} }
void Actor::shutUp() { void Actor::shutUp() {
g_imuse->stopSound(_talkSoundName.c_str()); // Don't stop the sound, the call to stop the sound
// is made by the game
_talkSoundName = ""; _talkSoundName = "";
if (_lipSynch != NULL) { if (_lipSynch != NULL) {
if ((_talkAnim != -1) && (_talkChore[_talkAnim] >= 0)) if ((_talkAnim != -1) && (_talkChore[_talkAnim] >= 0))
@ -387,6 +398,18 @@ void Actor::popCostume() {
freeCostumeChore(_costumeStack.back(), _talkCostume[i], _talkChore[i]); freeCostumeChore(_costumeStack.back(), _talkCostume[i], _talkChore[i]);
delete _costumeStack.back(); delete _costumeStack.back();
_costumeStack.pop_back(); _costumeStack.pop_back();
Costume *newCost;
if (_costumeStack.empty())
newCost = NULL;
else
newCost = _costumeStack.back();
if (newCost == NULL) {
if (debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
printf("Popped (freed) the last costume for an actor.\n");
}
} else {
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
warning("Attempted to pop (free) a costume when the stack is empty!");
} }
} }
@ -487,7 +510,14 @@ void Actor::update() {
// Update lip synching // Update lip synching
if (_lipSynch != NULL) { if (_lipSynch != NULL) {
int posSound = g_imuse->getPosIn60HzTicks(_talkSoundName.c_str()); int posSound;
// While getPosIn60HzTicks will return "-1" to indicate that the
// sound is no longer playing, it is more appropriate to check first
if(g_imuse->getSoundStatus(_talkSoundName.c_str()))
posSound = g_imuse->getPosIn60HzTicks(_talkSoundName.c_str());
else
posSound = -1;
if (posSound != -1) { if (posSound != -1) {
int anim = _lipSynch->getAnim(posSound); int anim = _lipSynch->getAnim(posSound);
if (_talkAnim != anim) { if (_talkAnim != anim) {

11
actor.h
View file

@ -54,7 +54,13 @@ public:
float roll() const { return _roll; } float roll() const { return _roll; }
void setVisibility(bool val) { _visible = val; } void setVisibility(bool val) { _visible = val; }
bool visible() const { return _visible; } bool visible() const { return _visible; }
void putInSet(const char *name) { _setName = name; } // Don't actually change the set immediately, see engine.cpp for details
void putInSet(const char *name) { _setNameTmp = name; }
void putInSet() {
if (_setName != _setNameTmp) {
_setName = _setNameTmp;
}
}
void setTurnRate(float rate) { _turnRate = rate; } void setTurnRate(float rate) { _turnRate = rate; }
float turnRate() const { return _turnRate; } float turnRate() const { return _turnRate; }
void setWalkRate(float rate) { _walkRate = rate; } void setWalkRate(float rate) { _walkRate = rate; }
@ -128,7 +134,8 @@ public:
private: private:
std::string _name; std::string _name;
std::string _setName; std::string _setName; // The actual current set
std::string _setNameTmp; // The temporary name for the set
Color _talkColor; Color _talkColor;
Vector3d _pos; Vector3d _pos;
float _pitch, _yaw, _roll; float _pitch, _yaw, _roll;

View file

@ -565,7 +565,10 @@ public:
void setMapName(char *) { } void setMapName(char *) { }
void setKey(int val); void setKey(int val);
void reset(); void reset();
~SoundComponent() { } ~SoundComponent() {
// Stop the sound if it's in progress
reset();
}
private: private:
std::string _soundName; std::string _soundName;
@ -584,14 +587,12 @@ SoundComponent::SoundComponent(Costume::Component *parent, int parentID, const c
void SoundComponent::setKey(int val) { void SoundComponent::setKey(int val) {
switch (val) { switch (val) {
case 0: // "Play" case 0: // "Play"
if (!g_imuse->getSoundStatus(_soundName.c_str())) { // No longer a need to check the sound status, if it's already playing
// g_imuse->stopSound(_soundName.c_str()); // then it will just use the existing handle
// } else { g_imuse->startSfx(_soundName.c_str());
g_imuse->startSfx(_soundName.c_str()); if (g_engine->currScene() && g_currentUpdatedActor) {
if (g_engine->currScene() && g_currentUpdatedActor) { Vector3d pos = g_currentUpdatedActor->pos();
Vector3d pos = g_currentUpdatedActor->pos(); g_engine->currScene()->setSoundPosition(_soundName.c_str(), pos);
g_engine->currScene()->setSoundPosition(_soundName.c_str(), pos);
}
} }
break; break;
case 1: // "Stop" case 1: // "Stop"
@ -607,13 +608,15 @@ void SoundComponent::setKey(int val) {
} }
void SoundComponent::reset() { void SoundComponent::reset() {
g_imuse->stopSound(_soundName.c_str()); // A lot of the sound components this gets called against aren't actually running
if(g_imuse->getSoundStatus(_soundName.c_str()))
g_imuse->stopSound(_soundName.c_str());
} }
Costume::Costume(const char *filename, const char *data, int len, Costume *prevCost) : Costume::Costume(const char *filename, const char *data, int len, Costume *prevCost) :
_fname(filename), _colormap(DEFAULT_COLORMAP) { _fname(filename), _colormap(DEFAULT_COLORMAP) {
TextSplitter ts(data, len); TextSplitter ts(data, len);
ts.expectString("costume v0.1"); ts.expectString("costume v0.1");
ts.expectString("section tags"); ts.expectString("section tags");
int numTags; int numTags;
@ -685,8 +688,11 @@ Costume::Costume(const char *filename, const char *data, int len, Costume *prevC
Costume::~Costume() { Costume::~Costume() {
stopChores(); stopChores();
for (int i = _numComponents - 1; i >= 0; i--) for (int i = _numComponents - 1; i >= 0; i--) {
delete _components[i]; // The "Sprite" component can be NULL
if (_components[i] != NULL)
delete _components[i];
}
delete[] _chores; delete[] _chores;
} }
@ -695,6 +701,7 @@ Costume::Component::Component(Component *parent, int parentID, char *tag) {
_cost = NULL; _cost = NULL;
setParent(parent); setParent(parent);
memcpy(_tag, tag, 4); memcpy(_tag, tag, 4);
_tag[5] = 0;
} }
void Costume::Component::setParent(Component *newParent) { void Costume::Component::setParent(Component *newParent) {
@ -833,6 +840,37 @@ Costume::Component *Costume::loadComponent (char tag[4], Costume::Component *par
return NULL; return NULL;
} }
Model::HierNode *Costume::getModelNodes()
{
for(int i=0;i<_numComponents;i++) {
if (_components[i] == NULL)
continue;
// Needs to handle Main Models (pigeons) and normal Models
// (when Manny climbs the rope)
if (std::memcmp(_components[i]->tag(), "mmdl", 4) == 0)
return dynamic_cast<ModelComponent *>(_components[i])->hierarchy();
}
return NULL;
}
void Costume::playChoreLooping(int num) {
if (num < 0 || num >= _numChores) {
if (debugLevel == DEBUG_CHORES || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
warning("Requested chore number %d is outside the range of chores (0-%d)!", num, _numChores);
return;
}
_chores[num].playLooping();
}
void Costume::playChore(int num) {
if (num < 0 || num >= _numChores) {
if (debugLevel == DEBUG_CHORES || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
warning("Requested chore number %d is outside the range of chores (0-%d)!", num, _numChores);
return;
}
_chores[num].play();
}
void Costume::stopChores() { void Costume::stopChores() {
for (int i = 0; i < _numChores; i++) for (int i = 0; i < _numChores; i++)
_chores[i].stop(); _chores[i].stop();

View file

@ -19,6 +19,7 @@
#define COSTUME_H #define COSTUME_H
#include "matrix4.h" #include "matrix4.h"
#include "model.h"
#include <string> #include <string>
@ -32,13 +33,13 @@ public:
~Costume(); ~Costume();
const char *filename() const { return _fname.c_str(); } const char *filename() const { return _fname.c_str(); }
void playChore(int num);
void playChore(int num) { _chores[num].play(); } void playChoreLooping(int num);
void playChoreLooping(int num) { _chores[num].playLooping(); }
void setChoreLastFrame(int num) { _chores[num].setLastFrame(); } void setChoreLastFrame(int num) { _chores[num].setLastFrame(); }
void setChoreLooping(int num, bool val) { _chores[num].setLooping(val); } void setChoreLooping(int num, bool val) { _chores[num].setLooping(val); }
void stopChore(int num) { _chores[num].stop(); } void stopChore(int num) { _chores[num].stop(); }
char *getColormap() { return _colormap; } char *getColormap() { return _colormap; }
Model::HierNode *getModelNodes();
void setColormap(char *map) { void setColormap(char *map) {
_colormap = map; _colormap = map;
for(int i=0;i<_numComponents;i++) { for(int i=0;i<_numComponents;i++) {
@ -81,7 +82,7 @@ std::memcmp(_components[i]->tag(), "mat ", 4) == 0
virtual ~Component() { } virtual ~Component() { }
protected: protected:
char _tag[4]; char _tag[5];
int _parentID; int _parentID;
Component *_parent, *_child, *_sibling; Component *_parent, *_child, *_sibling;
Matrix4 _matrix; Matrix4 _matrix;

View file

@ -30,6 +30,7 @@ enum enDebugLevels {
DEBUG_MODEL, DEBUG_MODEL,
DEBUG_STUB, DEBUG_STUB,
DEBUG_SMUSH, DEBUG_SMUSH,
DEBUG_IMUSE,
DEBUG_CHORES, DEBUG_CHORES,
DEBUG_ALL DEBUG_ALL
}; };
@ -44,6 +45,7 @@ static const char *debug_levels[] = {
"MODEL", "MODEL",
"STUB", "STUB",
"SMUSH", "SMUSH",
"IMUSE",
"CHORE", "CHORE",
"ALL" "ALL"
}; };
@ -56,7 +58,8 @@ static const char *debug_descriptions[] = {
"Bitmap debug messages will be printed", "Bitmap debug messages will be printed",
"Model debug messages will be printed", "Model debug messages will be printed",
"Stub (missing function) debug messages will be printed", "Stub (missing function) debug messages will be printed",
"SMUSH debug messages will be printed", "SMUSH (video) debug messages will be printed",
"IMUSE (audio) debug messages will be printed",
"Chore debug messages will be printed", "Chore debug messages will be printed",
"All debug messages will be printed", "All debug messages will be printed",
}; };

View file

@ -368,8 +368,13 @@ void DriverGL::drawBitmap(const Bitmap *bitmap) {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
} else if (bitmap->_format == 5) { // ZBuffer image } else if (bitmap->_format == 5) { // ZBuffer image
// Only draw the manual zbuffer when enabled // Only draw the manual zbuffer when enabled
if (ZBUFFER_GLOBAL) if (ZBUFFER_GLOBAL) {
drawDepthBitmap(bitmap->_x, bitmap->_y, bitmap->_width, bitmap->_height, bitmap->_data[bitmap->_currImage - 1]); if (bitmap->_currImage - 1 < bitmap->_numImages) {
drawDepthBitmap(bitmap->_x, bitmap->_y, bitmap->_width, bitmap->_height, bitmap->_data[bitmap->_currImage - 1]);
} else {
warning("zbuffer image has index out of bounds! %d/%d\n", bitmap->_currImage, bitmap->_numImages);
}
}
} }
glEnable(GL_LIGHTING); glEnable(GL_LIGHTING);
} }
@ -508,7 +513,6 @@ void DriverGL::drawSmushFrame(int offsetX, int offsetY) {
glDisable(GL_LIGHTING); glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
// draw // draw
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);

View file

@ -92,6 +92,10 @@ Engine::Engine() :
} }
void Engine::handleButton(int operation, int key) { void Engine::handleButton(int operation, int key) {
// If we're not supposed to handle the key then don't
if (!_controlsEnabled[key])
return;
lua_beginblock(); lua_beginblock();
lua_Object menu = getEventHandler("menuHandler"); lua_Object menu = getEventHandler("menuHandler");
if (menu != LUA_NOOBJECT && !lua_isnil(menu)) { if (menu != LUA_NOOBJECT && !lua_isnil(menu)) {
@ -139,27 +143,75 @@ void Engine::mainLoop() {
// Process events // Process events
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
if (event.type == SDL_KEYDOWN && _controlsEnabled[event.key.keysym.sym]) // Handle any button operations
handleButton(SDL_KEYDOWN, event.key.keysym.sym); if(event.type == SDL_KEYDOWN || event.type == SDL_KEYUP)
if (event.type == SDL_KEYUP && _controlsEnabled[event.key.keysym.sym]) { handleButton(event.type, event.key.keysym.sym);
handleButton(SDL_KEYUP, event.key.keysym.sym); // Check for "Hard" quit"
} if (event.type == SDL_QUIT)
if (event.type == SDL_QUIT) { return;
lua_beginblock();
lua_Object handler = getEventHandler("exitHandler");
if (handler != LUA_NOOBJECT)
lua_callfunction(handler);
lua_endblock();
}
if (event.type == SDL_KEYDOWN) { if (event.type == SDL_KEYDOWN) {
if (event.key.keysym.sym == SDLK_z) if (event.key.keysym.sym == SDLK_z
g_resourceloader->loadKeyframe("ma_card_hold.key"); && (event.key.keysym.mod & KMOD_CTRL)) {
void *resource;
int c, i = 0;
char buf[512];
// Tool for debugging the loading of a particular resource without
// having to actually make it all the way to it in the game
fprintf(stderr, "Enter resource to load (extension specifies type): ");
while (i < 512 && (c = fgetc(stdin)) != EOF && c != '\n')
buf[i++] = c;
buf[i] = '\0';
if (strncmp(buf, "exp:", 4) == 0)
// Export a resource in order to view it directly
resource = (void *) g_resourceloader->exportResource(&buf[4]);
else if (strstr(buf, ".key"))
resource = (void *) g_resourceloader->loadKeyframe(buf);
else if (strstr(buf, ".zbm") || strstr(buf, ".bm"))
resource = (void *) g_resourceloader->loadBitmap(buf);
else if (strstr(buf, ".cmp"))
resource = (void *) g_resourceloader->loadColormap(buf);
else if (strstr(buf, ".cos"))
resource = (void *) g_resourceloader->loadCostume(buf, NULL);
else if (strstr(buf, ".lip"))
resource = (void *) g_resourceloader->loadLipSynch(buf);
else if (strstr(buf, ".snm"))
resource = (void *) g_smush->play(buf, 0, 0);
else if (strstr(buf, ".wav") || strstr(buf, ".imu")) {
g_imuse->startSfx(buf);
resource = (void *) 1;
} else if (strstr(buf, ".mat")) {
CMap *cmap = g_resourceloader->loadColormap("item.cmp");
warning("Default colormap applied to resources loaded in this fashion!");
resource = (void *) g_resourceloader->loadMaterial(buf, *cmap);
} else {
warning("Resource type not understood!");
break;
}
if (resource == NULL)
warning("Requested resouce (%s) not found!");
}
if ((event.key.keysym.sym == SDLK_RETURN || if ((event.key.keysym.sym == SDLK_RETURN ||
event.key.keysym.sym == SDLK_KP_ENTER) && event.key.keysym.sym == SDLK_KP_ENTER) &&
(event.key.keysym.mod & KMOD_ALT)) (event.key.keysym.mod & KMOD_ALT)) {
g_driver->toggleFullscreenMode(); g_driver->toggleFullscreenMode();
if (event.key.keysym.sym == SDLK_q) }
return; if (event.key.keysym.sym == SDLK_q) {
lua_Object menu = getEventHandler("menuHandler");
// Can't handle the exit menu on top of the normal one
// at this time, so exit flat-out if we're in a menu
if (lua_isnil(menu)) {
printf("NOTICE: The left/right arrow keys do not update the quit screen at this time, use Y/N keys instead!\n");
lua_beginblock();
lua_Object handler = getEventHandler("exitHandler");
if (handler != LUA_NOOBJECT)
lua_callfunction(handler);
lua_endblock();
} else
return;
}
} }
} }
@ -170,6 +222,13 @@ void Engine::mainLoop() {
savegameSave(); savegameSave();
} }
// It appears that the lua tasks should run before rendering,
// if you watch the game loading screen you'll see that it
// turns out better to run this beforehand
if (!_menuMode)
// Run asynchronous tasks
lua_runtasks();
if (_mode == ENGINE_MODE_SMUSH) { if (_mode == ENGINE_MODE_SMUSH) {
if (g_smush->isPlaying()) { if (g_smush->isPlaying()) {
_movieTime = g_smush->getMovieTime(); _movieTime = g_smush->getMovieTime();
@ -186,9 +245,16 @@ void Engine::mainLoop() {
} else if (_mode == ENGINE_MODE_NORMAL) { } else if (_mode == ENGINE_MODE_NORMAL) {
g_driver->clearScreen(); g_driver->clearScreen();
// Update actor costumes // Update actor costumes & sets
for (ActorListType::iterator i = _actors.begin(); i != _actors.end(); i++) { for (ActorListType::iterator i = _actors.begin(); i != _actors.end(); i++) {
Actor *a = *i; Actor *a = *i;
// Activate the new set
// While this doesn't seem to affect anything this should be done here
// instead of inside the lua_runtasks loop, otherwise certain functions
// may request a set that was just deactivated
a->putInSet();
// Update the actor's costumes
g_currentUpdatedActor = *i; g_currentUpdatedActor = *i;
if (_currScene != NULL && a->inSet(_currScene->name()) && a->visible()) if (_currScene != NULL && a->inSet(_currScene->name()) && a->visible())
a->update(); a->update();
@ -201,6 +267,10 @@ void Engine::mainLoop() {
// Draw underlying scene components // Draw underlying scene components
if (_currScene != NULL) { if (_currScene != NULL) {
// Background objects are drawn underneath everything except the background
// There are a bunch of these, especially in the tube-switcher room
_currScene->drawBitmaps(ObjectState::OBJSTATE_BACKGROUND);
// Underlay objects are just above the background
_currScene->drawBitmaps(ObjectState::OBJSTATE_UNDERLAY); _currScene->drawBitmaps(ObjectState::OBJSTATE_UNDERLAY);
// State objects are drawn on top of other things, such as the flag // State objects are drawn on top of other things, such as the flag
// on Manny's message tube // on Manny's message tube
@ -301,10 +371,6 @@ void Engine::mainLoop() {
} }
} }
if (!_menuMode)
// Run asynchronous tasks
lua_runtasks();
if (g_imuseState != -1) { if (g_imuseState != -1) {
g_imuse->setMusicState(g_imuseState); g_imuse->setMusicState(g_imuseState);
g_imuseState = -1; g_imuseState = -1;
@ -474,6 +540,7 @@ void Engine::setSceneLock(const char *name, bool lockStatus) {
void Engine::setScene(const char *name) { void Engine::setScene(const char *name) {
Scene *scene = findScene(name); Scene *scene = findScene(name);
Scene *lastScene = _currScene;
// If the scene already exists then use the existing data // If the scene already exists then use the existing data
if (scene != NULL) { if (scene != NULL) {
@ -483,23 +550,27 @@ void Engine::setScene(const char *name) {
Block *b = g_resourceloader->getFileBlock(name); Block *b = g_resourceloader->getFileBlock(name);
if (b == NULL) if (b == NULL)
warning("Could not find scene file %s\n", name); warning("Could not find scene file %s\n", name);
if (_currScene != NULL && !_currScene->locked) {
removeScene(_currScene);
delete _currScene;
}
_currScene = new Scene(name, b->data(), b->len()); _currScene = new Scene(name, b->data(), b->len());
registerScene(_currScene); registerScene(_currScene);
_currScene->setSoundParameters(20, 127); _currScene->setSoundParameters(20, 127);
// should delete the old scene after creating the new one
if (lastScene != NULL && !lastScene->locked) {
removeScene(lastScene);
delete lastScene;
}
delete b; delete b;
} }
void Engine::setScene(Scene *scene) { void Engine::setScene(Scene *scene) {
if (_currScene != NULL && !_currScene->locked) { Scene *lastScene = _currScene;
removeScene(_currScene);
delete _currScene;
}
_currScene = scene; _currScene = scene;
_currScene->setSoundParameters(20, 127); _currScene->setSoundParameters(20, 127);
// should delete the old scene after setting the new one
if (lastScene != NULL && !lastScene->locked) {
removeScene(lastScene);
delete lastScene;
}
} }
void Engine::setTextSpeed(int speed) { void Engine::setTextSpeed(int speed) {

View file

@ -94,6 +94,7 @@ class Engine {
public: public:
void setMode(int mode) { _mode = mode; } void setMode(int mode) { _mode = mode; }
int getMode() { return _mode; }
void setSpeechMode(int mode) { _speechMode = mode; } void setSpeechMode(int mode) { _speechMode = mode; }
int getSpeechMode() { return _speechMode; } int getSpeechMode() { return _speechMode; }
@ -101,7 +102,14 @@ public:
unsigned frameStart() const { return _frameStart; } unsigned frameStart() const { return _frameStart; }
unsigned frameTime() const { return _frameTime; } unsigned frameTime() const { return _frameTime; }
float perSecond(float rate) const { return rate * _frameTime / 1000; } float perSecond(float rate) const {
// The actor "Doug" at the Kitty races has no _turnRate set by default
// so we need to return some sort of time that's non-zero for a rate
// value of zero
if (rate == 0.0)
rate = 100.0;
return rate * _frameTime / 1000;
}
int getTextSpeed() { return _textSpeed; } int getTextSpeed() { return _textSpeed; }
void setTextSpeed(int speed); void setTextSpeed(int speed);

View file

@ -82,6 +82,33 @@ Font::~Font() {
free(_fontData); free(_fontData);
} }
uint16 Font::getCharIndex(unsigned char c)
{
uint16 c2 = uint16(c);
// In order to ensure the correct character codes for
// accented characters it is necessary to check the
// requested code against the index of characters for
// the font. Previously, signed characters were
// causing the problem but it might be possible for
// an invalid character to be called for other reasons.
//
// Example: Without this fix when Manny greets Eva
// for the first time and he says "Buenos Días" the
// 'í' character will either show up as a different
// character or it crashes the game.
for (uint i = 0; i < _numChars; ++i) {
if (_charIndex[i] == c2)
return i;
}
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
warning("The requsted character (code 0x%x) does not correspond to anything in the font data!\n", c2);
// If we couldn't find the character then default to
// the first character in the font so that something
// gets loaded to prevent the game from crashing
return 0;
}
// Hardcoded default font for GUI, etc // Hardcoded default font for GUI, etc
const uint8 Font::emerFont[][13] = { const uint8 Font::emerFont[][13] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},

13
font.h
View file

@ -28,16 +28,17 @@ public:
Font(const char *filename, const char *data, int len); Font(const char *filename, const char *data, int len);
~Font(); ~Font();
int32 getCharWidth(char c) { return _charHeaders[_charIndex[c]].width; } int32 getCharWidth(unsigned char c) { return _charHeaders[getCharIndex(c)].width; }
int32 getCharHeight(char c) { return _charHeaders[_charIndex[c]].height; } int32 getCharHeight(unsigned char c) { return _charHeaders[getCharIndex(c)].height; }
int32 getCharLogicalWidth(char c) { return _charHeaders[_charIndex[c]].logicalWidth; } int32 getCharLogicalWidth(unsigned char c) { return _charHeaders[getCharIndex(c)].logicalWidth; }
int32 getCharStartingCol(char c) { return _charHeaders[_charIndex[c]].startingCol; } int32 getCharStartingCol(unsigned char c) { return _charHeaders[getCharIndex(c)].startingCol; }
int32 getCharStartingLine(char c) { return _charHeaders[_charIndex[c]].startingLine; } int32 getCharStartingLine(unsigned char c) { return _charHeaders[getCharIndex(c)].startingLine; }
const byte *getCharData(char c) { return _fontData + (_charHeaders[_charIndex[c]].offset); } const byte *getCharData(unsigned char c) { return _fontData + (_charHeaders[getCharIndex(c)].offset); }
static const uint8 Font::emerFont[][13]; static const uint8 Font::emerFont[][13];
private: private:
uint16 getCharIndex(unsigned char c);
struct CharHeader { struct CharHeader {
int32 offset; int32 offset;
int32 unknown; int32 unknown;

View file

@ -29,8 +29,7 @@
#include "imuse/imuse_sndmgr.h" #include "imuse/imuse_sndmgr.h"
#include "imuse/imuse_mcmp_mgr.h" #include "imuse/imuse_mcmp_mgr.h"
#define MAX_IMUSE_TRACKS 8 #include "imuse/limits.h"
#define MAX_IMUSE_FADETRACKS 8
struct ImuseTable { struct ImuseTable {
byte opcode; byte opcode;
@ -134,6 +133,7 @@ public:
int getGroupSfxVolume() { return _volSfx; } int getGroupSfxVolume() { return _volSfx; }
int getGroupMusicVolume() { return _volMusic; } int getGroupMusicVolume() { return _volMusic; }
Track *findTrack(const char *soundName);
void setPriority(const char *soundName, int priority); void setPriority(const char *soundName, int priority);
void setVolume(const char *soundName, int volume); void setVolume(const char *soundName, int volume);
int getVolume(const char *soundName); int getVolume(const char *soundName);
@ -151,7 +151,7 @@ public:
void flushTracks(); void flushTracks();
bool isVoicePlaying(); bool isVoicePlaying();
char *getCurMusicSoundName(); char *getCurMusicSoundName();
bool getSoundStatus(const char *soundName) const; bool getSoundStatus(const char *soundName);
int32 getPosIn60HzTicks(const char *soundName); int32 getPosIn60HzTicks(const char *soundName);
}; };

View file

@ -105,12 +105,14 @@ void Imuse::playMusic(const ImuseTable *table, int atribPos, bool sequence) {
} else { } else {
char *soundName = getCurMusicSoundName(); char *soundName = getCurMusicSoundName();
int pan; int pan;
if (table->pan == 0) if (table->pan == 0)
pan = 64; pan = 64;
else else
pan = table->pan; pan = table->pan;
if ((table->opcode == 3) && (!sequence) && (strcmp(soundName, table->filename) == 0) && if (soundName != NULL && (table->opcode == 3) && (!sequence)
(table->atribPos != 0) && (table->atribPos == _stateMusicTable[_curMusicState].atribPos)) { && (strcmp(soundName, table->filename) == 0) && (table->atribPos != 0)
&& table->atribPos == _stateMusicTable[_curMusicState].atribPos) {
setFadeVolume(soundName, table->volume, table->fadeOut60TicksDelay); setFadeVolume(soundName, table->volume, table->fadeOut60TicksDelay);
setFadePan(soundName, pan, table->fadeOut60TicksDelay); setFadePan(soundName, pan, table->fadeOut60TicksDelay);
setHookId(soundName, hookId); setHookId(soundName, hookId);

View file

@ -27,6 +27,8 @@
#include "imuse/imuse_sndmgr.h" #include "imuse/imuse_sndmgr.h"
void Imuse::flushTracks() { void Imuse::flushTracks() {
// flushTracks should not lock the stack since all the functions
// that call it already do (stopAllSounds, startSound)
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) { for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
Track *track = _track[l]; Track *track = _track[l];
if (track->used && track->readyToRemove) { if (track->used && track->readyToRemove) {
@ -41,6 +43,7 @@ void Imuse::flushTracks() {
_sound->closeSound(track->soundHandle); _sound->closeSound(track->soundHandle);
track->soundHandle = NULL; track->soundHandle = NULL;
track->used = false; track->used = false;
strcpy(track->soundName, "");
} }
} }
} }
@ -49,6 +52,8 @@ void Imuse::flushTracks() {
void Imuse::refreshScripts() { void Imuse::refreshScripts() {
bool found = false; bool found = false;
StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l]; Track *track = _track[l];
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
@ -74,18 +79,25 @@ void Imuse::startSfx(const char *soundName, int priority) {
} }
int32 Imuse::getPosIn60HzTicks(const char *soundName) { int32 Imuse::getPosIn60HzTicks(const char *soundName) {
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *getTrack = NULL;
Track *track = _track[l];
if (track->handle.isActive() && (strcmp(track->soundName, soundName) == 0)) { getTrack = findTrack(soundName);
int32 pos = (5 * (track->dataOffset + track->regionOffset)) / (track->iteration / 12); // Warn the user if the track was not found
return pos; if (getTrack == NULL) {
} if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
warning("Music track '%s' could not be found to get ticks!", soundName);
return false;
} }
if (getTrack->handle.isActive()) {
int32 pos = (5 * (getTrack->dataOffset + getTrack->regionOffset)) / (getTrack->iteration / 12);
return pos;
}
return -1; return -1;
} }
bool Imuse::isVoicePlaying() { bool Imuse::isVoicePlaying() {
StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l]; Track *track = _track[l];
if (track->volGroupId == IMUSE_VOLGRP_VOICE) { if (track->volGroupId == IMUSE_VOLGRP_VOICE) {
@ -98,30 +110,45 @@ bool Imuse::isVoicePlaying() {
return false; return false;
} }
bool Imuse::getSoundStatus(const char *soundName) const { bool Imuse::getSoundStatus(const char *soundName) {
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *statusTrack = NULL;
Track *track = _track[l];
if (strcmp(track->soundName, soundName) == 0) { // If there's no name then don't try to get the status!
if (track->handle.isActive()) { if (strlen(soundName) == 0)
return true; return false;
}
} statusTrack = findTrack(soundName);
// Warn the user if the track was not found
if (statusTrack == NULL) {
// This debug warning should be "light" since this function gets called
// on occassion to see if a sound has stopped yet
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
printf("Music track '%s' could not be found to get status, assume inactive.\n", soundName);
return false;
} }
return statusTrack->handle.isActive();
return false;
} }
void Imuse::stopSound(const char *soundName) { void Imuse::stopSound(const char *soundName) {
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *removeTrack = NULL;
Track *track = _track[l];
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) { removeTrack = findTrack(soundName);
track->toBeRemoved = true; // Warn the user if the track was not found
} if (removeTrack == NULL) {
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
warning("Music track '%s' could not be found to stop!", soundName);
return;
} }
removeTrack->toBeRemoved = true;
} }
void Imuse::stopAllSounds() { void Imuse::stopAllSounds() {
for(;;) { int i;
StackLock lock(_mutex);
// Make 5 attempts to close out all the sounds before failing
// At this time it is inappropriate to say we are stable enough
// to just let this run forever
for(i = 5; i > 0; i--) {
bool foundNotRemoved = false; bool foundNotRemoved = false;
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) { for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
Track *track = _track[l]; Track *track = _track[l];
@ -135,6 +162,10 @@ void Imuse::stopAllSounds() {
flushTracks(); flushTracks();
SDL_Delay(50); SDL_Delay(50);
} }
if (i == 0) {
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
warning("Imuse::stopAllSounds() did not stop everything!");
}
} }
void Imuse::pause(bool p) { void Imuse::pause(bool p) {

View file

@ -25,6 +25,7 @@
#include "mixer/mixer.h" #include "mixer/mixer.h"
#include "mixer/audiostream.h" #include "mixer/audiostream.h"
#include "imuse/limits.h"
class McmpMgr; class McmpMgr;
class Block; class Block;
@ -32,11 +33,18 @@ class Block;
class ImuseSndMgr { class ImuseSndMgr {
public: public:
#define MAX_IMUSE_SOUNDS 16 // MAX_IMUSE_SOUNDS should not be hardcoded, it should represent
// the maximum number of different tracks, see limits.h
#define MAX_IMUSE_SOUNDS MAX_IMUSE_TRACKS+MAX_IMUSE_FADETRACKS
#define IMUSE_VOLGRP_VOICE 1 // The numbering below fixes talking to Domino in his office
#define IMUSE_VOLGRP_SFX 2 // and it also allows Manny to get the info for Mercedes
#define IMUSE_VOLGRP_MUSIC 3 // Colomar, without this the game hangs at these points!
#define IMUSE_VOLGRP_BGND 0
#define IMUSE_VOLGRP_ACTION 1
#define IMUSE_VOLGRP_SFX 2
#define IMUSE_VOLGRP_MUSIC 3
#define IMUSE_VOLGRP_VOICE 4
private: private:
struct Region { struct Region {

View file

@ -65,7 +65,7 @@ ImuseTable grimStateMusicTable[] = {
{2, 1083, 0, 0, 60, 127, 0, "1083 - Beaver Room.IMC"}, {2, 1083, 0, 0, 60, 127, 0, "1083 - Beaver Room.IMC"},
{2, 1084, 0, 0, 60, 80, 0, "1084 - Foggy Cactus.IMC"}, {2, 1084, 0, 0, 60, 80, 0, "1084 - Foggy Cactus.IMC"},
{2, 1085, 0, 0, 60, 105, 0, "1085 - Rubamat Exterior.IMC"}, {2, 1085, 0, 0, 60, 105, 0, "1085 - Rubamat Exterior.IMC"},
{2, 1086, 0, 4, 60, 80, 30, "1086 - Blue Hector.IMC"}, {2, 1086, 0, 4, 60, 80, 30, "1087 - Blue Hector.IMC"},
{2, 1100, 0, 0, 60, 127, 0, "1109 - Cafe Exterior.IMC"}, {2, 1100, 0, 0, 60, 127, 0, "1109 - Cafe Exterior.IMC"},
{3, 1101, 45, 5, 24, 60, 0, "1101 - Cafe Office.IMC"}, {3, 1101, 45, 5, 24, 60, 0, "1101 - Cafe Office.IMC"},
{3, 1102, 45, 0, 24, 127, 0, "1102 - Cafe Intercom.IMC"}, {3, 1102, 45, 0, 24, 127, 0, "1102 - Cafe Intercom.IMC"},
@ -105,8 +105,8 @@ ImuseTable grimStateMusicTable[] = {
{2, 1147, 0, 0, 60, 127, 0, "1147 - LOL Security Int.IMC"}, {2, 1147, 0, 0, 60, 127, 0, "1147 - LOL Security Int.IMC"},
{2, 1148, 0, 0, 60, 127, 0, "1148 - Carla's Life.IMC"}, {2, 1148, 0, 0, 60, 127, 0, "1148 - Carla's Life.IMC"},
{2, 1149, 0, 0, 24, 127, 0, "1149 - Bomb.IMC"}, {2, 1149, 0, 0, 24, 127, 0, "1149 - Bomb.IMC"},
{3, 1150, 83, 0, 60, 105, 0, "1150 - Track Stairs.IMC"}, {3, 1150, 83, 0, 60, 105, 0, "1152 - Track Stairs.IMC"},
{3, 1151, 83, 0, 60, 105, 0, "1151 - Track Stairs.IMC"}, {3, 1151, 83, 0, 60, 105, 0, "1152 - Track Stairs.IMC"},
{3, 1152, 83, 0, 60, 105, 0, "1152 - Track Stairs.IMC"}, {3, 1152, 83, 0, 60, 105, 0, "1152 - Track Stairs.IMC"},
{2, 1153, 0, 0, 24, 127, 0, "1153 - Track Base.IMC"}, {2, 1153, 0, 0, 24, 127, 0, "1153 - Track Base.IMC"},
{2, 1154, 0, 0, 60, 127, 0, "1154 - Kitty Hall.IMC"}, {2, 1154, 0, 0, 60, 127, 0, "1154 - Kitty Hall.IMC"},

View file

@ -28,50 +28,70 @@
int Imuse::allocSlot(int priority) { int Imuse::allocSlot(int priority) {
int l, lowest_priority = 127; int l, lowest_priority = 127;
int trackId = -1; int trackId = -1;
// allocSlot called by startSound so no locking is necessary
for (l = 0; l < MAX_IMUSE_TRACKS; l++) { for (l = 0; l < MAX_IMUSE_TRACKS; l++) {
if (!_track[l]->used) { if (!_track[l]->used) {
trackId = l; return l; // Found an unused track
break;
} }
} }
if (trackId == -1) { warning("Imuse::startSound(): All slots are full");
warning("Imuse::startSound(): All slots are full"); for (l = 0; l < MAX_IMUSE_TRACKS; l++) {
for (l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *track = _track[l];
Track *track = _track[l]; if (track->used && !track->toBeRemoved && lowest_priority > track->priority) {
if (track->used && !track->toBeRemoved && lowest_priority > track->priority) { lowest_priority = track->priority;
lowest_priority = track->priority; trackId = l;
trackId = l;
}
}
if (lowest_priority <= priority) {
assert(trackId != -1);
_track[trackId]->toBeRemoved = true;
warning("Imuse::startSound(): Removed sound %s from track %d", _track[trackId]->soundName, trackId);
} else {
warning("Imuse::startSound(): Priority sound too low");
return -1;
} }
} }
if (lowest_priority <= priority) {
assert(trackId != -1);
_track[trackId]->toBeRemoved = true;
warning("Imuse::startSound(): Removed sound %s from track %d", _track[trackId]->soundName, trackId);
} else {
warning("Imuse::startSound(): Priority sound too low");
return -1;
}
return trackId; return trackId;
} }
bool Imuse::startSound(const char *soundName, int volGroupId, int hookId, int volume, int pan, int priority) { bool Imuse::startSound(const char *soundName, int volGroupId, int hookId, int volume, int pan, int priority) {
int l = allocSlot(priority); Track *track = NULL;
int i, l = -1;
StackLock lock(_mutex);
// If the track is already playing then there is absolutely no
// reason to start it again, the existing track should be modified
// instead of starting a new copy of the track
for (i = 0; i < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; i++) {
// Filenames are case insensitive, see findTrack
if (!strcasecmp(_track[i]->soundName, soundName)) {
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
printf("Imuse::startSound(): Track '%s' already playing.\n", soundName);
return true;
}
}
l = allocSlot(priority);
if (l == -1) { if (l == -1) {
warning("Imuse::startSound() Can't start sound - no free slots"); warning("Imuse::startSound(): Can't start sound - no free slots");
return false; return false;
} }
track = _track[l];
Track *track = _track[l]; i = 5;
while (track->used) { // At this time it is inappropriate to assume that this will always
// succeed, so set a limit of 5 tries on running flushTracks
while (track->used && i > 0) {
// The designated track is not yet available. So, we call flushTracks() // The designated track is not yet available. So, we call flushTracks()
// to get it processed (and thus made ready for us). Since the actual // to get it processed (and thus made ready for us). Since the actual
// processing is done by another thread, we also call parseEvents to // processing is done by another thread, we also call parseEvents to
// give it some time (and to avoid busy waiting/looping). // give it some time (and to avoid busy waiting/looping).
flushTracks(); flushTracks();
i--;
}
if (i == 0) {
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
warning("Imuse::startSound(): flushTracks was unnable to free up a track!");
} }
track->pan = pan * 1000; track->pan = pan * 1000;
@ -142,59 +162,82 @@ bool Imuse::startSound(const char *soundName, int volGroupId, int hookId, int vo
return true; return true;
} }
void Imuse::setPriority(const char *soundName, int priority) { Imuse::Track *Imuse::findTrack(const char *soundName) {
assert ((priority >= 0) && (priority <= 127)); StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l]; Track *track = _track[l];
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
track->priority = priority; // Since the audio (at least for Eva's keystrokes) can be referenced
// two ways: keyboard.IMU and keyboard.imu, make a case insensitive
// search for the track to make sure we can find it
if (strcasecmp(track->soundName, soundName) == 0) {
return track;
} }
} }
return NULL;
}
void Imuse::setPriority(const char *soundName, int priority) {
Track *changeTrack = NULL;
assert ((priority >= 0) && (priority <= 127));
changeTrack = findTrack(soundName);
// Check to make sure we found the track
if (changeTrack == NULL) {
warning("Unable to find track '%s' to change priority!", soundName);
return;
}
changeTrack->priority = priority;
} }
void Imuse::setVolume(const char *soundName, int volume) { void Imuse::setVolume(const char *soundName, int volume) {
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *changeTrack;
Track *track = _track[l];
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) { changeTrack = findTrack(soundName);
track->vol = volume * 1000; if (changeTrack == NULL) {
} warning("Unable to find track '%s' to change volume!", soundName);
return;
} }
changeTrack->vol = volume * 1000;
} }
void Imuse::setPan(const char *soundName, int pan) { void Imuse::setPan(const char *soundName, int pan) {
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *changeTrack;
Track *track = _track[l];
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) { changeTrack = findTrack(soundName);
track->pan = pan; if (changeTrack == NULL) {
} warning("Unable to find track '%s' to change volume!", soundName);
return;
} }
changeTrack->pan = pan;
} }
int Imuse::getVolume(const char *soundName) { int Imuse::getVolume(const char *soundName) {
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *getTrack;
Track *track = _track[l];
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) { getTrack = findTrack(soundName);
return track->vol / 1000; if (getTrack == NULL) {
} warning("Unable to find track '%s' to get volume!", soundName);
return 0;
} }
return getTrack->vol / 1000;
return 0;
} }
void Imuse::setHookId(const char *soundName, int hookId) { void Imuse::setHookId(const char *soundName, int hookId) {
StackLock lock(_mutex); Track *changeTrack;
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { changeTrack = findTrack(soundName);
Track *track = _track[l]; if (changeTrack == NULL) {
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) { warning("Unable to find track '%s' to change hook id!", soundName);
track->curHookId = hookId; return;
}
} }
changeTrack->curHookId = hookId;
} }
int Imuse::getCountPlayedTracks(const char *soundName) { int Imuse::getCountPlayedTracks(const char *soundName) {
int count = 0; int count = 0;
StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l]; Track *track = _track[l];
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) { if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) {
@ -206,59 +249,61 @@ int Imuse::getCountPlayedTracks(const char *soundName) {
} }
void Imuse::selectVolumeGroup(const char *soundName, int volGroupId) { void Imuse::selectVolumeGroup(const char *soundName, int volGroupId) {
Track *changeTrack;
assert((volGroupId >= 1) && (volGroupId <= 4)); assert((volGroupId >= 1) && (volGroupId <= 4));
if (volGroupId == 4) if (volGroupId == 4)
volGroupId = 3; volGroupId = 3;
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { changeTrack = findTrack(soundName);
Track *track = _track[l]; if (changeTrack == NULL) {
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) { warning("Unable to find track '%s' to change volume group id!", soundName);
track->volGroupId = volGroupId; return;
}
} }
changeTrack->volGroupId = volGroupId;
} }
void Imuse::setFadeVolume(const char *soundName, int destVolume, int duration) { void Imuse::setFadeVolume(const char *soundName, int destVolume, int duration) {
StackLock lock(_mutex); Track *changeTrack;
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { changeTrack = findTrack(soundName);
Track *track = _track[l]; if (changeTrack == NULL) {
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) { warning("Unable to find track '%s' to change fade volume!", soundName);
track->volFadeDelay = duration; return;
track->volFadeDest = destVolume * 1000;
track->volFadeStep = (track->volFadeDest - track->vol) * 60 * (1000 / _callbackFps) / (1000 * duration);
track->volFadeUsed = true;
}
} }
changeTrack->volFadeDelay = duration;
changeTrack->volFadeDest = destVolume * 1000;
changeTrack->volFadeStep = (changeTrack->volFadeDest - changeTrack->vol) * 60 * (1000 / _callbackFps) / (1000 * duration);
changeTrack->volFadeUsed = true;
} }
void Imuse::setFadePan(const char *soundName, int destPan, int duration) { void Imuse::setFadePan(const char *soundName, int destPan, int duration) {
StackLock lock(_mutex); Track *changeTrack;
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { changeTrack = findTrack(soundName);
Track *track = _track[l]; if (changeTrack == NULL) {
if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) { warning("Unable to find track '%s' to change fade pan!", soundName);
track->panFadeDelay = duration; return;
track->panFadeDest = destPan * 1000;
track->panFadeStep = (track->panFadeDest - track->pan) * 60 * (1000 / _callbackFps) / (1000 * duration);
track->panFadeUsed = true;
}
} }
changeTrack->panFadeDelay = duration;
changeTrack->panFadeDest = destPan * 1000;
changeTrack->panFadeStep = (changeTrack->panFadeDest - changeTrack->pan) * 60 * (1000 / _callbackFps) / (1000 * duration);
changeTrack->panFadeUsed = true;
} }
char *Imuse::getCurMusicSoundName() { char *Imuse::getCurMusicSoundName() {
StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l]; Track *track = _track[l];
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
return track->soundName; return track->soundName;
} }
} }
return NULL;
return "";
} }
void Imuse::fadeOutMusic(int duration) { void Imuse::fadeOutMusic(int duration) {
StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l]; Track *track = _track[l];
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {

25
imuse/limits.h Normal file
View file

@ -0,0 +1,25 @@
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003-2005 The ScummVM-Residual Team (www.scummvm.org)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#ifndef IMUSE_LIMITS_H
#define IMUSE_LIMITS_H
//#define MAX_IMUSE_TRACKS 8
#define MAX_IMUSE_TRACKS 20
#define MAX_IMUSE_FADETRACKS 8
#endif

View file

@ -35,11 +35,28 @@ KeyframeAnim::KeyframeAnim(const char *filename, const char *data, int len) :
} }
void KeyframeAnim::loadBinary(const char *data, int len) { void KeyframeAnim::loadBinary(const char *data, int len) {
// First four bytes are the FYEK Keyframe identifier code
// Next 36 bytes are the filename
if (debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL) {
char filebuf[37];
memcpy(filebuf, data + 4, 36);
filebuf[37] = 0;
printf("Loading Keyframe '%s'.\n", filebuf);
}
// Next four bytes are the flags
_flags = READ_LE_UINT32(data + 40); _flags = READ_LE_UINT32(data + 40);
// Next four bytes are a duplicate of _numJoints (?)
// Next four bytes are the type
_type = READ_LE_UINT32(data + 48); _type = READ_LE_UINT32(data + 48);
// Next four bytes are the frames per second
_fps = get_float(data + 52); _fps = get_float(data + 52);
// Next four bytes are the number of frames
_numFrames = READ_LE_UINT32(data + 56); _numFrames = READ_LE_UINT32(data + 56);
// Next four bytes are the number of joints
_numJoints = READ_LE_UINT32(data + 60); _numJoints = READ_LE_UINT32(data + 60);
// Next four bytes are unknown (?)
// Next four bytes are the number of markers
_numMarkers = READ_LE_UINT32(data + 68); _numMarkers = READ_LE_UINT32(data + 68);
_markers = new Marker[_numMarkers]; _markers = new Marker[_numMarkers];
for (int i = 0; i < _numMarkers; i++) { for (int i = 0; i < _numMarkers; i++) {
@ -51,16 +68,22 @@ void KeyframeAnim::loadBinary(const char *data, int len) {
for (int i = 0; i < _numJoints; i++) for (int i = 0; i < _numJoints; i++)
_nodes[i] = NULL; _nodes[i] = NULL;
const char *dataEnd = data + len; const char *dataEnd = data + len;
data += 180; // The first 136 bytes are for the header, this was originally
// listed as 180 bytes since the first operation is usually a
// "null" key, however ma_card_hold.key showed that this is
// not always the case so we should not skip this operation
data += 136;
while (data < dataEnd) { while (data < dataEnd) {
// ma_card_hold.key crashes at this part without checking int nodeNum;
// to make sure nodeNum is valid, unfortunately I believe // The first 32 bytes (of a keyframe) are the name handle
// whatever data we're losing from this file is what prevents // The next four bytes are the node number identifier
// the game from continuing after Manny reads the message nodeNum = READ_LE_UINT32(data + 32);
//
// TODO: Find out what really goes wrong when we read // Because of the issue above ma_card_hold.key used to crash
// the data in ma_card_hold.key and fix it // at this part without checking to make sure nodeNum is a
int nodeNum = READ_LE_UINT32(data + 32); // valid number, we'll leave this in just in case something
// else is still wrong but it should now load correctly in
// all cases
if (nodeNum >= _numJoints) { if (nodeNum >= _numJoints) {
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) { if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) {
warning("A node number was greater than the maximum number of nodes (%d/%d)", nodeNum, _numJoints); warning("A node number was greater than the maximum number of nodes (%d/%d)", nodeNum, _numJoints);
@ -117,10 +140,10 @@ void KeyframeAnim::animate(Model::HierNode *nodes, float time, int priority1, in
if (frame > _numFrames) if (frame > _numFrames)
frame = _numFrames; frame = _numFrames;
for (int i = 0; i < _numJoints; i++) for (int i = 0; i < _numJoints; i++) {
if (_nodes[i] != NULL) if (_nodes[i] != NULL)
_nodes[i]->animate(nodes[i], frame, ((_type & nodes[i]._type) != 0 ? priority2 : priority1));
_nodes[i]->animate(nodes[i], frame, ((_type & nodes[i]._type) != 0 ? priority2 : priority1)); }
} }
void KeyframeAnim::KeyframeEntry::loadBinary(const char *&data) { void KeyframeAnim::KeyframeEntry::loadBinary(const char *&data) {
@ -138,13 +161,18 @@ void KeyframeAnim::KeyframeEntry::loadBinary(const char *&data) {
} }
void KeyframeAnim::KeyframeNode::loadBinary(const char *&data) { void KeyframeAnim::KeyframeNode::loadBinary(const char *&data) {
std::memcpy(_meshName, data, 32); // If the name handle is entirely null (like ma_rest.key)
// then we shouldn't try to set the name
if (READ_LE_UINT32(data) == 0)
std::memcpy(_meshName, "(null)", 32);
else
std::memcpy(_meshName, data, 32);
_numEntries = READ_LE_UINT32(data + 36); _numEntries = READ_LE_UINT32(data + 36);
data += 44; data += 44;
_entries = new KeyframeEntry[_numEntries]; _entries = new KeyframeEntry[_numEntries];
for (int i = 0; i < _numEntries; i++) for (int i = 0; i < _numEntries; i++)
_entries[i].loadBinary(data); _entries[i].loadBinary(data);
} }
void KeyframeAnim::KeyframeNode::loadText(TextSplitter &ts) { void KeyframeAnim::KeyframeNode::loadText(TextSplitter &ts) {
ts.scanString("mesh name %s", 1, _meshName); ts.scanString("mesh name %s", 1, _meshName);

295
lua.cpp
View file

@ -139,9 +139,26 @@ static inline Bitmap *check_bitmapobject(int num) {
return NULL; return NULL;
} }
static inline int check_int(int num) { static inline double check_double(int num) {
double val = luaL_check_number(num); double val;
// Have found some instances, such as in Rubacava if you jump there,
// where doubles of "zero" are called as nil
if(lua_isnil(lua_getparam(num)))
return 0.0;
return luaL_check_number(num);
}
static inline int check_int(int num) {
double val;
// Have found some instances, such as in Rubacava and the tube-switcher
// room, where integers of "zero" are called as nil
if(lua_isnil(lua_getparam(num)))
return 0;
val = luaL_check_number(num);
return int(round(val)); return int(round(val));
} }
@ -200,6 +217,7 @@ static void PrintDebug() {
msg.insert(0, "Debug: "); msg.insert(0, "Debug: ");
std::fputs(msg.c_str(), stderr); std::fputs(msg.c_str(), stderr);
msg.append("\n");
} }
} }
@ -506,21 +524,13 @@ static void GetActorPos() {
static void SetActorRot() { static void SetActorRot() {
float pitch, yaw, roll; float pitch, yaw, roll;
lua_Object param3;
Actor *act; Actor *act;
DEBUG_FUNCTION(); DEBUG_FUNCTION();
param3 = lua_getparam(3);
act = check_actor(1); act = check_actor(1);
pitch = luaL_check_number(2); pitch = check_double(2);
// param3 can be nil, the tube-switcher scene appears yaw = check_double(3);
// to call SetActorRot will nil for param3 intentionally roll = check_double(4);
if (lua_isnil(param3))
yaw = 0;
else
yaw = luaL_check_number(3);
roll = luaL_check_number(4);
if (getbool(5)) if (getbool(5))
act->turnTo(pitch, yaw, roll); act->turnTo(pitch, yaw, roll);
else else
@ -582,14 +592,18 @@ static void GetActorYawToPoint() {
} }
static void PutActorInSet() { static void PutActorInSet() {
const char *set = "";
Actor *act; Actor *act;
DEBUG_FUNCTION(); DEBUG_FUNCTION();
act = check_actor(1); act = check_actor(1);
if (!lua_isnil(lua_getparam(2))) if (!lua_isnil(lua_getparam(2))) {
const char *set;
set = luaL_check_string(2); set = luaL_check_string(2);
act->putInSet(set); // Make sure the actor isn't already in the set
if (!act->inSet(set))
act->putInSet(set);
}
} }
static void SetActorWalkRate() { static void SetActorWalkRate() {
@ -742,6 +756,64 @@ stubWarning("TurnActorTo");
static void PointActorAt() { static void PointActorAt() {
stubWarning("PointActorAt"); stubWarning("PointActorAt");
} }
/* Get the location of one of the actor's nodes, this is
* used by Glottis to watch where Manny is located in
* order to hand him the work order. This function is
* also important for when Velasco hands Manny the logbook
* in Rubacava
*/
static void GetActorNodeLocation() {
Model::HierNode *allNodes;
Costume *c;
Actor *act;
int node;
// Should this actually do anything?
DEBUG_FUNCTION();
act = check_actor(1);
node = check_int(2);
c = act->currentCostume();
if (c == NULL) {
lua_pushnil();
lua_pushnil();
lua_pushnil();
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
warning("GetActorNodeLocation() when actor has no costume (which means no nodes)!");
return;
}
allNodes = c->getModelNodes();
if (allNodes == NULL) {
lua_pushnil();
lua_pushnil();
lua_pushnil();
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
warning("GetActorNodeLocation() when actor has no nodes!");
}
lua_pushnumber(allNodes[node]._pos.x());
lua_pushnumber(allNodes[node]._pos.y());
lua_pushnumber(allNodes[node]._pos.z());
}
static void SetActorWalkDominate() {
lua_Object param2;
Actor *act;
// Should this actually do anything?
DEBUG_FUNCTION();
act = check_actor(1);
if (act == NULL) {
lua_pushnil();
return;
}
param2 = lua_getparam(2);
if (lua_isnil(param2))
lua_pushnil();
else if (lua_isnumber(param2))
lua_pushnumber(lua_getnumber(param2));
else
warning("Unknown SetActorWalkDominate parameter!");
}
static void SetActorColormap() { static void SetActorColormap() {
char *mapname; char *mapname;
CMap *_cmap; CMap *_cmap;
@ -794,10 +866,13 @@ static void GetActorCostume() {
DEBUG_FUNCTION(); DEBUG_FUNCTION();
act = check_actor(1); act = check_actor(1);
c = act->currentCostume(); c = act->currentCostume();
if (c == NULL) if (c == NULL) {
lua_pushnil(); lua_pushnil();
else if (debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
lua_pushstring(const_cast<char *>(c->filename())); printf("GetActorCostume() on '%s' when actor has no costume!", act->name());
return;
}
lua_pushstring(const_cast<char *>(c->filename()));
} }
static void PopActorCostume() { static void PopActorCostume() {
@ -957,9 +1032,6 @@ static void ActorLookAt() {
if (lua_isnumber(y)) if (lua_isnumber(y))
act->setLookAtRate(luaL_check_number(3)); act->setLookAtRate(luaL_check_number(3));
act->setLooking(true);
return;
} else if ( lua_isnumber(x)) { // look at xyz } else if ( lua_isnumber(x)) { // look at xyz
Vector3d vector; Vector3d vector;
float fX; float fX;
@ -988,13 +1060,13 @@ static void ActorLookAt() {
if (lua_isnumber(y)) if (lua_isnumber(y))
act->setLookAtRate(luaL_check_number(3)); act->setLookAtRate(luaL_check_number(3));
} else if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) } else {
warning("ActorLookAt: Don't know what to look at!"); if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
warning("ActorLookAt: Don't know what to look at!");
}
act->setLooking(true); act->setLooking(true);
// Fixes random bug when changing scenes (?) after we've done a jump lua_pushnumber(1);
// (this function is close to the failure point)
lua_pushnil();
} }
static void SetActorLookRate() { static void SetActorLookRate() {
@ -1274,16 +1346,15 @@ static void IsActorInSector(void) {
numSectors = g_engine->currScene()->getSectorCount(); numSectors = g_engine->currScene()->getSectorCount();
for (i = 0; i < numSectors; i++) { for (i = 0; i < numSectors; i++) {
Sector *sector = g_engine->currScene()->getSectorBase(i); Sector *sector = g_engine->currScene()->getSectorBase(i);
if (sector->visible() && strmatch(sector->name(), name)) { if (sector->visible() && strmatch(sector->name(), name)) {
if (sector->isPointInSector(act->pos())) { if (sector->isPointInSector(act->pos())) {
lua_pushnumber(sector->id()); lua_pushnumber(sector->id());
lua_pushstring((char *)sector->name()); lua_pushstring((char *)sector->name());
lua_pushnumber(sector->type()); lua_pushnumber(sector->type());
return;
} }
} }
} }
lua_pushnil(); lua_pushnil();
} }
@ -1431,9 +1502,13 @@ static void ImStartSound() {
soundName = luaL_check_string(1); soundName = luaL_check_string(1);
priority = check_int(2); priority = check_int(2);
group = check_int(3); group = check_int(3);
// Start the sound with the appropriate settings
if (g_imuse->startSound(soundName, group, 0, 127, 0, priority)) { if (g_imuse->startSound(soundName, group, 0, 127, 0, priority)) {
lua_pushstring(soundName); lua_pushstring(soundName);
} else { } else {
if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ERROR || debugLevel == DEBUG_ALL)
error("ImStartSound failed to start '%s'", soundName);
lua_pushnil(); lua_pushnil();
} }
} }
@ -1859,6 +1934,7 @@ void getTextObjectParams(TextObject *textObject, lua_Object table_obj) {
/* Clean the buffer of text objects and primitives /* Clean the buffer of text objects and primitives
* this is known to be used when changing between menus * this is known to be used when changing between menus
* and when loading some cutscenes
*/ */
static void CleanBuffer() { static void CleanBuffer() {
DEBUG_FUNCTION(); DEBUG_FUNCTION();
@ -1897,6 +1973,18 @@ char *itemText(lua_Object itemTable, int menuItem) {
return NULL; return NULL;
} }
/* This function sends the SDL signal to
* go ahead and exit the game
*/
static void Exit() {
SDL_Event event;
DEBUG_FUNCTION();
event.type = SDL_QUIT;
if (SDL_PushEvent(&event) != 0)
error("Unable to push exit event!");
}
/* This function provides all of the menu /* This function provides all of the menu
* handling that has been observed * handling that has been observed
*/ */
@ -1920,35 +2008,6 @@ static void menuHandler() {
// get the keycode // get the keycode
key = atoi(lua_getstring(keycode)); key = atoi(lua_getstring(keycode));
// get the item list for most menus
itemTable = getTableValue(menuTable, "menu");
if (!lua_istable(itemTable)) {
// 3D Acceleration Menu
itemTable = getTableValue(menuTable, "active_menu");
if (!lua_istable(itemTable)) {
// Control Help menu
lua_Object nextPage = getTableValue(menuTable, "next_page");
lua_Object prevPage = getTableValue(menuTable, "prev_page");
if (lua_isfunction(nextPage) && key == SDLK_RIGHT) {
lua_beginblock();
lua_pushobject(menuTable);
lua_callfunction(nextPage);
lua_endblock();
} else if (lua_isfunction(prevPage) && key == SDLK_LEFT) {
lua_beginblock();
lua_pushobject(menuTable);
lua_callfunction(prevPage);
lua_endblock();
}
return;
}
}
// get the current item
menuItem = atoi(lua_getstring(getTableValue(itemTable, "cur_item")));
menuItems = atoi(lua_getstring(getTableValue(itemTable, "num_items")));
// handle hotkeys // handle hotkeys
if (key >= SDLK_a && key <= SDLK_z) { if (key >= SDLK_a && key <= SDLK_z) {
char keychar[2]; char keychar[2];
@ -1965,11 +2024,71 @@ static void menuHandler() {
key = 0; key = 0;
return; // stop processing (we don't need to handle normal keystrokes) return; // stop processing (we don't need to handle normal keystrokes)
} }
// Handle the correct type of menu
if (!lua_isnil(getTableValue(menuTable, "menu"))) {
// Get the item list for most menus
itemTable = getTableValue(menuTable, "menu");
} else if (!lua_isnil(getTableValue(menuTable, "active_menu"))) {
// 3D Acceleration Menu
itemTable = getTableValue(menuTable, "active_menu");
} else if (!lua_isnil(getTableValue(menuTable, "num_choices"))) {
// Exit Menu
int current_choice = atoi(lua_getstring(getTableValue(menuTable, "current_choice")));
int num_choices = atoi(lua_getstring(getTableValue(menuTable, "num_choices")));
if (key == SDLK_RIGHT)
current_choice++;
else if (key == SDLK_LEFT)
current_choice--;
if (current_choice < 0)
current_choice = 0;
else if (current_choice >= num_choices)
current_choice = num_choices-1;
setTableValue(menuTable, "current_choice", current_choice);
// TODO: Need to figure out how to update the screen under this
// type of menu operation!
return;
} else if (lua_isfunction(getTableValue(menuTable, "next_page"))
|| lua_isfunction(getTableValue(menuTable, "prev_page"))) {
// Control Help menu
lua_Object nextPage = getTableValue(menuTable, "next_page");
lua_Object prevPage = getTableValue(menuTable, "prev_page");
if (lua_isfunction(nextPage) && key == SDLK_RIGHT) {
lua_beginblock();
lua_pushobject(menuTable);
lua_callfunction(nextPage);
lua_endblock();
} else if (lua_isfunction(prevPage) && key == SDLK_LEFT) {
lua_beginblock();
lua_pushobject(menuTable);
lua_callfunction(prevPage);
lua_endblock();
}
return;
} else {
warning("Unhandled type of menu!");
return;
}
// get the current item
menuItem = atoi(lua_getstring(getTableValue(itemTable, "cur_item")));
menuItems = atoi(lua_getstring(getTableValue(itemTable, "num_items")));
// hack the "&Return to Game" command so it actually works // hack the "&Return to Game" command so it actually works
if (key == SDLK_RETURN && strmatch(itemText(itemTable, menuItem), "/sytx247/")) if (key == SDLK_RETURN && strmatch(itemText(itemTable, menuItem), "/sytx247/"))
key = SDLK_ESCAPE; key = SDLK_ESCAPE;
// hack the "&Quit" command so it actually works
// (at this time it cannot open the exit menu on top of the normal one)
if (key == SDLK_RETURN && strmatch(itemText(itemTable, menuItem), "/sytx248/")) {
Exit();
return;
}
/* if we're running the menu then we need to manually handle /* if we're running the menu then we need to manually handle
* a lot of the operations necessary to use the menu * a lot of the operations necessary to use the menu
*/ */
@ -1982,17 +2101,12 @@ static void menuHandler() {
return; return;
lua_Object close = getTableFunction(menuTable, "cancel"); lua_Object close = getTableFunction(menuTable, "cancel");
lua_Object destroy = getTableFunction(menuTable, "destroy");
lua_beginblock(); lua_beginblock();
lua_pushobject(menuTable); lua_pushobject(menuTable);
lua_pushnil(); lua_pushnil();
lua_callfunction(close); lua_callfunction(close);
lua_endblock(); lua_endblock();
lua_beginblock();
lua_pushnumber(menuItem);
lua_callfunction(destroy);
lua_endblock();
break; break;
} }
case SDLK_DOWN: case SDLK_DOWN:
@ -2031,16 +2145,7 @@ static void menuHandler() {
lua_endblock(); lua_endblock();
} }
} }
/* Clean the requested menu
*/
static void destroyMenu() {
DEBUG_FUNCTION();
CleanBuffer();
lua_Object system_table = lua_getglobal("system");
setTableValue(system_table, "menuHandler", (lua_Object) 0);
}
/* Check for an existing object by a certain name /* Check for an existing object by a certain name
* this function is used by several functions that look * this function is used by several functions that look
* for text objects to see if they need to be created/modified/destroyed. * for text objects to see if they need to be created/modified/destroyed.
@ -2252,14 +2357,18 @@ static void StartMovie() {
pushbool(g_smush->play(luaL_check_string(1), x, y)); pushbool(g_smush->play(luaL_check_string(1), x, y));
} }
/* Fullscreen movie playing query and normal movie
* query should actually detect correctly and not
* just return true whenever ANY movie is playing
*/
static void IsFullscreenMoviePlaying() { static void IsFullscreenMoviePlaying() {
DEBUG_FUNCTION(); DEBUG_FUNCTION();
pushbool(g_smush->isPlaying()); pushbool(g_smush->isPlaying() && g_engine->getMode() == ENGINE_MODE_SMUSH);
} }
static void IsMoviePlaying() { static void IsMoviePlaying() {
DEBUG_FUNCTION(); DEBUG_FUNCTION();
pushbool(g_smush->isPlaying()); pushbool(g_smush->isPlaying() && g_engine->getMode() == ENGINE_MODE_NORMAL);
} }
static void StopMovie() { static void StopMovie() {
@ -2411,11 +2520,6 @@ static void GetDiskFreeSpace() {
// Objectstate functions // Objectstate functions
static void NewObjectState() { static void NewObjectState() {
enum ObjectPosition {
OBJSTATE_UNDERLAY = 1,
OBJSTATE_OVERLAY = 2,
OBJSTATE_STATE = 3
};
ObjectState *state = NULL; ObjectState *state = NULL;
ObjectState::Position pos; ObjectState::Position pos;
char *bitmap, *zbitmap; char *bitmap, *zbitmap;
@ -2423,6 +2527,7 @@ static void NewObjectState() {
int setupID; int setupID;
DEBUG_FUNCTION(); DEBUG_FUNCTION();
// Called with "nil" if you jump to zone "sp"
setupID = check_int(1); // Setup ID setupID = check_int(1); // Setup ID
pos = check_objstate_pos(2); // When to draw pos = check_objstate_pos(2); // When to draw
bitmap = luaL_check_string(3); // Bitmap bitmap = luaL_check_string(3); // Bitmap
@ -2683,18 +2788,30 @@ static void RenderModeUser() {
} }
} }
/* For now this should just destroy any text
* objects that have been created and unlink
* the menu (this may be more appropriate under
* RenderModeUser, determine later)
*/
static void SetGamma() {
lua_Object system_table = lua_getglobal("system");
// TODO: Make this un-dim the screen
stubWarning("SetGamma");
CleanBuffer();
// Un-Install Menu Key Handler
lua_pushobject(system_table);
lua_pushstring(const_cast<char *>("menuHandler"));
lua_pushnil();
lua_settable();
}
static void Display() { static void Display() {
lua_Object system_table; lua_Object system_table;
DEBUG_FUNCTION(); DEBUG_FUNCTION();
system_table = lua_getglobal("system"); system_table = lua_getglobal("system");
// Install Menu Destroy Handler
lua_pushobject(system_table);
lua_pushstring(const_cast<char *>("userPaintHandler"));
lua_pushobject(lua_gettable());
lua_pushstring(const_cast<char *>("destroy"));
lua_pushcfunction(destroyMenu);
lua_settable();
// Install Menu Key Handler // Install Menu Key Handler
lua_pushobject(system_table); lua_pushobject(system_table);
lua_pushstring(const_cast<char *>("menuHandler")); lua_pushstring(const_cast<char *>("menuHandler"));
@ -2838,7 +2955,6 @@ STUB_FUNC(ActivateActorShadow)
STUB_FUNC(SetShadowColor) STUB_FUNC(SetShadowColor)
STUB_FUNC(DimRegion) STUB_FUNC(DimRegion)
STUB_FUNC(ForceRefresh) STUB_FUNC(ForceRefresh)
STUB_FUNC(SetGamma)
STUB_FUNC(LightMgrStartup) STUB_FUNC(LightMgrStartup)
STUB_FUNC(SetLightIntensity) STUB_FUNC(SetLightIntensity)
STUB_FUNC(SetLightPosition) STUB_FUNC(SetLightPosition)
@ -2862,12 +2978,10 @@ STUB_FUNC(SetActorPitch)
STUB_FUNC(GetPointSector) STUB_FUNC(GetPointSector)
STUB_FUNC(IsPointInSector) STUB_FUNC(IsPointInSector)
STUB_FUNC(SetActorFrustrumCull) STUB_FUNC(SetActorFrustrumCull)
STUB_FUNC(SetActorWalkDominate)
STUB_FUNC(GetCameraActor) STUB_FUNC(GetCameraActor)
STUB_FUNC(DriveActorTo) STUB_FUNC(DriveActorTo)
STUB_FUNC(WalkActorVector) STUB_FUNC(WalkActorVector)
STUB_FUNC(GetActorRect) STUB_FUNC(GetActorRect)
STUB_FUNC(GetActorNodeLocation)
STUB_FUNC(SetActorTimeScale) STUB_FUNC(SetActorTimeScale)
STUB_FUNC(SetActorScale) STUB_FUNC(SetActorScale)
STUB_FUNC(GetTranslationMode) STUB_FUNC(GetTranslationMode)
@ -2876,7 +2990,6 @@ STUB_FUNC(PrintLine)
STUB_FUNC(KillPrimitive) STUB_FUNC(KillPrimitive)
STUB_FUNC(WalkActorToAvoiding) STUB_FUNC(WalkActorToAvoiding)
STUB_FUNC(GetActorChores) STUB_FUNC(GetActorChores)
STUB_FUNC(Exit)
STUB_FUNC(SetCameraPosition) STUB_FUNC(SetCameraPosition)
STUB_FUNC(GetCameraFOV) STUB_FUNC(GetCameraFOV)
STUB_FUNC(SetCameraFOV) STUB_FUNC(SetCameraFOV)

View file

@ -263,10 +263,12 @@ void Model::loadText(TextSplitter &ts, const CMap &cmap) {
_materials = new ResPtr<Material>[_numMaterials]; _materials = new ResPtr<Material>[_numMaterials];
_materialNames = new char[_numMaterials][32]; _materialNames = new char[_numMaterials][32];
for (int i = 0; i < _numMaterials; i++) { for (int i = 0; i < _numMaterials; i++) {
char materialName[32];
int num; int num;
ts.scanString("%d: %32s", 2, &num, _materialNames[num]); ts.scanString("%d: %32s", 2, &num, materialName);
_materials[num] = g_resourceloader->loadMaterial(_materialNames[num], cmap); _materials[num] = g_resourceloader->loadMaterial(materialName, cmap);
strcpy(_materialNames[num], materialName);
} }
ts.expectString("section: geometrydef"); ts.expectString("section: geometrydef");
@ -401,16 +403,17 @@ void Model::Mesh::loadText(TextSplitter &ts, ResPtr<Material> *materials) {
_faces = new Face[_numFaces]; _faces = new Face[_numFaces];
_materialid = new int[_numFaces]; _materialid = new int[_numFaces];
for (int i = 0; i < _numFaces; i++) { for (int i = 0; i < _numFaces; i++) {
int num, type, geo, light, tex, verts; int num, materialid, type, geo, light, tex, verts;
float extralight; float extralight;
int readlen; int readlen;
if (ts.eof()) if (ts.eof())
error("Expected face data, got EOF\n"); error("Expected face data, got EOF\n");
if (std::sscanf(ts.currentLine(), " %d: %d %i %d %d %d %f %d%n", &num, &_materialid[num], &type, &geo, &light, &tex, &extralight, &verts, &readlen) < 8) if (std::sscanf(ts.currentLine(), " %d: %d %i %d %d %d %f %d%n", &num, &materialid, &type, &geo, &light, &tex, &extralight, &verts, &readlen) < 8)
error("Expected face data, got `%s'\n", ts.currentLine()); error("Expected face data, got `%s'\n", ts.currentLine());
_materialid[num] = materialid;
_faces[num]._material = materials[_materialid[num]]; _faces[num]._material = materials[_materialid[num]];
_faces[num]._type = type; _faces[num]._type = type;
_faces[num]._geo = geo; _faces[num]._geo = geo;

View file

@ -28,9 +28,12 @@
class ObjectState { class ObjectState {
public: public:
enum Position { enum Position {
OBJSTATE_BACKGROUND = 0,
OBJSTATE_UNDERLAY = 1, OBJSTATE_UNDERLAY = 1,
OBJSTATE_OVERLAY = 2, OBJSTATE_OVERLAY = 2,
OBJSTATE_STATE = 3 OBJSTATE_STATE = 3
// TODO: Find out what ObjectState 6 is supposed to be
// OBJSTATE_UNKNOWN = 6
}; };
ObjectState(int setupID, ObjectState::Position pos, const char *bitmap, const char *zbitmap, bool visible); ObjectState(int setupID, ObjectState::Position pos, const char *bitmap, const char *zbitmap, bool visible);

View file

@ -285,6 +285,18 @@ Model *ResourceLoader::loadModel(const char *filename, const CMap &c) {
return result; return result;
} }
bool ResourceLoader::exportResource(const char *filename) {
FILE *myFile = fopen(filename, "w");
Block *b = getFileBlock(filename);
if (b == NULL)
return false;
fwrite(b->data(), b->len(), 1, myFile);
fclose(myFile);
delete b;
return true;
}
void ResourceLoader::uncache(const char *filename) { void ResourceLoader::uncache(const char *filename) {
std::string fname = filename; std::string fname = filename;
makeLower(fname); makeLower(fname);

View file

@ -91,6 +91,7 @@ public:
std::FILE *openNewStream(const char *filename) const; std::FILE *openNewStream(const char *filename) const;
int fileLength(const char *filename) const; int fileLength(const char *filename) const;
bool exportResource(const char *filename);
Bitmap *loadBitmap(const char *fname); Bitmap *loadBitmap(const char *fname);
CMap *loadColormap(const char *fname); CMap *loadColormap(const char *fname);
Costume *loadCostume(const char *fname, Costume *prevCost); Costume *loadCostume(const char *fname, Costume *prevCost);

View file

@ -111,17 +111,25 @@ void Scene::Setup::load(TextSplitter &ts) {
ts.scanString(" background %256s", 1, buf); ts.scanString(" background %256s", 1, buf);
_bkgndBm = g_resourceloader->loadBitmap(buf); _bkgndBm = g_resourceloader->loadBitmap(buf);
if(debugLevel == DEBUG_BITMAPS || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL) if (_bkgndBm == NULL) {
printf("Loading scene bitmap: %s\n", buf); if (debugLevel == DEBUG_BITMAPS || debugLevel == DEBUG_ERROR || debugLevel == DEBUG_ALL)
printf("Unable to load scene bitmap: %s\n", buf);
} else {
if (debugLevel == DEBUG_BITMAPS || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
printf("Loaded scene bitmap: %s\n", buf);
}
// ZBuffer is optional // ZBuffer is optional
if (!ts.checkString("zbuffer")) { if (!ts.checkString("zbuffer")) {
_bkgndZBm = NULL; _bkgndZBm = NULL;
} else { } else {
ts.scanString(" zbuffer %256s", 1, buf); ts.scanString(" zbuffer %256s", 1, buf);
_bkgndZBm = g_resourceloader->loadBitmap(buf); // Don't even try to load if it's the "none" bitmap
if(debugLevel == DEBUG_BITMAPS || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL) if (strcmp(buf, "<none>.lbm") != 0) {
printf("Loading scene z-buffer bitmap: %s\n", buf); _bkgndZBm = g_resourceloader->loadBitmap(buf);
if (debugLevel == DEBUG_BITMAPS || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
printf("Loading scene z-buffer bitmap: %s\n", buf);
}
} }
ts.scanString(" position %f %f %f", 3, &_pos.x(), &_pos.y(), &_pos.z()); ts.scanString(" position %f %f %f", 3, &_pos.x(), &_pos.y(), &_pos.z());
@ -186,6 +194,12 @@ void Scene::setupLights() {
} }
void Scene::setSetup(int num) { void Scene::setSetup(int num) {
// Looks like num is zero-based so >= should work to find values
// that are out of the range of valid setups
if (num >= _numSetups || num < 0) {
error("Failed to change scene setup, value out of range!");
return;
}
_currSetup = _setups + num; _currSetup = _setups + num;
} }

10
scene.h
View file

@ -47,10 +47,12 @@ public:
_currSetup->_bkgndZBm->draw(); _currSetup->_bkgndZBm->draw();
if (_currSetup->_bkgndBm == NULL) { if (_currSetup->_bkgndBm == NULL) {
error("Null background for setup %s in %s", _currSetup->_name.c_str(), _name.c_str()); // This should fail softly, for some reason jumping to the signpost (sg) will load
return; // the scene in such a way that the background isn't immediately available
} warning("Background hasn't loaded yet for setup %s in %s!", _currSetup->_name.c_str(), _name.c_str());
_currSetup->_bkgndBm->draw(); } else {
_currSetup->_bkgndBm->draw();
}
} }
void drawBitmaps(ObjectState::Position stage); void drawBitmaps(ObjectState::Position stage);
void setupCamera() { void setupCamera() {

108
smush.cpp
View file

@ -29,6 +29,9 @@
#include <cstring> #include <cstring>
#include <zlib.h> #include <zlib.h>
#define ANNO_HEADER "MakeAnim animation type 'Bl16' parameters: "
#define BUFFER_SIZE 16385
Smush *g_smush; Smush *g_smush;
static uint16 smushDestTable[5786]; static uint16 smushDestTable[5786];
@ -49,6 +52,7 @@ Smush::Smush() {
_videoFinished = false; _videoFinished = false;
_videoPause = true; _videoPause = true;
_updateNeeded = false; _updateNeeded = false;
_startPos = NULL;
_stream = NULL; _stream = NULL;
_movieTime = 0; _movieTime = 0;
_frame = 0; _frame = 0;
@ -65,6 +69,7 @@ void Smush::init() {
_videoFinished = false; _videoFinished = false;
_videoPause = false; _videoPause = false;
_updateNeeded = false; _updateNeeded = false;
_videoLooping = false;
assert(!_internalBuffer); assert(!_internalBuffer);
assert(!_externalBuffer); assert(!_externalBuffer);
@ -88,6 +93,7 @@ void Smush::deinit() {
_externalBuffer = NULL; _externalBuffer = NULL;
} }
_videoLooping = false;
_videoFinished = true; _videoFinished = true;
_videoPause = true; _videoPause = true;
if (_stream) { if (_stream) {
@ -129,10 +135,34 @@ void Smush::handleFrame() {
tag = _file.readUint32BE(); tag = _file.readUint32BE();
if (tag == MKID_BE('ANNO')) { if (tag == MKID_BE('ANNO')) {
printf("Announcement!\n"); char *anno;
byte *data;
size = _file.readUint32BE(); size = _file.readUint32BE();
for (int l = 0; l < size; l++) data = (byte *)malloc(size);
_file.readByte(); _file.read(data, size);
anno = (char *)data;
if (strncmp(anno, ANNO_HEADER, sizeof(ANNO_HEADER)-1) == 0) {
char *annoData = anno + sizeof(ANNO_HEADER);
int loop;
// Examples:
// Water streaming around boat from Manny's balcony
// MakeAnim animation type 'Bl16' parameters: 10000;12000;100;1;0;0;0;0;25;0;
// Water in front of the Blue Casket
// MakeAnim animation type 'Bl16' parameters: 20000;25000;100;1;0;0;0;0;25;0;
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
printf("Announcement data: %s\n", anno);
sscanf(annoData, "%*d;%*d;%*d;%d;%*d;%*d;%*d;%*d;%*d;%*d;%*d;", &loop);
_videoLooping = (bool) loop;
_startPos = _file.getPos();
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
printf("_videoLooping: %d\n", _videoLooping);
} else {
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
printf("Announcement header not understood: %s\n", anno);
}
free(anno);
tag = _file.readUint32BE(); tag = _file.readUint32BE();
} }
@ -164,12 +194,19 @@ printf("Announcement!\n");
_updateNeeded = true; _updateNeeded = true;
_frame++; _frame++;
if (_frame == _nbframes) {
_videoFinished = true;
g_engine->setMode(ENGINE_MODE_NORMAL);
}
_movieTime += _speed / 1000; _movieTime += _speed / 1000;
if (_frame == _nbframes) {
// If we're not supposed to loop (or looping fails) then end the video
if(!_videoLooping || !_file.setPos(_startPos)) {
_videoFinished = true;
g_engine->setMode(ENGINE_MODE_NORMAL);
return;
}
// If the position change succeeded then reset the frame number
// for some reason we need to set it to 1 instead of zero or we
// won't render a frame
_frame = 1;
}
} }
void Smush::handleFramesHeader() { void Smush::handleFramesHeader() {
@ -260,9 +297,42 @@ zlibFile::~zlibFile() {
close(); close();
} }
struct SAVEPOS *zlibFile::getPos() {
struct SAVEPOS *pos;
long position = std::ftell(_handle);
if (position == -1 && debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) {
warning("zlibFile::open() unable to find start position!");
return NULL;
}
pos = (struct SAVEPOS *) malloc(sizeof(struct SAVEPOS));
pos->filePos = position;
inflateCopy(&pos->streamBuf, &_stream);
pos->tmpBuf = (char *)calloc(1, BUFFER_SIZE);
memcpy(pos->tmpBuf, _inBuf, BUFFER_SIZE);
return pos;
}
bool zlibFile::setPos(struct SAVEPOS *pos) {
int ret;
if (_handle == NULL || pos == NULL) {
warning("Unable to rewind SMUSH movie (invalid handle)!");
return false;
}
ret = std::fseek(_handle, pos->filePos, SEEK_SET);
if (ret == -1) {
warning("Unable to rewind SMUSH movie (seek failed)! %m");
return false;
}
memcpy(_inBuf, pos->tmpBuf, BUFFER_SIZE);
inflateCopy(&_stream, &pos->streamBuf);
_fileDone = false;
return true;
}
bool zlibFile::open(const char *filename) { bool zlibFile::open(const char *filename) {
char flags = 0; char flags = 0;
_inBuf = (char *)calloc(1, 16385); _inBuf = (char *)calloc(1, BUFFER_SIZE);
if (_handle) { if (_handle) {
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
@ -287,8 +357,12 @@ bool zlibFile::open(const char *filename) {
fread(_inBuf, 6, sizeof(char), _handle); // XFlags fread(_inBuf, 6, sizeof(char), _handle); // XFlags
// Xtra & Comment // Xtra & Comment
if (((((flags & 0x04) != 0) || ((flags & 0x10) != 0))) && (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_ERROR || debugLevel == DEBUG_ALL)) if (((flags & 0x04) != 0) || ((flags & 0x10) != 0)) {
error("zlibFile::open() Unsupported header flag"); if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_ERROR || debugLevel == DEBUG_ALL) {
error("zlibFile::open() Unsupported header flag");
}
return false;
}
if ((flags & 0x08) != 0) { // Orig. Name if ((flags & 0x08) != 0) { // Orig. Name
do { do {
@ -299,7 +373,7 @@ bool zlibFile::open(const char *filename) {
if ((flags & 0x02) != 0) // CRC if ((flags & 0x02) != 0) // CRC
fread(_inBuf, 2, sizeof(char), _handle); fread(_inBuf, 2, sizeof(char), _handle);
memset(_inBuf, 0, 16384); // Zero buffer (debug) memset(_inBuf, 0, BUFFER_SIZE-1); // Zero buffer (debug)
_stream.zalloc = NULL; _stream.zalloc = NULL;
_stream.zfree = NULL; _stream.zfree = NULL;
_stream.opaque = Z_NULL; _stream.opaque = Z_NULL;
@ -310,7 +384,7 @@ bool zlibFile::open(const char *filename) {
_stream.next_in = NULL; _stream.next_in = NULL;
_stream.next_out = NULL; _stream.next_out = NULL;
_stream.avail_in = 0; _stream.avail_in = 0;
_stream.avail_out = 16384; _stream.avail_out = BUFFER_SIZE-1;
return true; return true;
} }
@ -350,7 +424,7 @@ uint32 zlibFile::read(void *ptr, uint32 len) {
_fileDone = false; _fileDone = false;
while (_stream.avail_out != 0) { while (_stream.avail_out != 0) {
if (_stream.avail_in == 0) { // !eof if (_stream.avail_in == 0) { // !eof
_stream.avail_in = fread(_inBuf, 1, 16384, _handle); _stream.avail_in = fread(_inBuf, 1, BUFFER_SIZE-1, _handle);
if (_stream.avail_in == 0) { if (_stream.avail_in == 0) {
fileEOF = true; fileEOF = true;
break; break;
@ -360,8 +434,10 @@ uint32 zlibFile::read(void *ptr, uint32 len) {
result = inflate(&_stream, Z_NO_FLUSH); result = inflate(&_stream, Z_NO_FLUSH);
if (result == Z_STREAM_END) { // EOF if (result == Z_STREAM_END) { // EOF
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) // "Stream end" is zlib's way of saying that it's done after the current call,
warning("zlibFile::read() Stream ended"); // so as long as no calls are made after we've received this message we're OK
if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
printf("zlibFile::read() Stream ended\n");
_fileDone = true; _fileDone = true;
break; break;
} }

10
smush.h
View file

@ -29,6 +29,12 @@
#include <zlib.h> #include <zlib.h>
#include <cstring> #include <cstring>
struct SAVEPOS {
long filePos;
z_stream streamBuf;
char *tmpBuf;
};
class zlibFile { class zlibFile {
private: private:
FILE *_handle; FILE *_handle;
@ -39,7 +45,9 @@ private:
public: public:
zlibFile(); zlibFile();
~zlibFile(); ~zlibFile();
bool setPos(struct SAVEPOS *pos);
bool open(const char *filename); bool open(const char *filename);
struct SAVEPOS *getPos();
void close(); void close();
bool isOpen(); bool isOpen();
@ -67,6 +75,8 @@ private:
int _freq; int _freq;
bool _videoFinished; bool _videoFinished;
bool _videoPause; bool _videoPause;
bool _videoLooping;
struct SAVEPOS *_startPos;
int _x, _y; int _x, _y;
int _width, _height; int _width, _height;
byte *_internalBuffer, *_externalBuffer; byte *_internalBuffer, *_externalBuffer;

Binary file not shown.