[Scummvm-cvs-logs] CVS: residual actor.cpp,NONE,1.1 actor.h,NONE,1.1 bitmap.cpp,NONE,1.1 bitmap.h,NONE,1.1 bits.h,NONE,1.1 color.h,NONE,1.1 colormap.h,NONE,1.1 costume.cpp,NONE,1.1 costume.h,NONE,1.1 debug.cpp,NONE,1.1 debug.h,NONE,1.1 engine.cpp,NONE,1.1 engine.h,NONE,1.1 hash_map.h,NONE,1.1 keyframe.cpp,NONE,1.1 keyframe.h,NONE,1.1 lab.cpp,NONE,1.1 lab.h,NONE,1.1 localize.cpp,NONE,1.1 localize.h,NONE,1.1 lua.cpp,NONE,1.1 lua.h,NONE,1.1 main.cpp,NONE,1.1 material.cpp,NONE,1.1 material.h,NONE,1.1 mixer.cpp,NONE,1.1 mixer.h,NONE,1.1 model.cpp,NONE,1.1 model.h,NONE,1.1 registry.cpp,NONE,1.1 registry.h,NONE,1.1 resource.cpp,NONE,1.1 resource.h,NONE,1.1 scene.cpp,NONE,1.1 scene.h,NONE,1.1 sound.cpp,NONE,1.1 sound.h,NONE,1.1 textsplit.cpp,NONE,1.1 textsplit.h,NONE,1.1 vector3d.h,NONE,1.1 grimdialog.htm,1.1.1.1,1.2 actor.cc,1.1.1.1,NONE actor.hh,1.1.1.1,NONE bitmap.cc,1.1.1.1,NONE bitmap.hh,1.1.1.1,NONE bits.hh,1.1.1.1,NONE color.hh,1.1.1.1,NONE colormap.hh,1.1.1.1,NONE costume.cc,1.1.1.1,NONE costume.hh,1.1.1.1,NONE debug.cc,1.1.1.1,NONE debug.hh,1.1.1.1,NONE engine.cc,1.1.1.1,NONE engine.hh,1.1.1.1,NONE hash_map.hh,1.1.1.1,NONE keyframe.cc,1.1.1.1,NONE keyframe.hh,1.1.1.1,NONE lab.cc,1.1.1.1,NONE lab.hh,1.1.1.1,NONE localize.cc,1.1.1.1,NONE localize.hh,1.1.1.1,NONE lua.cc,1.1.1.1,NONE lua.hh,1.1.1.1,NONE main.cc,1.1.1.1,NONE material.cc,1.1.1.1,NONE material.hh,1.1.1.1,NONE mixer.cc,1.1.1.1,NONE mixer.hh,1.1.1.1,NONE model.cc,1.1.1.1,NONE model.hh,1.1.1.1,NONE registry.cc,1.1.1.1,NONE registry.hh,1.1.1.1,NONE resource.cc,1.2,NONE resource.hh,1.1.1.1,NONE scene.cc,1.1.1.1,NONE scene.hh,1.1.1.1,NONE sound.cc,1.1.1.1,NONE sound.hh,1.1.1.1,NONE textsplit.cc,1.1.1.1,NONE textsplit.hh,1.1.1.1,NONE vector3d.hh,1.1.1.1,NONE

Vincent Hamm yazoo at users.sourceforge.net
Fri Aug 15 12:54:09 CEST 2003


Update of /cvsroot/scummvm/residual
In directory sc8-pr-cvs1:/tmp/cvs-serv22667

Modified Files:
	grimdialog.htm 
Added Files:
	actor.cpp actor.h bitmap.cpp bitmap.h bits.h color.h 
	colormap.h costume.cpp costume.h debug.cpp debug.h engine.cpp 
	engine.h hash_map.h keyframe.cpp keyframe.h lab.cpp lab.h 
	localize.cpp localize.h lua.cpp lua.h main.cpp material.cpp 
	material.h mixer.cpp mixer.h model.cpp model.h registry.cpp 
	registry.h resource.cpp resource.h scene.cpp scene.h sound.cpp 
	sound.h textsplit.cpp textsplit.h vector3d.h 
Removed Files:
	actor.cc actor.hh bitmap.cc bitmap.hh bits.hh color.hh 
	colormap.hh costume.cc costume.hh debug.cc debug.hh engine.cc 
	engine.hh hash_map.hh keyframe.cc keyframe.hh lab.cc lab.hh 
	localize.cc localize.hh lua.cc lua.hh main.cc material.cc 
	material.hh mixer.cc mixer.hh model.cc model.hh registry.cc 
	registry.hh resource.cc resource.hh scene.cc scene.hh sound.cc 
	sound.hh textsplit.cc textsplit.hh vector3d.hh 
Log Message:
renamed all .cc to cpp and all .hh to .h


--- NEW FILE: actor.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "actor.h"
#include "engine.h"
#include "costume.h"
#include "sound.h"
#include "mixer.h"
#include <cmath>
#include <cstring>
#include <SDL.h>
#include <SDL_opengl.h>

Actor::Actor(const char *name) :
  name_(name), talkColor_(255, 255, 255), pos_(0, 0, 0),
  pitch_(0), yaw_(0), roll_(0), walkRate_(0), turnRate_(0),
  visible_(false), talkSound_(NULL)
{
  Engine::instance()->registerActor(this);
}

void Actor::walkForward() {
  float dist = Engine::instance()->perSecond(walkRate_);
  float yaw_deg = yaw_ * (M_PI / 180), pitch_deg = pitch_ * (M_PI / 180);
  Vector3d forwardVec(-std::sin(yaw_deg) * std::cos(pitch_deg),
		      std::cos(yaw_deg) * std::cos(pitch_deg),
		      std::sin(pitch_deg));
  pos_ += forwardVec * dist;
}

void Actor::turn(int dir) {
  float delta = Engine::instance()->perSecond(turnRate_) * dir;
  yaw_ += delta;
}

float Actor::angleTo(const Actor &a) const {
  float yaw_deg = yaw_ * (M_PI / 180);
  Vector3d forwardVec(-std::sin(yaw_deg), std::cos(yaw_deg), 0);
  Vector3d delta = a.pos() - pos_;
  delta.z() = 0;
  return angle(forwardVec, delta) * (180 / M_PI);
}

void Actor::sayLine(const char *msg) {
  // For now, just play the appropriate sound if found.  Eventually,
  // this needs to handle possibly displaying text, starting up
  // appropriate talking chores, etc.

  // Find the message identifier
  if (msg[0] != '/')
    return;
  const char *secondSlash = std::strchr(msg + 1, '/');
  if (secondSlash == NULL)
    return;
  std::string msgId(msg + 1, secondSlash);
  talkSound_ = ResourceLoader::instance()->loadSound((msgId + ".wav").c_str());
  if (talkSound_ != NULL)
    Mixer::instance()->playVoice(talkSound_);
}

bool Actor::talking() {
  if (talkSound_ == NULL)
    return false;
  if (talkSound_->done()) {
    talkSound_ = NULL;
    return false;
  }
  return true;
}

void Actor::pushCostume(Costume *c) {
  Costume *copy = new Costume(*c, currentCostume());
  costumeStack_.push_back(copy);
}

void Actor::setCostume(Costume *c) {
  if (! costumeStack_.empty())
    popCostume();
  pushCostume(c);
}

void Actor::popCostume() {
  costumeStack_.pop_back();
}

void Actor::clearCostumes() {
  // Make sure to destroy costume copies in reverse order
  while (! costumeStack_.empty())
    costumeStack_.pop_back();
}

Costume *Actor::findCostume(const char *name) {
  for (std::list<ResPtr<Costume> >::iterator i = costumeStack_.begin();
       i != costumeStack_.end(); i++)
    if (std::strcmp((*i)->filename(), name) == 0)
      return *i;
  return NULL;
}

void Actor::update() {
  for (std::list<ResPtr<Costume> >::iterator i = costumeStack_.begin();
       i != costumeStack_.end(); i++)
    (*i)->update();
}

void Actor::draw() {
  if (! costumeStack_.empty()) {
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glTranslatef(pos_.x(), pos_.y(), pos_.z());
    glRotatef(yaw_, 0, 0, 1);
    glRotatef(pitch_, 1, 0, 0);
    glRotatef(roll_, 0, 1, 0);
    costumeStack_.back()->draw();
    glPopMatrix();
  }
}

--- NEW FILE: actor.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 ACTOR_H
#define ACTOR_H

#include "color.h"
#include "vector3d.h"
#include "resource.h"
#include <string>
#include <list>

class Sound;
class Costume;

class Actor {
public:
  Actor(const char *name);

  const char *name() const { return name_.c_str(); }

  void setTalkColor(const Color& c) { talkColor_ = c; }
  Color talkColor() const { return talkColor_; }
  void setPos(Vector3d pos) { pos_ = pos; }
  Vector3d pos() const { return pos_; }
  void setRot(float pitch, float yaw, float roll) {
    pitch_ = pitch; yaw_ = yaw; roll_ = roll;
  }
  float pitch() const { return pitch_; }
  float yaw() const { return yaw_; }
  float roll() const { return roll_; }
  void setVisibility(bool val) { visible_ = val; }
  bool visible() const { return visible_; }
  void putInSet(const char *name) { setName_ = name; }
  void setTurnRate(float rate) { turnRate_ = rate; }
  float turnRate() const { return turnRate_; }
  void setWalkRate(float rate) { walkRate_ = rate; }
  float walkRate() const { return walkRate_; }

  float angleTo(const Actor &a) const;

  bool inSet(const char *name) const {
    return setName_ == name;
  }
  void walkForward();
  void turn(int dir);

  void sayLine(const char *msg);
  bool talking();

  void pushCostume(Costume *c);
  void setCostume(Costume *c);
  void popCostume();
  void clearCostumes();
  Costume *currentCostume() {
    if (costumeStack_.empty())
      return NULL;
    else
      return costumeStack_.back();
  }
  Costume *findCostume(const char *name);
  int costumeStackDepth() const {
    return costumeStack_.size();
  }

  void update();
  void draw();

private:
  std::string name_;
  std::string setName_;
  Color talkColor_;
  Vector3d pos_;
  float pitch_, yaw_, roll_;
  float walkRate_, turnRate_;
  bool visible_;
  ResPtr<Sound> talkSound_;
  std::list<ResPtr<Costume> > costumeStack_;

  friend class Engine;
};

#endif

--- NEW FILE: bitmap.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include <cstdlib>
#include <cstring>
#include <SDL.h>
#include <SDL_opengl.h>
#include "bitmap.h"
#include "bits.h"
#include "debug.h"

static void decompress_codec3(const char *compressed, char *result);

Bitmap::Bitmap(const char *filename, const char *data, int len) :
  Resource(filename)
{
  if (len < 8 || memcmp(data, "BM  F\0\0\0", 8) != 0)
    error("Invalid magic loading bitmap\n");

  int codec = get_LE_uint32(data + 8);
  num_images_ = get_LE_uint32(data + 16);
  x_ = get_LE_uint32(data + 20);
  y_ = get_LE_uint32(data + 24);
  format_ = get_LE_uint32(data + 32);
  width_ = get_LE_uint32(data + 128);
  height_ = get_LE_uint32(data + 132);
  curr_image_ = 0;

  data_ = new char*[num_images_];
  int pos = 0x88;
  for (int i = 0; i < num_images_; i++) {
    data_[i] = new char[2 * width_ * height_];
    if (codec == 0) {
      memcpy(data_[i], data + pos, 2 * width_ * height_);
      pos += 2 * width_ * height_ + 8;
    }
    else if (codec == 3) {
      int compressed_len = get_LE_uint32(data + pos);
      decompress_codec3(data + pos + 4, data_[i]);
      pos += compressed_len + 12;
    }
  }
}

void Bitmap::prepareGL() {
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, 640, 480, 0, 0, 1);
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_LIGHTING);
  glPixelZoom(1, -1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void Bitmap::draw() const {
  glRasterPos2i(x_, y_);
  if (format_ == 1)
    glDrawPixels(width_, height_, GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
		 data_[curr_image_]);
  else if (format_ == 5)
    glDrawPixels(width_, height_, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,
		 data_[curr_image_]);
}

Bitmap::~Bitmap() {
  for (int i = 0; i < num_images_; i++)
    delete[] data_[i];
  delete[] data_;
}

#define GET_BIT bit = bitstr_value & 1; \
  bitstr_len--; \
  bitstr_value >>= 1; \
  if (bitstr_len == 0) { \
    bitstr_value = get_LE_uint16(compressed); \
    bitstr_len = 16; \
    compressed += 2; \
  } \
  do {} while (0)

static void decompress_codec3(const char *compressed, char *result) {
  int bitstr_value = get_LE_uint16(compressed);
  int bitstr_len = 16;
  compressed += 2;
  bool bit;

  for (;;) {
    GET_BIT;
    if (bit == 1)
      *result++ = *compressed++;
    else {
      GET_BIT;
      int copy_len, copy_offset;
      if (bit == 0) {
	GET_BIT;
	copy_len = 2 * bit;
	GET_BIT;
	copy_len += bit + 3;
	copy_offset = get_uint8(compressed++) - 0x100;
      }
      else {
	copy_offset = (get_uint8(compressed) |
		       (get_uint8(compressed + 1) & 0xf0) << 4) - 0x1000;
	copy_len = (get_uint8(compressed + 1) & 0xf) + 3;
	compressed += 2;
	if (copy_len == 3) {
	  copy_len = get_uint8(compressed++) + 1;
	  if (copy_len == 1)
	    return;
	}
      }
      while (copy_len > 0) {
	*result = result[copy_offset];
	result++;
	copy_len--;
      }
    }
  }
}

--- NEW FILE: bitmap.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 BITMAP_H
#define BITMAP_H

#include "resource.h"
#include <cstring>

class Bitmap : public Resource {
public:
  // Construct a bitmap from the given data.
  Bitmap(const char *filename, const char *data, int len);

  // Set up GL for drawing bitmaps
  static void prepareGL();
  void draw() const;

  // Set which image in an animated bitmap to use
  void setNumber(int n) { curr_image_ = n; }

  int numImages() const { return num_images_; }
  int currentImage() const { return curr_image_; }

  int width() const { return width_; }
  int height() const { return height_; }
  int x() const { return x_; }
  int y() const { return y_; }

  ~Bitmap();

private:
  char **data_;
  int num_images_, curr_image_;
  int width_, height_, x_, y_;
  int format_;
};

#endif

--- NEW FILE: bits.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 BITS_H
#define BITS_H

#include <SDL/SDL_byteorder.h>
#include <stdint.h>
#include "vector3d.h"

inline uint8_t get_uint8(const char *data) {
  return *(reinterpret_cast<const unsigned char *>(data));
}

#if SDL_BYTEORDER == SDL_BIG_ENDIAN

inline uint32_t get_LE_uint32(const char *data) {
  const unsigned char *udata = reinterpret_cast<const unsigned char *>(data);
  return udata[0] | (udata[1] << 8) | (udata[2] << 16) | (udata[3] << 24);
}

inline uint16_t get_LE_uint16(const char *data) {
  const unsigned char *udata = reinterpret_cast<const unsigned char *>(data);
  return udata[0] | (udata[1] << 8);
}

inline uint16_t get_BE_uint16(const char *data) {
  return *(reinterpret_cast<const uint16_t *>(data));
}

inline uint32_t get_BE_uint32(const char *data) {
  return *(reinterpret_cast<const uint32_t *>(data));
}

#else

inline uint32_t get_LE_uint32(const char *data) {
  return *(reinterpret_cast<const uint32_t *>(data));
}

inline uint16_t get_LE_uint16(const char *data) {
  return *(reinterpret_cast<const uint16_t *>(data));
}

inline uint16_t get_BE_uint16(const char *data) {
  const unsigned char *udata = reinterpret_cast<const unsigned char *>(data);
  return (udata[0] << 8) | udata[1];
}

inline uint32_t get_BE_uint32(const char *data) {
  const unsigned char *udata = reinterpret_cast<const unsigned char *>(data);
  return (udata[0] << 24) | (udata[1] << 16) | (udata[2] << 8) | udata[3];
}

#endif

#ifdef i386

inline float get_float(const char *data) {
  return *(reinterpret_cast<const float *>(data));
}

#else

#error get_float not implemented on non-i386 machines yet

#endif

inline Vector3d get_vector3d(const char *data) {
  return Vector3d(get_float(data), get_float(data + 4), get_float(data + 8));
}

#endif

--- NEW FILE: color.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 COLOR_H
#define COLOR_H

class Color {
public:
  unsigned char vals_[3];

  Color() { }
  Color(unsigned char r, unsigned char g, unsigned char b) {
    vals_[0] = r; vals_[1] = g; vals_[2] = b;
  }
  Color(const Color& c) {
    vals_[0] = c.vals_[0]; vals_[1] = c.vals_[1]; vals_[2] = c.vals_[2];
  }
  unsigned char &red() { return vals_[0]; }
  unsigned char red() const { return vals_[0]; }
  unsigned char &green() { return vals_[1]; }
  unsigned char green() const { return vals_[1]; }
  unsigned char &blue() { return vals_[2]; }
  unsigned char blue() const { return vals_[2]; }

  Color& operator =(const Color &c) {
    vals_[0] = c.vals_[0]; vals_[1] = c.vals_[1]; vals_[2] = c.vals_[2];
    return *this;
  }
};

#endif

--- NEW FILE: colormap.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 COLORMAP_H
#define COLORMAP_H

#include "debug.h"
#include "resource.h"
#include <cstring>

class Colormap : public Resource {
public:
  // Load a colormap from the given data.
  Colormap(const char *filename, const char *data, int len) :
    Resource(filename)
  {
    if (len < 4 || std::memcmp(data, "CMP ", 4) != 0)
      error("Invalid magic loading colormap\n");
    std::memcpy(colors, data + 64, sizeof(colors));
  }

  // The color data, in RGB format
  char colors[256 * 3];
};

#endif

--- NEW FILE: costume.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "costume.h"
#include "textsplit.h"
#include "debug.h"
#include "engine.h"
#include "colormap.h"
#include "keyframe.h"
#include "model.h"
#include "lua.h"
#include "sound.h"
#include "mixer.h"
#include <string>
#include <cstdio>
#include <map>

class ModelComponent : public Costume::Component {
public:
  ModelComponent(Costume::Component *parent, int parentID,
		 const char *filename);
  ModelComponent *copy(Costume::Component *newParent);
  void setKey(int val);
  void update();
  void reset();
  void setColormap(Colormap *c);
  void loadModel();
  ~ModelComponent();

  Model::HierNode *hierarchy() { return hier_; }
  void draw();

protected:
  std::string filename_;
  ResPtr<Model> obj_;
  ResPtr<Colormap> cmap_;
  Model::HierNode *hier_;
};

class MainModelComponent : public ModelComponent {
public:
  MainModelComponent(Costume::Component *parent, int parentID,
		     const char *filename);
  MainModelComponent *copy(Costume::Component *newParent);
  MainModelComponent *copy(Model *prevObj, Model::HierNode *prevHier);
  void update();
  void reset();
  ~MainModelComponent();

private:
  bool hierShared_;

  friend class Costume;
};

class MeshComponent : public Costume::Component {
public:
  MeshComponent(Costume::Component *parent, int parentID,
		const char *name);
  MeshComponent *copy(Costume::Component *newParent);
  void setKey(int val);
  void reset();
  ~MeshComponent() { }

  Model::HierNode *node() { return node_; }

private:
  int num_;
  Model::HierNode *node_;
};

ModelComponent::ModelComponent(Costume::Component *parent, int parentID,
			       const char *filename) :
  Costume::Component(parent, parentID), filename_(filename), obj_(NULL),
  cmap_(NULL), hier_(NULL) {
}

ModelComponent *ModelComponent::copy(Costume::Component *newParent) {
  loadModel();
  ModelComponent *result = new ModelComponent(*this);
  result->setParent(newParent);
  result->hier_ = obj_->copyHierarchy();
  result->hier_->hierVisible_ = false;
  if (newParent != NULL) {
    MeshComponent *mc = dynamic_cast<MeshComponent *>(newParent);
    if (mc != NULL)
      mc->node()->addChild(result->hier_);
    else
      warning("Parent of model %s wasn't a mesh\n", filename_.c_str());
  }
  return result;
}

void ModelComponent::setKey(int val) {
  hier_->hierVisible_ = (val != 0);
}

// Reset the hierarchy nodes for any keyframe animations (which
// are children of this component and therefore get updated later).
void ModelComponent::update() {
  for (int i = 0; i < obj_->numNodes(); i++) {
    hier_[i].priority_ = -1;
    hier_[i].animPos_ = hier_[i].pos_;
    hier_[i].animPitch_ = hier_[i].pitch_;
    hier_[i].animYaw_ = hier_[i].yaw_;
    hier_[i].animRoll_ = hier_[i].roll_;
    hier_[i].totalWeight_ = 1;
  }
}

void ModelComponent::reset() {
  hier_->hierVisible_ = false;
}

void ModelComponent::setColormap(Colormap *c) {
  cmap_ = c;
  obj_ = ResourceLoader::instance()->loadModel(filename_.c_str(), *c);
  hier_ = obj_->copyHierarchy();
}

void ModelComponent::loadModel() {
  if (obj_ == NULL) {
    warning("No colormap specified for %s\n", filename_.c_str());
    setColormap(ResourceLoader::instance()->loadColormap("item.cmp"));
  }
}

ModelComponent::~ModelComponent() {
  if (hier_ != NULL && hier_->parent_ != NULL)
    hier_->parent_->removeChild(hier_);
  delete[] hier_;
}

void ModelComponent::draw() {
  loadModel();
  if (parent_ == NULL)		// Otherwise it was already drawn by
				// being included in the parent's hierarchy
    hier_->draw();
}

MainModelComponent::MainModelComponent(Costume::Component *parent,
				       int parentID,
				       const char *filename) :
  ModelComponent(parent, parentID, filename), hierShared_(false)
{
}

MainModelComponent *MainModelComponent::copy(Costume::Component *newParent) {
  loadModel();
  MainModelComponent *result = new MainModelComponent(*this);
  result->setParent(newParent);
  result->hier_ = obj_->copyHierarchy();
  result->hierShared_ = false;
  return result;
}

MainModelComponent *MainModelComponent::copy(Model *prevObj,
					     Model::HierNode *prevHier) {
  MainModelComponent *result = new MainModelComponent(*this);
  result->setParent(NULL);
  result->obj_ = prevObj;
  result->hier_ = prevHier;
  result->hierShared_ = true;
  return result;
}

void MainModelComponent::update() {
  if (! hierShared_)		// Otherwise, it was already initialized
				// and reinitializing it will destroy work
				// from previous costumes
    ModelComponent::update();
}

void MainModelComponent::reset() {
  hier_->hierVisible_ = true;
}

MainModelComponent::~MainModelComponent() {
  if (hierShared_)
    hier_ = NULL;		// Keep ~ModelComp from deleting it
}

class ColormapComponent : public Costume::Component {
public:
  ColormapComponent(Costume::Component *parent, int parentID,
		    const char *filename);
  ColormapComponent *copy(Costume::Component *newParent);
  ~ColormapComponent();

private:
  ResPtr<Colormap> cmap_;
};

ColormapComponent::ColormapComponent(Costume::Component *parent,
				     int parentID,
				     const char *filename) :
  Costume::Component(parent, parentID)
{
  cmap_ = ResourceLoader::instance()->loadColormap(filename);
  ModelComponent *mc = dynamic_cast<ModelComponent *>(parent);
  if (mc != NULL)
    mc->setColormap(cmap_);
}

ColormapComponent *ColormapComponent::copy(Costume::Component *newParent) {
  ColormapComponent *result = new ColormapComponent(*this);
  result->setParent(newParent);
  return result;
}

ColormapComponent::~ColormapComponent() {
}

class KeyframeComponent : public Costume::Component {
public:
  KeyframeComponent(Costume::Component *parent, int parentID,
		    const char *filename);
  KeyframeComponent *copy(Costume::Component *newParent);
  void setKey(int val);
  void update();
  void reset();
  ~KeyframeComponent() { }

private:
  ResPtr<KeyframeAnim> keyf_;
  int priority1_, priority2_;
  Model::HierNode *hier_;
  bool active_;
  int repeatMode_;
  int currTime_;
};

KeyframeComponent::KeyframeComponent(Costume::Component *parent,
				     int parentID,
				     const char *filename) :
  Costume::Component(parent, parentID), priority1_(1), priority2_(5),
  hier_(NULL), active_(false)
{
  const char *comma = std::strchr(filename, ',');
  if (comma != NULL) {
    std::string realName(filename, comma);
    keyf_ = ResourceLoader::instance()->loadKeyframe(realName.c_str());
    std::sscanf(comma + 1, "%d,%d", &priority1_, &priority2_);
  }
  else
    keyf_ = ResourceLoader::instance()->loadKeyframe(filename);
}

void KeyframeComponent::setKey(int val) {
  switch (val) {
  case 0:
  case 1:
  case 2:
  case 3:
    if (! active_ || val != 1) {
      active_ = true;
      currTime_ = -1;
    }
    repeatMode_ = val;
    break;
  case 4:
    active_ = false;
    break;
  default:
    warning("Unknown key %d for keyframe %s\n", val, keyf_->filename());
  }
}

void KeyframeComponent::reset() {
  active_ = false;
}

void KeyframeComponent::update() {
  if (! active_)
    return;
  if (currTime_ < 0)		// For first time through
    currTime_ = 0;
  else
    currTime_ += Engine::instance()->frameTime();
  int animLength = int(keyf_->length() * 1000);
  if (currTime_ > animLength) { // What to do at end?
    switch (repeatMode_) {
    case 0:			// Stop
    case 3:			// Fade at end
      active_ = false;
      return;
    case 1:			// Loop
      do
	currTime_ -= animLength;
      while (currTime_ > animLength);
      break;
    case 2:			// Hold at end
      currTime_ = animLength;
      break;
    }
  }
  keyf_->animate(hier_, currTime_ / 1000.0, priority1_, priority2_);
}

KeyframeComponent *KeyframeComponent::copy(Costume::Component *newParent) {
  KeyframeComponent *result = new KeyframeComponent(*this);
  result->setParent(newParent);
  ModelComponent *mc = dynamic_cast<ModelComponent *>(newParent);
  if (mc != NULL)
    result->hier_ = mc->hierarchy();
  else {
    warning("Parent of %s was not a model\n", keyf_->filename());
    result->hier_ = NULL;
  }
  return result;
}

MeshComponent::MeshComponent(Costume::Component *parent, int parentID,
			     const char *name) :
  Costume::Component(parent, parentID), node_(NULL) {
  if (std::sscanf(name, "mesh %d", &num_) < 1)
    error("Couldn't parse mesh name %s\n", name);
}

MeshComponent *MeshComponent::copy(Costume::Component *newParent) {
  MeshComponent *result = new MeshComponent(*this);
  result->setParent(newParent);
  ModelComponent *mc = dynamic_cast<ModelComponent *>(newParent);
  if (mc != NULL)
    result->node_ = mc->hierarchy() + num_;
  else {
    warning("Parent of mesh %d was not a model\n", num_);
    result->node_ = NULL;
  }
  return result;
}

void MeshComponent::setKey(int val) {
  node_->meshVisible_ = (val != 0);
}

void MeshComponent::reset() {
  node_->meshVisible_ = true;
}

class LuaVarComponent : public Costume::Component {
public:
  LuaVarComponent(Costume::Component *parent, int parentID,
		  const char *name);
  LuaVarComponent *copy(Costume::Component *newParent);
  void setKey(int val);
  ~LuaVarComponent() { }

private:
  std::string name_;
};

LuaVarComponent::LuaVarComponent(Costume::Component *parent, int parentID,
				 const char *name) :
  Costume::Component(parent, parentID), name_(name) {
}

LuaVarComponent *LuaVarComponent::copy(Costume::Component *newParent) {
  LuaVarComponent *result = new LuaVarComponent(*this);
  result->setParent(newParent);
  return result;
}

void LuaVarComponent::setKey(int val) {
  lua_pushnumber(val);
  lua_setglobal(const_cast<char *>(name_.c_str()));
}

class SoundComponent : public Costume::Component {
public:
  SoundComponent(Costume::Component *parent, int parentID,
		 const char *name);
  SoundComponent *copy(Costume::Component *newParent);
  void setKey(int val);
  void reset();
  ~SoundComponent() { }

private:
  ResPtr<Sound> sound_;
};

SoundComponent::SoundComponent(Costume::Component *parent, int parentID,
			       const char *filename) :
  Costume::Component(parent, parentID) {
  const char *comma = std::strchr(filename, ',');
  if (comma != NULL) {
    std::string realName(filename, comma);
    sound_ = ResourceLoader::instance()->loadSound(realName.c_str());
  }
  else
    sound_ = ResourceLoader::instance()->loadSound(filename);
}

SoundComponent *SoundComponent::copy(Costume::Component *newParent) {
  SoundComponent *result = new SoundComponent(*this);
  result->setParent(newParent);
  return result;
}

void SoundComponent::setKey(int val) {
  switch (val) {
  case 0:
    Mixer::instance()->playSfx(sound_);
    break;
  case 2:
    Mixer::instance()->stopSfx(sound_);
    break;
  default:
    warning("Unknown key %d for sound %s\n", val, sound_->filename());
  }
}

void SoundComponent::reset() {
  Mixer::instance()->stopSfx(sound_);
}

Costume::Costume(const char *filename, const char *data, int len) :
  Resource(filename), orig_(NULL)
{
  TextSplitter ts(data, len);
  ts.expectString("costume v0.1");

  ts.expectString("section tags");
  int numTags;
  ts.scanString(" numtags %d", 1, &numTags);
  typedef char tag[4];
  tag *tags = new tag[numTags];
  for (int i = 0; i < numTags; i++) {
    int which;
    tag t;
    ts.scanString(" %d '%c%c%c%c'", 5, &which, &t[0], &t[1], &t[2], &t[3]);
    std::memcpy(tags + which, t, 4);
  }

  ts.expectString("section components");
  ts.scanString(" numcomponents %d", 1, &numComponents_);
  components_ = new Component*[numComponents_];
  for (int i = 0; i < numComponents_; i++) {
    int id, tagID, hash, parentID;
    int namePos;
    const char *line = ts.currentLine();
    if (std::sscanf(line, " %d %d %d %d %n", &id, &tagID, &hash, &parentID,
		    &namePos) < 4)
      error("Bad component specification line: `%s'\n", line);
    ts.nextLine();
    components_[id] =
      loadComponent(tags[tagID],
		    parentID == -1 ? NULL : components_[parentID], parentID,
		    line + namePos);
  }

  ts.expectString("section chores");
  ts.scanString(" numchores %d", 1, &numChores_);
  chores_ = new Chore[numChores_];
  for (int i = 0; i < numChores_; i++) {
    int id, length, tracks;
    char name[32];
    ts.scanString(" %d %d %d %32s", 4, &id, &length, &tracks, name);
    chores_[id].length_ = length;
    chores_[id].numTracks_ = tracks;
    std::memcpy(chores_[id].name_, name, 32);
  }

  ts.expectString("section keys");
  for (int i = 0; i < numChores_; i++) {
    int which;
    ts.scanString("chore %d", 1, &which);
    chores_[which].load(this, ts);
  }

delete[] tags;
}

Costume::~Costume() {
  stopChores();
  for (int i = numComponents_ - 1; i >= 0; i--)
    delete components_[i];
  delete[] chores_;
}

Costume::Component::Component(Component *parent, int parentID) {
  parentID_ = parentID;
  setParent(parent);
}

void Costume::Component::setParent(Component *newParent) {
  parent_ = newParent;
  child_ = NULL;
  sibling_ = NULL;
  if (parent_ != NULL) {
    Component **lastChildPos = &parent_->child_;
    while (*lastChildPos != NULL)
      lastChildPos = &((*lastChildPos)->sibling_);
    *lastChildPos = this;
  }
}

void Costume::Chore::load(Costume *owner, TextSplitter &ts) {
  owner_ = owner;
  tracks_ = new ChoreTrack[numTracks_];
  hasPlayed_ = playing_ = false;
  for (int i = 0; i < numTracks_; i++) {
    int compID, numKeys;
    ts.scanString(" %d %d", 2, &compID, &numKeys);
    tracks_[i].compID_ = compID;
    tracks_[i].numKeys_ = numKeys;
    tracks_[i].keys_ = new TrackKey[numKeys];
    for (int j = 0; j < numKeys; j++)
      ts.scanString(" %d %d", 2, &tracks_[i].keys_[j].time_,
		    &tracks_[i].keys_[j].value_);
  }
}

void Costume::Chore::play() {
  playing_ = true;
  hasPlayed_ = true;
  looping_ = false;
  currTime_ = -1;
}

void Costume::Chore::playLooping() {
  playing_ = true;
  hasPlayed_ = true;
  looping_ = true;
  currTime_ = -1;
}

void Costume::Chore::stop() {
  if (! hasPlayed_)
    return;
  playing_ = false;
  hasPlayed_ = false;
  for (int i = 0; i < numTracks_; i++) {
    Component *comp = owner_->components_[tracks_[i].compID_];
    if (comp != NULL)
      comp->reset();
  }
}

void Costume::Chore::setKeys(int startTime, int stopTime) {
  for (int i = 0; i < numTracks_; i++) {
    Component *comp = owner_->components_[tracks_[i].compID_];
    if (comp == NULL)
      continue;
    for (int j = 0; j < tracks_[i].numKeys_; j++) {
      if (tracks_[i].keys_[j].time_ > stopTime)
	break;
      if (tracks_[i].keys_[j].time_ > startTime)
	comp->setKey(tracks_[i].keys_[j].value_);
    }
  }
}

void Costume::Chore::update() {
  if (! playing_)
    return;
  int newTime;
  if (currTime_ < 0)
    newTime = 0;		// For first time through
  else
    newTime = currTime_ + Engine::instance()->frameTime();
  setKeys(currTime_, newTime);
  if (newTime > length_) {
    if (! looping_)
      playing_ = false;
    else {
      do {
	newTime -= length_;
	setKeys(-1, newTime);
      } while (newTime > length_);
    }
  }
  currTime_ = newTime;
}

Costume::Component *Costume::loadComponent
(char tag[4], Costume::Component *parent, int parentID, const char *name) {
  if (std::memcmp(tag, "mmdl", 4) == 0)
    return new MainModelComponent(parent, parentID, name);
  else if (std::memcmp(tag, "modl", 4) == 0)
    return new ModelComponent(parent, parentID, name);
  else if (std::memcmp(tag, "cmap", 4) == 0)
    return new ColormapComponent(parent, parentID, name);
  else if (std::memcmp(tag, "keyf", 4) == 0)
    return new KeyframeComponent(parent, parentID, name);
  else if (std::memcmp(tag, "mesh", 4) == 0)
    return new MeshComponent(parent, parentID, name);
  else if (std::memcmp(tag, "luav", 4) == 0)
    return new LuaVarComponent(parent, parentID, name);
  else if (std::memcmp(tag, "imls", 4) == 0)
    return new SoundComponent(parent, parentID, name);
  warning("Unknown tag '%.4s', name '%s'\n", tag, name);
  return NULL;
}

Costume::Costume(Costume &orig, Costume *prev) : Resource(orig), orig_(&orig) {
  numComponents_ = orig.numComponents_;
  components_ = new Component*[numComponents_];
  int start = 0;

  MainModelComponent *mmc =
    dynamic_cast<MainModelComponent *>(orig.components_[0]);
  MainModelComponent *prev_mmc =
    (prev == NULL ? NULL :
     dynamic_cast<MainModelComponent *>(prev->components_[0]));
  if (mmc != NULL && prev_mmc != NULL &&
      mmc->filename_ == prev_mmc->filename_) {
    prev_mmc->loadModel();
    components_[0] = mmc->copy(prev_mmc->obj_, prev_mmc->hier_);
    start = 1;
  }

  for (int i = start; i < numComponents_; i++) {
    if (orig.components_[i] != NULL) {
      Component *newParent;
      if (orig.components_[i]->parentID_ >= 0)
	newParent = components_[orig.components_[i]->parentID_];
      else
	newParent = NULL;
      components_[i] = orig.components_[i]->copy(newParent);
    }
    else
      components_[i] = NULL;
  }
  numChores_ = orig.numChores_;
  chores_ = new Chore[numChores_];
  std::memcpy(chores_, orig.chores_, numChores_ * sizeof(Chore));
  for (int i = 0; i < numChores_; i++)
    chores_[i].owner_ = this;
}

void Costume::stopChores() {
  for (int i = 0; i < numChores_; i++)
    chores_[i].stop();
}

int Costume::isChoring(int num, bool excludeLooping) {
  if (chores_[num].playing_ && !(excludeLooping && chores_[num].looping_))
    return num;
  else
    return -1;
}

int Costume::isChoring(bool excludeLooping) {
  for (int i = 0; i < numChores_; i++) {
    if (chores_[i].playing_ && !(excludeLooping && chores_[i].looping_))
      return i;
  }
  return -1;
}

void Costume::draw() {
  for (int i = 0; i < numComponents_; i++)
    if (components_[i] != NULL)
      components_[i]->draw();
}

void Costume::update() {
  for (int i = 0; i < numChores_; i++)
    chores_[i].update();
  for (int i = 0; i < numComponents_; i++)
    if (components_[i] != NULL)
      components_[i]->update();
}

--- NEW FILE: costume.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 COSTUME_H
#define COSTUME_H

#include "resource.h"

class TextSplitter;
class Actor;

class Costume : public Resource {
public:
  Costume(const char *filename, const char *data, int len);

  // Create a copy of orig, sharing the model data from prev if
  // appropriate
  Costume(Costume &orig, Costume *prev);

  ~Costume();

  void playChore(int num) { chores_[num].play(); }
  void playChoreLooping(int num) { chores_[num].playLooping(); }
  void setChoreLooping(int num, bool val) { chores_[num].setLooping(val); }
  void stopChore(int num) { chores_[num].stop(); }
  void stopChores();
  int isChoring(int num, bool excludeLooping);
  int isChoring(bool excludeLooping);

  void update();
  void draw();

  class Component {
  public:
    Component(Component *parent, int parentID);
    virtual Component *copy(Component *newParent) = 0;
    virtual void setKey(int /* val */) { }
    virtual void update() { }
    virtual void draw() { }
    virtual void reset() { }
    virtual ~Component() { }

    void setParent(Component *newParent);

  protected:
    int parentID_;
    Component *parent_, *child_, *sibling_;

    friend class Costume;
  };

private:
  // Reference the original copy of the costume in the cache
  ResPtr<Costume> orig_;

  Component *loadComponent(char tag[4], Component *parent, int parentID,
			   const char *name);

  int numComponents_;
  Component **components_;

  struct TrackKey {
    int time_, value_;
  };

  struct ChoreTrack {
    int compID_;
    int numKeys_;
    TrackKey *keys_;
  };

  class Chore {
  public:
    void load(Costume *owner, TextSplitter &ts);
    void play();
    void playLooping();
    void setLooping(bool val) { looping_ = val; }
    void stop();
    void update();

  private:
    Costume *owner_;

    int length_;
    int numTracks_;
    ChoreTrack *tracks_;
    char name_[32];

    bool hasPlayed_, playing_, looping_;
    int currTime_;

    void setKeys(int startTime, int stopTime);

    friend class Costume;
  };

  int numChores_;
  Chore *chores_;
};

#endif

--- NEW FILE: debug.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "debug.h"
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <SDL.h>

void warning(const char *fmt, ...) {
  std::fprintf(stderr, "WARNING: ");

  std::va_list va;

  va_start(va, fmt);
  std::vfprintf(stderr, fmt, va);
  va_end(va);
  std::fprintf(stderr, "\n");
}

void error(const char *fmt, ...) {
  std::fprintf(stderr, "ERROR: ");

  std::va_list va;

  va_start(va, fmt);
  std::vfprintf(stderr, fmt, va);
  va_end(va);
  std::fprintf(stderr, "\n");

  SDL_Quit();
  exit(1);
}

--- NEW FILE: debug.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 DEBUG_H
#define DEBUG_H

void warning(const char *fmt, ...);
void error(const char *fmt, ...);

#endif

--- NEW FILE: engine.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "engine.h"
#include "scene.h"
#include "lua.h"
#include "colormap.h"
#include "actor.h"
#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_timer.h>

Engine *Engine::instance_ = NULL;

Engine::Engine() :
  currScene_(NULL), selectedActor_(NULL)
{
  for (int i = 0; i < SDLK_EXTRA_LAST; i++)
    controlsEnabled_[i] = false;
}

void Engine::mainLoop() {
  frameTime_ = 0;
  frameStart_ = SDL_GetTicks();

  for (;;) {
    // Process events
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
      if (event.type == SDL_KEYDOWN &&
	  controlsEnabled_[event.key.keysym.sym]) {
	lua_beginblock();
	lua_Object handler = getEventHandler("buttonHandler");
	if (handler != LUA_NOOBJECT) {
	  lua_pushnumber(event.key.keysym.sym);
	  lua_pushnumber(1);
	  lua_pushnil();
	  lua_callfunction(handler);
	}
	lua_endblock();
      }
      if (event.type == SDL_KEYUP &&
	  controlsEnabled_[event.key.keysym.sym]) {
	lua_beginblock();
	lua_Object handler = getEventHandler("buttonHandler");
	if (handler != LUA_NOOBJECT) {
	  lua_pushnumber(event.key.keysym.sym);
	  lua_pushnil();
	  lua_pushnil();
	  lua_callfunction(handler);
	}
	lua_endblock();
      }
      if (event.type == SDL_QUIT) {
	lua_beginblock();
	lua_Object handler = getEventHandler("exitHandler");
	if (handler != LUA_NOOBJECT)
	  lua_callfunction(handler);
	lua_endblock();
      }
      if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_q)
	return;
    }

    // Run asynchronous tasks
    lua_runtasks();

    // Update actor costumes
    for (actor_list_type::iterator i = actors_.begin();
	 i != actors_.end(); i++) {
      Actor *a = *i;
      if (a->inSet(currScene_->name()) && a->visible())
	a->update();
    }

    // Draw the screen
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    Bitmap::prepareGL();
    if (currScene_ != NULL)
      currScene_->drawBackground();

    glEnable(GL_DEPTH_TEST);
    currScene_->setupCamera();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // Draw actors
    glEnable(GL_TEXTURE_2D);
    for (actor_list_type::iterator i = actors_.begin();
	 i != actors_.end(); i++) {
      Actor *a = *i;
      if (a->inSet(currScene_->name()) && a->visible())
	a->draw();
    }
    glDisable(GL_TEXTURE_2D);

    SDL_GL_SwapBuffers();

    // Update timing information
    unsigned newStart = SDL_GetTicks();
    frameTime_ = newStart - frameStart_;
    frameStart_ = newStart;

    lua_beginblock();
    set_frameTime(frameTime_);
    lua_endblock();
  }
}

void Engine::setScene(const char *name) {
  Block *b = ResourceLoader::instance()->getFileBlock(name);
  if (b == NULL)
    warning("Could not find scene file %s\n", name);
  delete currScene_;
  currScene_ = new Scene(name, b->data(), b->len());
  delete b;
}

--- NEW FILE: engine.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 ENGINE_H
#define ENGINE_H

#include "scene.h"
#include <cstdlib>
#include <list>
#include <SDL/SDL_keysym.h>

class Actor;

// Fake SDLK_* values for joystick and mouse events
enum {
  SDLK_JOY1_B1 = SDLK_LAST,
  SDLK_JOY1_B2,
  SDLK_JOY1_B3,
  SDLK_JOY1_B4,
  SDLK_JOY1_B5,
  SDLK_JOY1_B6,
  SDLK_JOY1_B7,
  SDLK_JOY1_B8,
  SDLK_JOY1_B9,
  SDLK_JOY1_B10,
  SDLK_JOY1_HLEFT,
  SDLK_JOY1_HUP,
  SDLK_JOY1_HRIGHT,
  SDLK_JOY1_HDOWN,
  SDLK_JOY2_B1,
  SDLK_JOY2_B2,
  SDLK_JOY2_B3,
  SDLK_JOY2_B4,
  SDLK_JOY2_B5,
  SDLK_JOY2_B6,
  SDLK_JOY2_B7,
  SDLK_JOY2_B8,
  SDLK_JOY2_B9,
  SDLK_JOY2_B10,
  SDLK_JOY2_HLEFT,
  SDLK_JOY2_HUP,
  SDLK_JOY2_HRIGHT,
  SDLK_JOY2_HDOWN,
  SDLK_MOUSE_B1,
  SDLK_MOUSE_B2,
  SDLK_MOUSE_B3,
  SDLK_MOUSE_B4,
  SDLK_AXIS_JOY1_X,
  SDLK_AXIS_JOY1_Y,
  SDLK_AXIS_JOY1_Z,
  SDLK_AXIS_JOY1_R,
  SDLK_AXIS_JOY1_U,
  SDLK_AXIS_JOY1_V,
  SDLK_AXIS_JOY2_X,
  SDLK_AXIS_JOY2_Y,
  SDLK_AXIS_JOY2_Z,
  SDLK_AXIS_JOY2_R,
  SDLK_AXIS_JOY2_U,
  SDLK_AXIS_JOY2_V,
  SDLK_AXIS_MOUSE_X,
  SDLK_AXIS_MOUSE_Y,
  SDLK_AXIS_MOUSE_Z,
  SDLK_EXTRA_LAST
};

class Engine {
public:
  static Engine *instance() {
    if (instance_ == NULL)
      instance_ = new Engine;
    return instance_;
  }

  void mainLoop();
  unsigned frameStart() const { return frameStart_; }
  unsigned frameTime() const { return frameTime_; }

  float perSecond(float rate) const { return rate * frameTime_ / 1000; }

  void enableControl(int num) { controlsEnabled_[num] = true; }
  void disableControl(int num) { controlsEnabled_[num] = false; }

  void registerActor(Actor *a) { actors_.push_back(a); }

  void setScene(const char *name);
  Scene *currScene() { return currScene_; }
  const char *sceneName() const { return currScene_->name(); }

  typedef std::list<Actor *> actor_list_type;
  actor_list_type::const_iterator actorsBegin() const {
    return actors_.begin();
  }
  actor_list_type::const_iterator actorsEnd() const {
    return actors_.end();
  }

  void setSelectedActor(Actor *a) { selectedActor_ = a; }
  Actor *selectedActor() { return selectedActor_; }

private:
  static Engine *instance_;

  Engine();
  ~Engine() { }

  Scene *currScene_;

  unsigned frameStart_, frameTime_;

  bool controlsEnabled_[SDLK_EXTRA_LAST];

  actor_list_type actors_;
  Actor *selectedActor_;
};

#endif

--- NEW FILE: hash_map.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 HASH_MAP_HH
#define HASH_MAP_HH

#include <ext/hash_map>
#include <string>

namespace std {
  using namespace __gnu_cxx;
};

namespace __gnu_cxx {
  template<> struct hash<std::string> {
    size_t operator()(const std::string &s) const {
      return std::hash<const char *>()(s.c_str());
    }
  };
}

#endif

--- NEW FILE: keyframe.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "keyframe.h"
#include "debug.h"
#include "bits.h"
#include "textsplit.h"
#include <cstring>

KeyframeAnim::KeyframeAnim(const char *filename, const char *data, int len) :
  Resource(filename) {
  if (len >= 4 && std::memcmp(data, "FYEK", 4) == 0)
    loadBinary(data, len);
  else {
    TextSplitter ts(data, len);
    loadText(ts);
  }
}

void KeyframeAnim::loadBinary(const char *data, int len) {
  flags_ = get_LE_uint32(data + 40);
  type_ = get_LE_uint32(data + 48);
  fps_ = get_float(data + 52);
  numFrames_ = get_LE_uint32(data + 56);
  numJoints_ = get_LE_uint32(data + 60);
  numMarkers_ = get_LE_uint32(data + 68);
  markers_ = new Marker[numMarkers_];
  for (int i = 0; i < numMarkers_; i++) {
    markers_[i].frame_ = get_float(data + 72 + 4 * i);
    markers_[i].val_ = get_LE_uint32(data + 104 + 4 * i);
  }

  nodes_ = new KeyframeNode*[numJoints_];
  for (int i = 0; i < numJoints_; i++)
    nodes_[i] = NULL;
  const char *dataEnd = data + len;
  data += 180;
  while (data < dataEnd) {
    int nodeNum = get_LE_uint32(data + 32);
    nodes_[nodeNum] = new KeyframeNode;
    nodes_[nodeNum]->loadBinary(data);
  }
}

void KeyframeAnim::loadText(TextSplitter &ts) {
  ts.expectString("section: header");
  ts.scanString("flags %i", 1, &flags_);
  ts.scanString("type %i", 1, &type_);
  ts.scanString("frames %d", 1, &numFrames_);
  ts.scanString("fps %f", 1, &fps_);
  ts.scanString("joints %d", 1, &numJoints_);

  if (std::strcmp(ts.currentLine(), "section: markers") == 0) {
    ts.nextLine();
    ts.scanString("markers %d", 1, &numMarkers_);
    markers_ = new Marker[numMarkers_];
    for (int i = 0; i < numMarkers_; i++)
      ts.scanString("%f %d", 2, &markers_[i].frame_, &markers_[i].val_);
  }
  else {
    numMarkers_ = 0;
    markers_ = NULL;
  }

  ts.expectString("section: keyframe nodes");
  int numNodes;
  ts.scanString("nodes %d", 1, &numNodes);
  nodes_ = new KeyframeNode*[numJoints_];
  for (int i = 0; i < numJoints_; i++)
    nodes_[i] = NULL;
  for (int i = 0; i < numNodes; i++) {
    int which;
    ts.scanString("node %d", 1, &which);
    nodes_[which] = new KeyframeNode;
    nodes_[which]->loadText(ts);
  }
}

KeyframeAnim::~KeyframeAnim() {
  for (int i = 0; i < numJoints_; i++)
    delete nodes_[i];
  delete[] markers_;
}

void KeyframeAnim::animate(Model::HierNode *nodes, float time,
			   int priority1, int priority2) const {
  float frame = time * fps_;
  if (frame > numFrames_)
    frame = numFrames_;
  for (int i = 0; i < numJoints_; i++)
    if (nodes_[i] != NULL)
      nodes_[i]->animate(nodes[i], frame,
			 ((type_ & nodes[i].type_) != 0 ?
			  priority2 : priority1));
}

void KeyframeAnim::KeyframeEntry::loadBinary(const char *&data) {
  frame_ = get_float(data);
  flags_ = get_LE_uint32(data + 4);
  pos_ = get_vector3d(data + 8);
  pitch_ = get_float(data + 20);
  yaw_ = get_float(data + 24);
  roll_ = get_float(data + 28);
  dpos_ = get_vector3d(data + 32);
  dpitch_ = get_float(data + 44);
  dyaw_ = get_float(data + 48);
  droll_ = get_float(data + 52);
  data += 56;
}

void KeyframeAnim::KeyframeNode::loadBinary(const char *&data) {
  std::memcpy(meshName_, data, 32);
  numEntries_ = get_LE_uint32(data + 36);
  data += 44;
  entries_ = new KeyframeEntry[numEntries_];
  for (int i = 0; i < numEntries_; i++)
    entries_[i].loadBinary(data);
}

void KeyframeAnim::KeyframeNode::loadText(TextSplitter &ts) {
  ts.scanString("mesh name %s", 1, meshName_);
  ts.scanString("entries %d", 1, &numEntries_);
  entries_ = new KeyframeEntry[numEntries_];
  for (int i = 0; i < numEntries_; i++) {
    int which, flags;
    float frame, x, y, z, p, yaw, r, dx, dy, dz, dp, dyaw, dr;
    ts.scanString(" %d: %f %i %f %f %f %f %f %f", 9, &which, &frame, &flags,
		  &x, &y, &z, &p, &yaw, &r);
    ts.scanString(" %f %f %f %f %f %f", 6, &dx, &dy, &dz, &dp, &dyaw, &dr);
    entries_[which].frame_ = frame;
    entries_[which].flags_ = flags;
    entries_[which].pos_ = Vector3d(x, y, z);
    entries_[which].dpos_ = Vector3d(dx, dy, dz);
    entries_[which].pitch_ = p;
    entries_[which].yaw_ = yaw;
    entries_[which].roll_ = r;
    entries_[which].dpitch_ = dp;
    entries_[which].dyaw_ = dyaw;
    entries_[which].droll_ = dr;
  }
}

KeyframeAnim::KeyframeNode::~KeyframeNode() {
  delete[] entries_;
}

void KeyframeAnim::KeyframeNode::animate(Model::HierNode &node,
					 float frame, int priority) const {
  if (numEntries_ == 0)
    return;
  if (priority < node.priority_)
    return;

  // Do a binary search for the nearest previous frame
  // Loop invariant: entries_[low].frame_ <= frame < entries_[high].frame_
  int low = 0, high = numEntries_;
  while (high > low + 1) {
    int mid = (low + high) / 2;
    if (entries_[mid].frame_ <= frame)
      low = mid;
    else
      high = mid;
  }

  float dt = frame - entries_[low].frame_;
  Vector3d pos = entries_[low].pos_ + dt * entries_[low].dpos_;
  float pitch = entries_[low].pitch_ + dt * entries_[low].dpitch_;
  float yaw = entries_[low].yaw_ + dt * entries_[low].dyaw_;
  float roll = entries_[low].roll_ + dt * entries_[low].droll_;
  if (pitch > 180)
    pitch -= 360;
  if (yaw > 180)
    yaw -= 360;
  if (roll > 180)
    roll -= 360;

  if (priority > node.priority_) {
    node.priority_ = priority;
    node.totalWeight_ = 1;
    node.animPos_ = pos;
    node.animPitch_ = pitch;
    node.animYaw_ = yaw;
    node.animRoll_ = roll;
  }
  else {			// priority == node.priority_
    node.totalWeight_++;
    node.animPos_ += pos;
    node.animPitch_ += pitch;
    node.animYaw_ += yaw;
    node.animRoll_ += roll;
  }
}

--- NEW FILE: keyframe.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 KEYFRAME_H
#define KEYFRAME_H

#include "vector3d.h"
#include "resource.h"
#include "model.h"

class TextSplitter;

class KeyframeAnim : public Resource {
public:
  KeyframeAnim(const char *filename, const char *data, int len);
  ~KeyframeAnim();

  void loadBinary(const char *data, int len);
  void loadText(TextSplitter &ts);
  void animate(Model::HierNode *nodes, float time,
	       int priority1 = 1, int priority2 = 5) const;

  float length() const { return numFrames_ / fps_; }

private:
  int flags_, type_, numFrames_, numJoints_;
  float fps_;
  int numMarkers_;

  struct Marker {
    float frame_;
    int val_;
  };
  Marker *markers_;

  struct KeyframeEntry {
    void loadBinary(const char *&data);

    float frame_;
    int flags_;
    Vector3d pos_, dpos_;
    float pitch_, yaw_, roll_, dpitch_, dyaw_, droll_;
  };

  struct KeyframeNode {
    void loadBinary(const char *&data);
    void loadText(TextSplitter &ts);
    ~KeyframeNode();

    void animate(Model::HierNode &node, float frame, int priority) const;
    char meshName_[32];
    int numEntries_;
    KeyframeEntry *entries_;
  };

  KeyframeNode **nodes_;
};

#endif

--- NEW FILE: lab.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "lab.h"
#include "bits.h"
#include <algorithm>
#include <cstdlib>
#include <cctype>

bool Lab::open(const char *filename) {
  close();
  f_ = std::fopen(filename, "rb");
  if (! isOpen())
    return false;

  char header[16];
  if (std::fread(header, 1, sizeof(header), f_) < sizeof(header)) {
    close();
    return false;
  }
  if (std::memcmp(header, "LABN", 4) != 0) {
    close();
    return false;
  }

  int num_entries = get_LE_uint32(header + 8);
  int string_table_size = get_LE_uint32(header + 12);

  char *string_table = new char[string_table_size];
  std::fseek(f_, 16 * (num_entries + 1), SEEK_SET);
  std::fread(string_table, 1, string_table_size, f_);

  std::fseek(f_, 16, SEEK_SET);
  char binary_entry[16];
  file_map_.resize(num_entries);
  for (int i = 0; i < num_entries; i++) {
    std::fread(binary_entry, 1, 16, f_);
    int fname_offset = get_LE_uint32(binary_entry);
    int start = get_LE_uint32(binary_entry + 4);
    int size = get_LE_uint32(binary_entry + 8);

    std::string fname = string_table + fname_offset;
    std::transform(fname.begin(), fname.end(), fname.begin(), tolower);

    file_map_.insert(std::make_pair(fname, LabEntry(start, size)));
  }

  delete [] string_table;
  return true;
}

bool Lab::fileExists(const char *filename) const {
  return find_filename(filename) != file_map_.end();
}

Block *Lab::getFileBlock(const char *filename) const {
  file_map_type::const_iterator i = find_filename(filename);
  if (i == file_map_.end())
    return NULL;

  std::fseek(f_, i->second.offset, SEEK_SET);
  char *data = new char[i->second.len];
  std::fread(data, 1, i->second.len, f_);
  return new Block(data, i->second.len);
}

int Lab::fileLength(const char *filename) const {
  file_map_type::const_iterator i = find_filename(filename);
  if (i == file_map_.end())
    return -1;

  return i->second.len;
}

Lab::file_map_type::const_iterator
Lab::find_filename(const char *filename) const {
  std::string s = filename;
  std::transform(s.begin(), s.end(), s.begin(), tolower);
  return file_map_.find(s);
}

void Lab::close() {
  if (f_ != NULL)
    std::fclose(f_);
  f_ = NULL;
  file_map_.clear();
}

--- NEW FILE: lab.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 LAB_H
#define LAB_H

#include <string>
#include <cstdio>
#include <stdint.h>
#include "hash_map.h"

class Block {
public:
  Block(const char *data, int len) :
    data_(data), len_(len) { }
  const char *data() const { return data_; }
  int len() const { return len_; }

  ~Block() { delete[] data_; }

private:
  Block();
  const char *data_;
  int len_;
  bool owner_;
};

class Lab {
public:
  Lab() : f_(NULL) { }
  explicit Lab(const char *filename) : f_(NULL) { open(filename); }
  bool open(const char *filename);
  bool isOpen() const { return f_ != NULL; }
  void close();
  bool fileExists(const char *filename) const;
  Block *getFileBlock(const char *filename) const;
  std::FILE *openNewStream(const char *filename) const;
  int fileLength(const char *filename) const;

  ~Lab() { close(); }

private:
  struct LabEntry {
    LabEntry(int the_offset, int the_len) :
      offset(the_offset), len(the_len) { }
    int offset, len;
  };

  std::FILE *f_;
  typedef std::hash_map<std::string, LabEntry> file_map_type;
  file_map_type file_map_;

  file_map_type::const_iterator find_filename(const char *filename) const;
};

#endif

--- NEW FILE: localize.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "localize.h"
#include "registry.h"
#include "debug.h"
#include <cstdio>
#include <cstring>

Localizer *Localizer::instance_ = NULL;

Localizer *Localizer::instance() {
  if (instance_ == NULL)
    instance_ = new Localizer;
  return instance_;
}

Localizer::Localizer() {
  std::FILE *f;
  const char *namesToTry[] = { "/GRIM.TAB", "/Grim.tab", "/grim.tab" };

  for (unsigned i = 0; i < sizeof(namesToTry) / sizeof(namesToTry[0]); i++) {
    const char *datadir = Registry::instance()->get("DataDir");
    std::string fname = (datadir != NULL ? datadir : ".");
    fname += namesToTry[i];
    f = std::fopen(fname.c_str(), "rb");
    if (f != NULL)
      break;
  }
  if (f == NULL)
    return;

  // Get the file size
  std::fseek(f, 0, SEEK_END);
  long filesize = std::ftell(f);
  std::fseek(f, 0, SEEK_SET);

  // Read in the data
  char *data = new char[filesize + 1];
  std::fread(data, 1, filesize, f);
  data[filesize] = '\0';
  std::fclose(f);

  if (filesize < 4 || std::memcmp(data, "RCNE", 4) != 0)
    error("Invalid magic reading grim.tab\n");

  // Decode the data
  for (int i = 4; i < filesize; i++)
    data[i] ^= '\xdd';

  char *nextline;
  for (char *line = data + 4; line != NULL && *line != '\0'; line = nextline) {
    nextline = std::strchr(line, '\n');
    if (nextline != NULL) {
      if (nextline[-1] == '\r')
	nextline[-1] = '\0';
      nextline++;
    }
    char *tab = std::strchr(line, '\t');
    if (tab == NULL)
      continue;
    std::string key(line, tab - line);
    std::string val = tab + 1;
    entries_[key] = val;
  }

  delete[] data;
}

std::string Localizer::localize(const char *str) const {
  if (str[0] != '/')
    return str;
  const char *slash2 = std::strchr(str + 1, '/');
  if (slash2 == NULL)
    return str;

  std::string key(str + 1, slash2 - str - 1);
  string_map::const_iterator i = entries_.find(key);
  if (i == entries_.end())
    return str;

  return "/" + key + '/' + i->second;
}

--- NEW FILE: localize.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 LOCALIZE_H
#define LOCALIZE_H

#include "hash_map.h"

class Localizer {
public:
  static Localizer *instance();

  std::string localize(const char *str) const;

private:
  Localizer();
  ~Localizer() { }

  static Localizer *instance_;

  typedef std::hash_map<std::string, std::string> string_map;
  string_map entries_;
};

#endif

--- NEW FILE: lua.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "lua.h"
#include "resource.h"
[...1047 lines suppressed...]
  lua_pushobject(system_table);
  lua_pushstring(const_cast<char *>(name));
  lua_Object handler = lua_gettable();
  if (lua_isnil(handler))
    return LUA_NOOBJECT;
  if (lua_istable(handler)) {
    lua_pushobject(handler);	// Push handler object

    lua_pushobject(handler);	// For gettable
    lua_pushstring(const_cast<char *>(name));
    handler = lua_gettable();
    if (lua_isnil(handler))
      return LUA_NOOBJECT;
  }
  if (! lua_isfunction(handler)) {
    warning("Invalid event handler %s", name);
    return LUA_NOOBJECT;
  }
  return handler;
}

--- NEW FILE: lua.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 LUA_HH
#define LUA_HH

extern "C" {
#include <lua.h>
#include <lualib.h>
#include <luadebug.h>
#include <lauxlib.h>
}

// Register Residual builtin functions and structures
void register_lua();

// Like dofile, except it loads the file to execute from a bundle file
int bundle_dofile(const char *filename);

// Set system.frameTime
void set_frameTime(float frameTime);

// Get the event handler function with the given name, pushing the handler
// object if appropriate
lua_Object getEventHandler(const char *name);

#endif

--- NEW FILE: main.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include <SDL.h>
#include <SDL_video.h>
#include <SDL_opengl.h>
#include "bitmap.h"
#include "resource.h"
#include "debug.h"
#include "lua.h"
#include "registry.h"
#include "engine.h"
#include "mixer.h"
#include <unistd.h>

static void saveRegistry() {
  Registry::instance()->save();
}

int main(int /* argc */, char ** /* argv */) {
  if (SDL_Init(SDL_INIT_VIDEO) < 0)
    return 1;
  SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

  if (SDL_SetVideoMode(640, 480, 24, SDL_OPENGL) == 0)
    error("Could not initialize video");

  atexit(SDL_Quit);
  atexit(saveRegistry);

  Bitmap *splash_bm = ResourceLoader::instance()->loadBitmap("splash.bm");

  SDL_Event event;
  while (SDL_PollEvent(&event)) {
    if (event.type == SDL_VIDEOEXPOSE) {
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      Bitmap::prepareGL();
      splash_bm->draw();
      SDL_GL_SwapBuffers();
    }
  }

  Mixer::instance()->start();

  lua_open();

  lua_beginblock();
  lua_iolibopen();
  lua_strlibopen();
  lua_mathlibopen();
  lua_endblock();

  lua_beginblock();
  register_lua();
  lua_endblock();

  lua_beginblock();
  bundle_dofile("_system.lua");
  lua_endblock();

  lua_beginblock();
  lua_pushnil();		// resumeSave
  lua_pushnumber(0);		// bootParam
  lua_call("BOOT");
  lua_endblock();

  Engine::instance()->mainLoop();

  return 0;
}

--- NEW FILE: material.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "material.h"
#include "colormap.h"
#include "bits.h"
#include "debug.h"

Material::Material(const char *filename, const char *data, int len,
		   const Colormap &cmap) :
  Resource(filename)
{
  if (len < 4 || memcmp(data, "MAT ", 4) != 0)
    error("invalid magic loading texture\n");

  num_images_ = get_LE_uint32(data + 12);
  curr_image_ = 0;
  textures_ = new GLuint[num_images_];
  glGenTextures(num_images_, textures_);
  width_ = get_LE_uint32(data + 76 + num_images_ * 40);
  height_ = get_LE_uint32(data + 80 + num_images_ * 40);

  data += 100 + num_images_ * 40;
  char *texdata = new char[width_ * height_ * 4];
  for (int i = 0; i < num_images_; i++) {
    char *texdatapos = texdata;
    for (int y = 0; y < height_; y++) {
      for (int x = 0; x < width_; x++) {
	int col = get_uint8(data);
	if (col == 0)
	  memset(texdatapos, 0, 4); // transparent
	else {
	  memcpy(texdatapos, cmap.colors + 3 * get_uint8(data), 3);
	  texdatapos[3] = '\xff'; // fully opaque
	}
	texdatapos += 4;
	data++;
      }
    }
    glBindTexture(GL_TEXTURE_2D, textures_[i]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0,
		 GL_RGBA, GL_UNSIGNED_BYTE, texdata);
    data += 24;
  }

  delete[] texdata;
}

void Material::select() const {
  glBindTexture(GL_TEXTURE_2D, textures_[curr_image_]);
  glMatrixMode(GL_TEXTURE);
  glLoadIdentity();
  glScalef(1.0f / width_, 1.0f / height_, 1);
}

Material::~Material() {
  glDeleteTextures(num_images_, textures_);
  delete[] textures_;
}

--- NEW FILE: material.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 MATERIAL_H
#define MATERIAL_H

#include "resource.h"
#include <SDL.h>
#include <SDL_opengl.h>
#include <cstring>

class Colormap;

class Material : public Resource {
public:
  // Load a texture from the given data.
  Material(const char *filename, const char *data, int len,
	   const Colormap &cmap);

  // Load this texture into the GL context
  void select() const;

  // Set which image in an animated texture to use
  void setNumber(int n) { curr_image_ = n; }

  int numImages() const { return num_images_; }
  int currentImage() const { return curr_image_; }

  ~Material();

private:
  int num_images_, curr_image_;
  int width_, height_;
  GLuint *textures_;
};

#endif

--- NEW FILE: mixer.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "mixer.h"
#include "sound.h"
#include "debug.h"
#include <cstdlib>
#include <SDL_audio.h>

class AudioLock {
public:
  AudioLock() { SDL_LockAudio(); }
  ~AudioLock() { SDL_UnlockAudio(); }
};

struct imuseTableEntry {
  int stateNum;
  const char *filename;
};

static const imuseTableEntry grimMusicTable[] = {
  { 1001, "1001 - Manny's Office.IMC" },
  { 1002, "1002 - Mr. Frustration.IMC" },
  { 1003, "1002 - Mr. Frustration.IMC" },
  { 1004, "1002 - Mr. Frustration.IMC" },
  { 1005, "1002 - Mr. Frustration.IMC" },
  { 1006, "1002 - Mr. Frustration.IMC" },
  { 1007, "1002 - Mr. Frustration.IMC" },
  { 1008, "1008 - Domino's Office.IMC" },
  { 1009, "1009 - Copal's Office.IMC" },
  { 1010, "1010 - Ledge.IMC" },
  { 1011, "1011 - Roof.IMC" },
  { 1020, "1020 - Tube Room.IMC" },
  { 1021, "1021 - Brennis.IMC" },
  { 1022, "1022 - Lobby.IMC" },
  { 1023, "1023 - Packing Room.IMC" },
  { 1030, "1030 - Garage.IMC" },
  { 1031, "1031 - Glottis' Shop.IMC" },
  { 1032, "1030 - Garage.IMC" },
  { 1040, "1040 - Festival Wet.IMC" },
  { 1041, "1041 - Festival Dry.IMC" },
  { 1042, "1041 - Festival Dry.IMC" },
  { 1043, "1041 - Festival Dry.IMC" },
  { 1044, "1040 - Festival Wet.IMC" },
  { 1050, "1050 - Headquarters.IMC" },
  { 1060, "1060 - Real World.IMC" },
  { 1070, "1070 - Stump Room.IMC" },
  { 1071, "1071 - Signpost Room.IMC" },
  { 1072, "1072 - Navigation.IMC" },
  { 1073, "1071 - Signpost Room.IMC" },
  { 1074, "1074 - Bone Wagon.IMC" },
  { 1075, "1075 - Spider's Eye.IMC" },
  { 1076, "1076 - Spider Room.IMC" },
  { 1077, "1077 - Tree Pump Amb.IMC" },
  { 1078, "1078 - Tree Pump.IMC" },
  { 1079, "1071 - Signpost Room.IMC" },
  { 1080, "1080 - Beaver Room Lobby.IMC" },
  { 1081, "1081 - Beaver Dam.IMC" },
  { 1082, "1083 - Beaver Room.IMC" },
  { 1083, "1083 - Beaver Room.IMC" },
  { 1084, "1084 - Foggy Cactus.IMC" },
  { 1085, "1085 - Rubamat Exterior.IMC" },
  { 1086, "1086 - Blue Hector.IMC" },
  { 1100, "1109 - Cafe Exterior.IMC" },
  { 1101, "1101 - Cafe Office.IMC" },
  { 1102, "1102 - Cafe Intercom.IMC" },
  { 1103, "1103 - Coat Check.IMC" },
  { 1104, "1104 - Lupe.IMC" },
  { 1105, "1106 - Glottis Noodle.IMC" },
  { 1106, "1106 - Glottis Noodle.IMC" },
  { 1107, "1101 - Cafe Office.IMC" },
  { 1108, "1108 - Casino Interior.IMC" },
  { 1109, "1109 - Cafe Exterior.IMC" },
  { 1110, "1110 - Cafe Ledge.IMC" },
  { 1111, "1108 - Casino Interior.IMC" },
  { 1112, "1112 - Rusty Sans Vox.IMC" },
  { 1120, "1120 - Elevator Station.IMC" },
  { 1121, "1122 - Blue Exterior.IMC" },
  { 1122, "1122 - Blue Exterior.IMC" },
  { 1123, "1123 - Blue Casket Ins.IMC" },
  { 1124, "1124 - Blue Casket Amb.IMC" },
  { 1125, "1125 - Smooth Hector.IMC" },
  { 1126, "1122 - Blue Exterior.IMC" },
  { 1127, "1127 - Limbo Dock.IMC" },
  { 1128, "1128 - Limbo Talk.IMC" },
  { 1129, "1129 - Limbo Poem.IMC" },
  { 1130, "1130 - Dry Dock.IMC" },
  { 1131, "1131 - Dry Dock Strike.IMC" },
  { 1132, "1132 - Lighthouse Ext.IMC" },
  { 1133, "1133 - Lola's Last.IMC" },
  { 1140, "1140 - Police Station.IMC" },
  { 1141, "1141 - Police Interior.IMC" },
  { 1142, "1141 - Police Interior.IMC" },
  { 1143, "1143 - Morgue.IMC" },
  { 1144, "1140 - Police Station.IMC" },
  { 1145, "1145 - Bridge Blimp.IMC" },
  { 1146, "1146 - LOL Security Ext.IMC" },
  { 1147, "1147 - LOL Security Int.IMC" },
  { 1148, "1148 - Carla's Life.IMC" },
  { 1149, "1149 - Bomb.IMC" },
  { 1150, "1150 - Track Stairs.IMC" },
  { 1151, "1151 - Track Stairs.IMC" },
  { 1152, "1152 - Track Stairs.IMC" },
  { 1153, "1153 - Track Base.IMC" },
  { 1154, "1154 - Kitty Hall.IMC" },
  { 1155, "1155 - Sanspoof.IMC" },
  { 1156, "1156 - Kitty Stables.IMC" },
  { 1160, "1160 - High Roller Hall.IMC" },
  { 1161, "1161 - High Roller Lnge.IMC" },
  { 1162, "1162 - Glottis Gambling.IMC" },
  { 1163, "1163 - Max's Office.IMC" },
  { 1164, "1125 - Hector Steps Out.IMC" },
  { 1165, "1125 - Hector Steps Out.IMC" },
  { 1166, "1125 - Hector Steps Out.IMC" },
  { 1167, "1167 - Dillopede Elev.IMC" },
  { 1168, "1168 - Dillopede Elev.IMC" },
  { 1169, "1169 - Dillopede Elev.IMC" },
  { 1170, "1170 - Extendo Bridge.IMC" },
  { 1171, "1170 - Extendo Bridge.IMC" },
  { 1172, "1170 - Extendo Bridge.IMC" },
  { 1173, "1173 - Scrimshaw Int.IMC" },
  { 1174, "1174 - Scrim Sleep.IMC" },
  { 1180, "1180 - Note to Manny.IMC" },
  { 1181, "1155 - Sanspoof.IMC" },
  { 1190, "1106 - Glottis Noodle.IMC" },
  { 1191, "1106 - Glottis Noodle.IMC" },
  { 1201, "1201 - Lola Zapata.IMC" },
  { 1202, "1202 - Inside the Lola.IMC" },
  { 1203, "1203 - Engine Room.IMC" },
  { 1204, "1204 - Porthole.IMC" },
  { 1205, "1204 - Porthole.IMC" },
  { 1210, "1210 - Sunken Lola.IMC" },
  { 1211, "1211 - Pearl Crater Sub.IMC" },
  { 1220, "1220 - Miner's Room.IMC" },
  { 1221, "1221 - Miner's Room.IMC" },
  { 1222, "1222 - Exterior Airlock.IMC" },
  { 1223, "1223 - Factory Hub.IMC" },
  { 1224, "1224 - Foreman's Office.IMC" },
  { 1230, "1230 - Vault Door.IMC" },
  { 1231, "1231 - Outer Vault.IMC" },
  { 1232, "1232 - Inner Vault.IMC" },
  { 1233, "1233 - Ashtray Room.IMC" },
  { 1234, "1234 - Ashtray Scary.IMC" },
  { 1235, "1235 - Ashtray Pensive.IMC" },
  { 1236, "1236 - Domino's Room.IMC" },
  { 1240, "1240 - Conveyor Under.IMC" },
  { 1241, "1240 - Conveyor Under.IMC" },
  { 1242, "1241 - Crane Intro.IMC" },
  { 1243, "1243 - Anchor Room.IMC" },
  { 1244, "1244 - Glottis Hanging.IMC" },
  { 1245, "1245 - End of the World.IMC" },
  { 1246, "1246 - End World Later.IMC" },
  { 1247, "1241 - Crane Intro.IMC" },
  { 1250, "1250 - Upper Beach.IMC" },
  { 1251, "1250 - Upper Beach.IMC" },
  { 1252, "1252 - Lower Beach Boat.IMC" },
  { 1253, "1253 - Lamancha Sub.IMC" },
  { 1254, "1254 - Crane Later.IMC" },
  { 1301, "1301 - Temple Gate.IMC" },
  { 1302, "1301 - Temple Gate.IMC" },
  { 1303, "1303 - Truck Depot.IMC" },
  { 1304, "1304 - Mayan Train Sta.IMC" },
  { 1305, "1305 - Mayan Workshop.IMC" },
  { 1306, "1306 - Mayan Train Pad.IMC" },
  { 1307, "1307 - Mechanic's Kitch.IMC" },
  { 1310, "1310 - Jello Bomb.IMC" },
  { 1311, "1310 - Jello Bomb.IMC" },
  { 1312, "1125 - Smooth Hector.IMC" },
  { 1313, "1125 - Smooth Hector.IMC" },
  { 1314, "1125 - Smooth Hector.IMC" },
  { 1315, "1122 - Blue Exterior.IMC" },
  { 1316, "1122 - Blue Exterior.IMC" },
  { 1317, "1122 - Blue Exterior.IMC" },
  { 1318, "1332 - Hector's Foyer.IMC" },
  { 1319, "1319 - Florist Video.IMC" },
  { 1320, "1320 - New LSA HQ.IMC" },
  { 1321, "1321 - LSA Sewer.IMC" },
  { 1322, "1321 - LSA Sewer.IMC" },
  { 1323, "1323 - Sewer Maze.IMC" },
  { 1324, "1324 - Albinozod.IMC" },
  { 1325, "1325 - Florist Shop.IMC" },
  { 1326, "1326 - Florist Shop Int.IMC" },
  { 1327, "1327 - Florist OK.IMC" },
  { 1328, "1323 - Sewer Maze.IMC" },
  { 1329, "1329 - Theater Backstag.IMC" },
  { 1330, "1330 - Lemans Lobby.IMC" },
  { 1331, "1330 - Lemans Lobby.IMC" },
  { 1332, "1332 - Hector's Foyer.IMC" },
  { 1333, "1333 - Brennis Talk.IMC" },
  { 1334, "1334 - Albino Trap.IMC" },
  { 1340, "1342 - Neon Ledge.IMC" },
  { 1350, "1350 - Meadow Flowers.IMC" },
  { 1351, "1351 - Meadow.IMC" },
  { 1352, "1352 - Car Head.IMC" },
  { 1353, "1353 - Greenhouse Appr.IMC" },
  { 1354, "1354 - Game Ending.IMC" },
  { 1355, "1355 - Shootout.IMC" },
  { 1400, "1400 - Start Credits.IMC" },
  { 1401, "1401 - Smooth Hector.IMC" },
  { 2001, "2001 - Climb Rope.IMC" },
  { 2010, "2010 - Glottis OK.IMC" },
  { 2020, "2020 - Reap Bruno.IMC" },
  { 2030, "2030 - Ledgepeckers.IMC" },
  { 2050, "2050 - Glottis Heart.IMC" },
  { 2055, "2055 - Slingshot Bone.IMC" },
  { 2060, "2060 - Glott Tree Fall.IMC" },
  { 2070, "2070 - Beaver Fly.IMC" },
  { 2071, "2071 - Beaver Sink.IMC" },
  { 2080, "2080 - Meet Velasco.IMC" },
  { 2140, "2140 - Ooo Bonewagon.IMC" },
  { 2141, "2141 - Ooo Meche.IMC" },
  { 2155, "2155 - Find Detector.IMC" },
  { 2156, "2156 - Glott Drink Wine.IMC" },
  { 2157, "2157 - Glott No Wine.IMC" },
  { 2161, "2161 - Raoul Appears.IMC" },
  { 2162, "2162 - Raoul KO.IMC" },
  { 2163, "2163 - Raoul Dissed.IMC" },
  { 2165, "2165 - Fake Tix.IMC" },
  { 2180, "2180 - Befriend Commies.IMC" },
  { 2186, "2186 - Nick Punchout.IMC" },
  { 2200, "2200 - Year 3 Iris.IMC" },
  { 2210, "2210 - Hit Men.IMC" },
  { 2230, "2230 - Open Vault.IMC" },
  { 2235, "2235 - Dead Tix.IMC" },
  { 2240, "2240 - Sprinkler.IMC" },
  { 2250, "2250 - Crane Track.IMC" },
  { 2255, "2255 - Crane Fall.IMC" },
  { 2300, "2300 - Yr 4 Iris.IMC" },
  { 2301, "2301 - Pop Bruno Casket.IMC" },
  { 2310, "2310 - Rocket Idea.IMC" },
  { 2320, "2320 - Jello Suspense.IMC" },
  { 2325, "2325 - Lumbago Lemo.IMC" },
  { 2327, "2327 - Breath Mint.IMC" },
  { 2330, "2330 - Pigeon Fly.IMC" },
  { 2340, "2340 - Coffee On Boys.IMC" },
  { 2350, "2350 - Sprout Aha.IMC" },
  { 2360, "2360 - Chowchilla Bye.IMC" },
  { 2370, "2370 - Salvador Death.IMC" },
  { 2399, "2399 - End Credits.IMC" }
};

Mixer *Mixer::instance_ = NULL;

Mixer *Mixer::instance() {
  if (instance_ == NULL)
    instance_ = new Mixer;
  return instance_;
}

void mixerCallback(void *userdata, Uint8 *stream, int len) {
  Mixer *m = static_cast<Mixer *>(userdata);
  int16_t *samples = reinterpret_cast<int16_t *>(stream);
  m->getAudio(samples, len / 2);
}

Mixer::Mixer() :
  musicSound_(NULL), seqSound_(NULL)
{
}

void Mixer::start() {
  Sound::init();

  SDL_AudioSpec desired;
  desired.freq = 22050;
  desired.format = AUDIO_S16SYS;
  desired.channels = 2;
  desired.samples = 2048;
  desired.callback = mixerCallback;
  desired.userdata = this;
  SDL_OpenAudio(&desired, NULL);
  SDL_PauseAudio(0);
}

void Mixer::playVoice(Sound *s) {
  AudioLock l;

  s->reset();
  voiceSounds_.push_back(s);
}

void Mixer::playSfx(Sound *s) {
  AudioLock l;

  s->reset();
  sfxSounds_.push_back(s);
}

void Mixer::stopSfx(Sound *s) {
  AudioLock l;

  for (sound_list::iterator i = sfxSounds_.begin();
       i != sfxSounds_.end(); ) {
    if (*i == s)
      i = sfxSounds_.erase(i);
    else
      i++;
  }
}

static int compareStates(const void *p1, const void *p2) {
  const imuseTableEntry *e1 = static_cast<const imuseTableEntry *>(p1);
  const imuseTableEntry *e2 = static_cast<const imuseTableEntry *>(p2);
  return e1->stateNum - e2->stateNum;
}

void Mixer::setImuseState(int state) {
  Sound *newSound = NULL;

  if (state != 1000) {
    imuseTableEntry key;
    key.stateNum = state;
    const imuseTableEntry *e = static_cast<imuseTableEntry *>
      (std::bsearch(&key, grimMusicTable,
		    sizeof(grimMusicTable) / sizeof(grimMusicTable[0]),
		    sizeof(grimMusicTable[0]), compareStates));
    if (e == NULL) {
      warning("Unknown IMuse state %d\n", state);
      return;
    }

    newSound = ResourceLoader::instance()->loadSound(e->filename);
    if (newSound == NULL) {
      warning("Could not find music file %s\n", e->filename);
      return;
    }
  }

  AudioLock l;
  if (newSound != musicSound_) {
    if (newSound != NULL)
      newSound->reset();
    musicSound_ = newSound;
  }
}

void Mixer::setImuseSeq(int state) {
  Sound *newSound = NULL;

  if (state != 2000) {
    imuseTableEntry key;
    key.stateNum = state;
    const imuseTableEntry *e = static_cast<imuseTableEntry *>
      (std::bsearch(&key, grimMusicTable,
		    sizeof(grimMusicTable) / sizeof(grimMusicTable[0]),
		    sizeof(grimMusicTable[0]), compareStates));
    if (e == NULL) {
      warning("Unknown IMuse state %d\n", state);
      return;
    }

    Sound *newSound = ResourceLoader::instance()->loadSound(e->filename);
    if (newSound == NULL) {
      warning("Could not find music file %s\n", e->filename);
      return;
    }
  }

  AudioLock l;
  if (newSound != seqSound_) {
    if (newSound != NULL)
      newSound->reset();
    seqSound_ = newSound;
  }
}

Sound *Mixer::findSfx(const char *filename) {
  AudioLock l;

  for (sound_list::iterator i = sfxSounds_.begin();
       i != sfxSounds_.end(); i++) {
    if (std::strcmp((*i)->filename(), filename) == 0)
      return *i;
  }
  return NULL;
}

bool Mixer::voicePlaying() const {
  AudioLock l;

  return ! voiceSounds_.empty();
}

void Mixer::getAudio(int16_t *data, int numSamples) {
  memset(data, 0, numSamples * 2);
  for (sound_list::iterator i = voiceSounds_.begin();
       i != voiceSounds_.end(); ) {
    (*i)->mix(data, numSamples);
    if ((*i)->done())
      i = voiceSounds_.erase(i);
    else
      i++;
  }
  for (sound_list::iterator i = sfxSounds_.begin();
       i != sfxSounds_.end(); ) {
    (*i)->mix(data, numSamples);
    if ((*i)->done())
      i = sfxSounds_.erase(i);
    else
      i++;
  }
  if (seqSound_ != NULL) {
    seqSound_->mix(data, numSamples);
    if (seqSound_->done())
      seqSound_ = NULL;
  }
  else if (musicSound_ != NULL) {
    musicSound_->mix(data, numSamples);
    if (musicSound_->done())
      musicSound_->reset();
  }
}

--- NEW FILE: mixer.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 MIXER_H
#define MIXER_H

#include "resource.h"
#include <list>
#include <SDL/SDL_audio.h>

class Sound;

class Mixer {
public:
  static Mixer *instance();

  void start();

  void playVoice(Sound *s);
  void playSfx(Sound *s);
  void stopSfx(Sound *s);
  void setImuseState(int state);
  void setImuseSeq(int seq);

  Sound *findSfx(const char *filename);
  bool voicePlaying() const;

private:
  Mixer();
  void getAudio(int16_t *data, int numSamples);

  static Mixer *instance_;
  typedef std::list<ResPtr<Sound> > sound_list;
  sound_list voiceSounds_, sfxSounds_;
  ResPtr<Sound> musicSound_, seqSound_;

  friend void mixerCallback(void *userdata, Uint8 *stream, int len);
};

#endif

--- NEW FILE: model.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "model.h"
#include "bits.h"
#include "resource.h"
#include "material.h"
#include "debug.h"
#include "textsplit.h"
#include <cstring>
#include <SDL.h>
#include <SDL_opengl.h>

Model::Model(const char *filename, const char *data, int len,
	     const Colormap &cmap) : Resource(filename)
{
  if (len >= 4 && std::memcmp(data, "LDOM", 4) == 0)
    loadBinary(data, cmap);
  else {
    TextSplitter ts(data, len);
    loadText(ts, cmap);
  }
}

void Model::loadBinary(const char *data, const Colormap &cmap) {
  numMaterials_ = get_LE_uint32(data + 4);
  data += 8;
  materials_ = new ResPtr<Material>[numMaterials_];
  for (int i = 0; i < numMaterials_; i++) {
    materials_[i] = ResourceLoader::instance()->loadMaterial(data, cmap);
    data += 32;
  }
  data += 32;			// skip name
  numGeosets_ = get_LE_uint32(data + 4);
  data += 8;
  geosets_ = new Geoset[numGeosets_];
  for (int i = 0; i < numGeosets_; i++)
    geosets_[i].loadBinary(data, materials_);
  numHierNodes_ = get_LE_uint32(data + 4);
  data += 8;
  rootHierNode_ = new HierNode[numHierNodes_];
  for (int i = 0; i < numHierNodes_; i++)
    rootHierNode_[i].loadBinary(data, rootHierNode_, geosets_[0]);
  radius_ = get_float(data);
  insertOffset_ = get_vector3d(data + 40);
}

Model::~Model() {
  delete[] materials_;
  delete[] geosets_;
  delete[] rootHierNode_;
}

void Model::Geoset::loadBinary(const char *&data,
			       ResPtr<Material> *materials) {
  numMeshes_ = get_LE_uint32(data);
  data += 4;
  meshes_ = new Mesh[numMeshes_];
  for (int i = 0; i < numMeshes_; i++)
    meshes_[i].loadBinary(data, materials);
}

Model::Geoset::~Geoset() {
  delete[] meshes_;
}

void Model::Mesh::loadBinary(const char *&data,
			     ResPtr<Material> *materials) {
  memcpy(name_, data, 32);
  geometryMode_ = get_LE_uint32(data + 36);
  lightingMode_ = get_LE_uint32(data + 40);
  textureMode_ = get_LE_uint32(data + 44);
  numVertices_ = get_LE_uint32(data + 48);
  numTextureVerts_ = get_LE_uint32(data + 52);
  numFaces_ = get_LE_uint32(data + 56);
  vertices_ = new float[3 * numVertices_];
  verticesI_ = new float[numVertices_];
  vertNormals_ = new float[3 * numVertices_];
  textureVerts_ = new float[2 * numTextureVerts_];
  data += 60;
  for (int i = 0; i < 3 * numVertices_; i++) {
    vertices_[i] = get_float(data);
    data += 4;
  }
  for (int i = 0; i < 2 * numTextureVerts_; i++) {
    textureVerts_[i] = get_float(data);
    data += 4;
  }
  for (int i = 0; i < numVertices_; i++) {
    verticesI_[i] = get_float(data);
    data += 4;
  }
  data += numVertices_ * 4;
  faces_ = new Face[numFaces_];
  for (int i = 0; i < numFaces_; i++)
    faces_[i].loadBinary(data, materials);
  vertNormals_ = new float[3 * numVertices_];
  for (int i = 0; i < 3 * numVertices_; i++) {
    vertNormals_[i] = get_float(data);
    data += 4;
  }
  shadow_ = get_LE_uint32(data);
  radius_ = get_float(data + 8);
  data += 36;
}

Model::Mesh::~Mesh() {
  delete[] vertices_;
  delete[] verticesI_;
  delete[] vertNormals_;
  delete[] textureVerts_;
  delete[] faces_;
}

void Model::Face::loadBinary(const char *&data, ResPtr<Material> *materials) {
  type_ = get_LE_uint32(data + 4);
  geo_ = get_LE_uint32(data + 8);
  light_ = get_LE_uint32(data + 12);
  tex_ = get_LE_uint32(data + 16);
  numVertices_ = get_LE_uint32(data + 20);
  int texPtr = get_LE_uint32(data + 28);
  int materialPtr = get_LE_uint32(data + 32);
  extraLight_ = get_float(data + 48);
  normal_ = get_vector3d(data + 64);
  data += 76;

  vertices_ = new int[numVertices_];
  for (int i = 0; i < numVertices_; i++) {
    vertices_[i] = get_LE_uint32(data);
    data += 4;
  }
  if (texPtr == 0)
    texVertices_ = NULL;
  else {
    texVertices_ = new int[numVertices_];
    for (int i = 0; i < numVertices_; i++) {
      texVertices_[i] = get_LE_uint32(data);
      data += 4;
    }
  }
  if (materialPtr == 0)
    material_ = 0;
  else {
    material_ = materials[get_LE_uint32(data)];
    data += 4;
  }
}

Model::Face::~Face() {
  delete[] vertices_;
  delete[] texVertices_;
}

void Model::HierNode::loadBinary(const char *&data,
				 Model::HierNode *hierNodes,
				 const Geoset &g) {
  memcpy(name_, data, 64);
  flags_ = get_LE_uint32(data + 64);
  type_ = get_LE_uint32(data + 72);
  int meshNum = get_LE_uint32(data + 76);
  if (meshNum < 0)
    mesh_ = NULL;
  else
    mesh_ = g.meshes_ + meshNum;
  depth_ = get_LE_uint32(data + 80);
  int parentPtr = get_LE_uint32(data + 84);
  numChildren_ = get_LE_uint32(data + 88);
  int childPtr = get_LE_uint32(data + 92);
  int siblingPtr = get_LE_uint32(data + 96);
  pivot_ = get_vector3d(data + 100);
  pos_ = get_vector3d(data + 112);
  pitch_ = get_float(data + 124);
  yaw_ = get_float(data + 128);
  roll_ = get_float(data + 132);
  data += 184;

  if (parentPtr != 0) {
    parent_ = hierNodes + get_LE_uint32(data);
    data += 4;
  }
  else
    parent_ = NULL;
  if (childPtr != 0) {
    child_ = hierNodes + get_LE_uint32(data);
    data += 4;
  }
  else
    child_ = NULL;
  if (siblingPtr != 0) {
    sibling_ = hierNodes + get_LE_uint32(data);
    data += 4;
  }
  else
    sibling_ = NULL;

  meshVisible_ = hierVisible_ = true;
  totalWeight_ = 1;
}

void Model::draw() const {
  rootHierNode_->draw();
}

Model::HierNode *Model::copyHierarchy() {
  HierNode *result = new HierNode[numHierNodes_];
  std::memcpy(result, rootHierNode_, numHierNodes_ * sizeof(HierNode));
  // Now adjust pointers
  for (int i = 0; i < numHierNodes_; i++) {
    if (result[i].parent_ != NULL)
      result[i].parent_ = result + (rootHierNode_[i].parent_ - rootHierNode_);
    if (result[i].child_ != NULL)
      result[i].child_ = result + (rootHierNode_[i].child_ - rootHierNode_);
    if (result[i].sibling_ != NULL)
      result[i].sibling_ = result + (rootHierNode_[i].sibling_ -
				     rootHierNode_);
  }
  return result;
}

void Model::loadText(TextSplitter &ts, const Colormap &cmap) {
  ts.expectString("section: header");
  int major, minor;
  ts.scanString("3do %d.%d", 2, &major, &minor);

  ts.expectString("section: modelresource");
  ts.scanString("materials %d", 1, &numMaterials_);
  materials_ = new ResPtr<Material>[numMaterials_];
  for (int i = 0; i < numMaterials_; i++) {
    int num;
    char name[32];
    ts.scanString("%d: %32s", 2, &num, name);
    materials_[num] = ResourceLoader::instance()->loadMaterial(name, cmap);
  }

  ts.expectString("section: geometrydef");
  ts.scanString("radius %f", 1, &radius_);
  ts.scanString("insert offset %f %f %f", 3,
		&insertOffset_.x(), &insertOffset_.y(), &insertOffset_.z());
  ts.scanString("geosets %d", 1, &numGeosets_);
  geosets_ = new Geoset[numGeosets_];
  for (int i = 0; i < numGeosets_; i++) {
    int num;
    ts.scanString("geoset %d", 1, &num);
    geosets_[num].loadText(ts, materials_);
  }

  ts.expectString("section: hierarchydef");
  ts.scanString("hierarchy nodes %d", 1, &numHierNodes_);
  rootHierNode_ = new HierNode[numHierNodes_];
  for (int i = 0; i < numHierNodes_; i++) {
    int num, flags, type, mesh, parent, child, sibling, numChildren;
    float x, y, z, pitch, yaw, roll, pivotx, pivoty, pivotz;
    char name[64];
    ts.scanString(" %d: %i %i %d %d %d %d %d %f %f %f %f %f %f %f %f %f %64s",
		  18, &num, &flags, &type, &mesh, &parent, &child, &sibling,
		  &numChildren, &x, &y, &z, &pitch, &yaw, &roll,
		  &pivotx, &pivoty, &pivotz, name);
    rootHierNode_[num].flags_ = flags;
    rootHierNode_[num].type_ = type;
    if (mesh < 0)
      rootHierNode_[num].mesh_ = NULL;
    else
      rootHierNode_[num].mesh_ = geosets_[0].meshes_ + mesh;
    if (parent >= 0) {
      rootHierNode_[num].parent_ = rootHierNode_ + parent;
      rootHierNode_[num].depth_ = rootHierNode_[parent].depth_ + 1;
    }
    else {
      rootHierNode_[num].parent_ = NULL;
      rootHierNode_[num].depth_ = 0;
    }
    if (child >= 0)
      rootHierNode_[num].child_ = rootHierNode_ + child;
    else
      rootHierNode_[num].child_ = NULL;
    if (sibling >= 0)
      rootHierNode_[num].sibling_ = rootHierNode_ + sibling;
    else
      rootHierNode_[num].sibling_ = NULL;
    rootHierNode_[num].numChildren_ = numChildren;
    rootHierNode_[num].pos_ = Vector3d(x, y, z);
    rootHierNode_[num].pitch_ = pitch;
    rootHierNode_[num].yaw_ = yaw;
    rootHierNode_[num].roll_ = roll;
    rootHierNode_[num].pivot_ = Vector3d(pivotx, pivoty, pivotz);

    rootHierNode_[num].meshVisible_ =
      rootHierNode_[num].hierVisible_ = true;
    rootHierNode_[num].totalWeight_ = 1;
  }
  if (! ts.eof())
    warning("Unexpected junk at end of model text\n");
}

void Model::Geoset::loadText(TextSplitter &ts, ResPtr<Material> *materials) {
  ts.scanString("meshes %d", 1, &numMeshes_);
  meshes_ = new Mesh[numMeshes_];
  for (int i = 0; i < numMeshes_; i++) {
    int num;
    ts.scanString("mesh %d", 1, &num);
    meshes_[num].loadText(ts, materials);
  }
}

void Model::Mesh::loadText(TextSplitter &ts, ResPtr<Material> *materials) {
  ts.scanString("name %32s", 1, name_);
  ts.scanString("radius %f", 1, &radius_);

  // In data001/rope_scale.3do, the shadow line is missing
  if (std::sscanf(ts.currentLine(), "shadow %d", &shadow_) < 1) {
    shadow_ = 0;
    warning("Missing shadow directive in model\n");
  }
  else
    ts.nextLine();
  ts.scanString("geometrymode %d", 1, &geometryMode_);
  ts.scanString("lightingmode %d", 1, &lightingMode_);
  ts.scanString("texturemode %d", 1, &textureMode_);
  ts.scanString("vertices %d", 1, &numVertices_);
  vertices_ = new float[3 * numVertices_];
  verticesI_ = new float[numVertices_];
  vertNormals_ = new float[3 * numVertices_];

  for (int i = 0; i < numVertices_; i++) {
    int num;
    float x, y, z, ival;
    ts.scanString(" %d: %f %f %f %f", 5, &num, &x, &y, &z, &ival);
    vertices_[3 * num] = x;
    vertices_[3 * num + 1] = y;
    vertices_[3 * num + 2] = z;
    verticesI_[num] = ival;
  }

  ts.scanString("texture vertices %d", 1, &numTextureVerts_);
  textureVerts_ = new float[2 * numTextureVerts_];

  for (int i = 0; i < numTextureVerts_; i++) {
    int num;
    float x, y;
    ts.scanString(" %d: %f %f", 3, &num, &x, &y);
    textureVerts_[2 * num] = x;
    textureVerts_[2 * num + 1] = y;
  }

  ts.expectString("vertex normals");
  for (int i = 0; i < numVertices_; i++) {
    int num;
    float x, y, z;
    ts.scanString(" %d: %f %f %f", 4, &num, &x, &y, &z);
    vertNormals_[3 * num] = x;
    vertNormals_[3 * num + 1] = y;
    vertNormals_[3 * num + 2] = z;
  }

  ts.scanString("faces %d", 1, &numFaces_);
  faces_ = new Face[numFaces_];
  for (int i = 0; i < numFaces_; i++) {
    int num, material, type, geo, light, tex, verts;
    float extralight;
    int readlen;
    if (ts.eof())
      error("Expected face data, got EOF\n");
    if (std::sscanf(ts.currentLine(), " %d: %d %i %d %d %d %f %d%n",
		    &num, &material, &type, &geo, &light, &tex, &extralight,
		    &verts, &readlen) < 8)
      error("Expected face data, got `%s'\n", ts.currentLine());
    faces_[num].material_ = materials[material];
    faces_[num].type_ = type;
    faces_[num].geo_ = geo;
    faces_[num].light_ = light;
    faces_[num].tex_ = tex;
    faces_[num].extraLight_ = extralight;
    faces_[num].numVertices_ = verts;
    faces_[num].vertices_ = new int[verts];
    faces_[num].texVertices_ = new int[verts];
    for (int j = 0; j < verts; j++) {
      int readlen2;
      if (std::sscanf(ts.currentLine() + readlen, " %d, %d%n",
		      faces_[num].vertices_ + j,
		      faces_[num].texVertices_ + j, &readlen2) < 2)
	error("Could not read vertex indices in line `%s'\n",
	      ts.currentLine());
      readlen += readlen2;
    }
    ts.nextLine();
  }

  ts.expectString("face normals");
  for (int i = 0; i < numFaces_; i++) {
    int num;
    float x, y, z;
    ts.scanString(" %d: %f %f %f", 4, &num, &x, &y, &z);
    faces_[num].normal_ = Vector3d(x, y, z);
  }
}

void Model::HierNode::draw() const {
  if (hierVisible_) {
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();

    glTranslatef(animPos_.x() / totalWeight_, animPos_.y() / totalWeight_,
		 animPos_.z() / totalWeight_);
    glRotatef(animYaw_ / totalWeight_, 0, 0, 1);
    glRotatef(animPitch_ / totalWeight_, 1, 0, 0);
    glRotatef(animRoll_ / totalWeight_, 0, 1, 0);

    if (mesh_ != NULL && meshVisible_) {
      glPushMatrix();
      glTranslatef(pivot_.x(), pivot_.y(), pivot_.z());
      mesh_->draw();
      glMatrixMode(GL_MODELVIEW);
      glPopMatrix();
    }
    if (child_ != NULL) {
      child_->draw();
      glMatrixMode(GL_MODELVIEW);
    }
    glPopMatrix();
  }
  if (sibling_ != NULL)
    sibling_->draw();
}

void Model::HierNode::addChild(HierNode *child) {
  HierNode **childPos = &child_;
  while (*childPos != NULL)
    childPos = &(*childPos)->sibling_;
  *childPos = child;
  child->parent_ = this;
}

void Model::HierNode::removeChild(HierNode *child) {
  HierNode **childPos = &child_;
  while (*childPos != NULL && *childPos != child)
    childPos = &(*childPos)->sibling_;
  if (*childPos != NULL) {
    *childPos = child->sibling_;
    child->parent_ = NULL;
  }
}

void Model::Mesh::draw() const {
  for (int i = 0; i < numFaces_; i++)
    faces_[i].draw(vertices_, vertNormals_, textureVerts_);
}

void Model::Face::draw(float *vertices, float *vertNormals,
		       float *textureVerts) const {
  material_->select();
  glNormal3fv(normal_.coords_);
  glBegin(GL_POLYGON);
  for (int i = 0; i < numVertices_; i++) {
    glNormal3fv(vertNormals + 3 * vertices_[i]);
    if (texVertices_ != NULL)
      glTexCoord2fv(textureVerts + 2 * texVertices_[i]);
    glVertex3fv(vertices + 3 * vertices_[i]);
  }
  glEnd();
}

--- NEW FILE: model.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 MODEL_H
#define MODEL_H

#include "vector3d.h"
#include "resource.h"
#include <cstring>

class Colormap;
class Material;
class TextSplitter;

class Model : public Resource {
public:
  // Construct a 3D model from the given data.
  Model(const char *filename, const char *data, int len, const Colormap &cmap);
  void loadBinary(const char *data, const Colormap &cmap);
  void loadText(TextSplitter &ts, const Colormap &cmap);

  void draw() const;

  ~Model();

  struct Geoset;
  struct Mesh;
  struct HierNode {
    void loadBinary(const char *&data, HierNode *hierNodes, const Geoset &g);
    void draw() const;
    void addChild(HierNode *child);
    void removeChild(HierNode *child);

    char name_[64];
    Mesh *mesh_;
    int flags_, type_;
    int depth_, numChildren_;
    HierNode *parent_, *child_, *sibling_;
    Vector3d pos_, pivot_;
    float pitch_, yaw_, roll_;
    Vector3d animPos_;
    float animPitch_, animYaw_, animRoll_;
    bool meshVisible_, hierVisible_;
    int priority_, totalWeight_;
  };

  HierNode *copyHierarchy();
  int numNodes() const { return numHierNodes_; }

private:
  struct Face {
    void loadBinary(const char *&data, ResPtr<Material> *materials);
    void draw(float *vertices, float *vertNormals, float *textureVerts) const;
    ~Face();

    Material *material_;
    int type_, geo_, light_, tex_;
    float extraLight_;
    int numVertices_;
    int *vertices_, *texVertices_;
    Vector3d normal_;
  };

  struct Mesh {
    void loadBinary(const char *&data, ResPtr<Material> *materials);
    void loadText(TextSplitter &ts, ResPtr<Material> *materials);
    void draw() const;
    ~Mesh();

    char name_[32];
    float radius_;
    int shadow_, geometryMode_, lightingMode_, textureMode_;

    int numVertices_;
    float *vertices_;		// sets of 3
    float *verticesI_;
    float *vertNormals_;	// sets of 3

    int numTextureVerts_;
    float *textureVerts_;	// sets of 2

    int numFaces_;
    Face *faces_;
  };

  struct Geoset {
    void loadBinary(const char *&data, ResPtr<Material> *materials);
    void loadText(TextSplitter &ts, ResPtr<Material> *materials);
    ~Geoset();

    int numMeshes_;
    Mesh *meshes_;
  };

  int numMaterials_;
  ResPtr<Material> *materials_;
  Vector3d insertOffset_;
  int numGeosets_;
  Geoset *geosets_;
  float radius_;
  int numHierNodes_;
  HierNode *rootHierNode_;
};

#endif

--- NEW FILE: registry.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "registry.h"
#include "debug.h"
#include <cstdlib>

Registry::Registry() : dirty_(false) {
  #ifdef WIN32
    std::string filename = "C:\residual.ini";
  #else
    std::string filename = std::string(std::getenv("HOME")) + "/.residualrc";
  #endif

  std::FILE *f = fopen(filename.c_str(), "r");
  if (f != NULL) {
    char line[1024];
    while (!feof(f) && fgets(line, sizeof(line), f) != NULL) {
      char *equals = std::strchr(line, '=');
      char *newline = std::strchr(line, '\n');
      if (newline != NULL)
	*newline = '\0';
      if (equals != NULL) {
	std::string key = std::string(line, equals - line);
	std::string val = std::string(equals + 1);
	settings_[key] = val;
      }
    }
    std::fclose(f);
  }
}

Registry *Registry::instance_ = NULL;

Registry *Registry::instance() {
  if (instance_ == NULL)
    instance_ = new Registry;
  return instance_;
}

const char *Registry::get(const char *key) const {
  group::const_iterator i = settings_.find(key);
  if (i == settings_.end())
    return NULL;
  else
    return i->second.c_str();
}

void Registry::set(const char *key, const char *val) {
  settings_[key] = val;
  dirty_ = true;
}

void Registry::save() {
  if (! dirty_)
    return;

  #ifdef WIN32
    std::string filename = "C:\residual.ini";
  #else
    std::string filename = std::string(std::getenv("HOME")) + "/.residualrc";
  #endif

  std::FILE *f = std::fopen(filename.c_str(), "w");
  if (f == NULL) {
    warning("Could not open registry file %s for writing\n",
	    filename.c_str());
    return;
  }

  for (group::iterator i = settings_.begin(); i != settings_.end(); i++)
    std::fprintf(f, "%s=%s\n", i->first.c_str(), i->second.c_str());
  std::fclose(f);
  dirty_ = false;
}

--- NEW FILE: registry.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 REGISTRY_HH
#define REGISTRY_HH

#include <string>
#include <map>

class Registry {
public:
  static Registry *instance();

  const char * get(const char *key) const;
  void set(const char *key, const char *val);
  void save();

private:
  Registry();
  ~Registry() { }

  static Registry *instance_;

  typedef std::map<std::string, std::string> group;
  group settings_;
  bool dirty_;
};

#endif

--- NEW FILE: resource.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "resource.h"
#include "registry.h"
#include "bitmap.h"
#include "colormap.h"
#include "costume.h"
#include "keyframe.h"
#include "material.h"
#include "model.h"
#include "sound.h"
#include "debug.h"
#include <dirent.h>
#include <cstring>
#include <cctype>
#include <string>
#include <algorithm>

static void makeLower(std::string& s) {
  std::transform(s.begin(), s.end(), s.begin(), tolower);
}

ResourceLoader *ResourceLoader::instance_ = NULL;

ResourceLoader::ResourceLoader() {
  const char *directory = Registry::instance()->get("DataDir");
  std::string dir_str = (directory != NULL ? directory : ".");
  dir_str += '/';
  int lab_counter = 0;
  DIR *d = opendir(dir_str.c_str());

  if (directory == NULL) 
    error("Cannot find DataDir registry entry - check configuration file");

  if (d == NULL)
    error("Cannot open DataDir (%s)- check configuration file", dir_str.c_str());

  printf("dir open\n");
  dirent *de;
  while ((de = readdir(d)) != NULL) {
    int namelen = strlen(de->d_name);
    if (namelen > 4 &&
	strcasecmp(de->d_name + namelen - 4, ".lab") == 0) {
      std::string fullname = dir_str + de->d_name;

      Lab *l = new Lab(fullname.c_str());
      lab_counter++;
      if (l->isOpen())
	labs_.push_back(l);
      else
	delete l;
    }
  }
  closedir(d);

  if (lab_counter == 0)
	error("Cannot find any resource files in %s - check configuration file", dir_str.c_str());
}

const Lab *ResourceLoader::findFile(const char *filename) const {
  for (LabList::const_iterator i = labs_.begin(); i != labs_.end(); i++)
    if ((*i)->fileExists(filename))
      return *i;
  return NULL;
}

bool ResourceLoader::fileExists(const char *filename) const {
  return findFile(filename) != NULL;
}

Block *ResourceLoader::getFileBlock(const char *filename) const {
  const Lab *l = findFile(filename);
  if (l == NULL)
    return NULL;
  else
    return l->getFileBlock(filename);
}

//std::FILE *ResourceLoader::openNewStream(const char *filename) const {
//  const Lab *l = findFile(filename);
//  if (l == NULL)
//    return NULL;
//  else
//    return l->openNewStream(filename);
//}

int ResourceLoader::fileLength(const char *filename) const {
  const Lab *l = findFile(filename);
  if (l == NULL)
    return 0;
  else
    return l->fileLength(filename);
}

Bitmap *ResourceLoader::loadBitmap(const char *filename) {
  std::string fname = filename;
  makeLower(fname);
  cache_type::iterator i = cache_.find(fname);
  if (i != cache_.end()) {
    return dynamic_cast<Bitmap *>(i->second);
  }

  Block *b = getFileBlock(filename);
  if (b == NULL)
    error("Could not find bitmap %s\n", filename);
  Bitmap *result = new Bitmap(filename, b->data(), b->len());
  delete b;
  cache_[fname] = result;
  return result;
}

Colormap *ResourceLoader::loadColormap(const char *filename) {
  std::string fname = filename;
  makeLower(fname);
  cache_type::iterator i = cache_.find(fname);
  if (i != cache_.end()) {
    return dynamic_cast<Colormap *>(i->second);
  }

  Block *b = getFileBlock(filename);
  if (b == NULL)
    error("Could not find colormap %s\n", filename);
  Colormap *result = new Colormap(filename, b->data(), b->len());
  delete b;
  cache_[fname] = result;
  return result;
}

Costume *ResourceLoader::loadCostume(const char *filename) {
  std::string fname = filename;
  makeLower(fname);
  cache_type::iterator i = cache_.find(fname);
  if (i != cache_.end()) {
    return dynamic_cast<Costume *>(i->second);
  }

  Block *b = getFileBlock(filename);
  if (b == NULL)
    error("Could not find colormap %s\n", filename);
  Costume *result = new Costume(filename, b->data(), b->len());
  delete b;
  cache_[fname] = result;
  return result;
}

KeyframeAnim *ResourceLoader::loadKeyframe(const char *filename) {
  std::string fname = filename;
  makeLower(fname);
  cache_type::iterator i = cache_.find(fname);
  if (i != cache_.end()) {
    return dynamic_cast<KeyframeAnim *>(i->second);
  }

  Block *b = getFileBlock(filename);
  if (b == NULL)
    error("Could not find keyframe file %s\n", filename);
  KeyframeAnim *result = new KeyframeAnim(filename, b->data(), b->len());
  delete b;
  cache_[fname] = result;
  return result;
}

Material *ResourceLoader::loadMaterial(const char *filename,
				       const Colormap &c) {
  std::string fname = filename;
  makeLower(fname);
  cache_type::iterator i = cache_.find(fname);
  if (i != cache_.end()) {
    return dynamic_cast<Material *>(i->second);
  }

  Block *b = getFileBlock(filename);
  if (b == NULL)
    error("Could not find material %s\n", filename);
  Material *result = new Material(filename, b->data(), b->len(), c);
  delete b;
  cache_[fname] = result;
  return result;
}

Model *ResourceLoader::loadModel(const char *filename, const Colormap &c) {
  std::string fname = filename;
  makeLower(fname);
  cache_type::iterator i = cache_.find(fname);
  if (i != cache_.end()) {
    return dynamic_cast<Model *>(i->second);
  }

  Block *b = getFileBlock(filename);
  if (b == NULL)
    error("Could not find model %s\n", filename);
  Model *result = new Model(filename, b->data(), b->len(), c);
  delete b;
  cache_[fname] = result;
  return result;
}

Sound *ResourceLoader::loadSound(const char *filename) {
  std::string fname = filename;
  makeLower(fname);
  cache_type::iterator i = cache_.find(fname);
  if (i != cache_.end()) {
    return dynamic_cast<Sound *>(i->second);
  }

  Block *b = getFileBlock(filename);
  if (b == NULL)
    return NULL;
  Sound *result = new Sound(filename, b->data(), b->len());
  delete b;
  cache_[fname] = result;
  return result;
}

void ResourceLoader::uncache(const char *filename) {
  std::string fname = filename;
  makeLower(fname);
  cache_type::iterator i = cache_.find(fname);
  if (i != cache_.end())
    cache_.erase(i);
}

--- NEW FILE: resource.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 RESOURCE_H
#define RESOURCE_H

#include "lab.h"
#include "hash_map.h"
#include <list>
#include <string>

class Bitmap;
class Colormap;
class Costume;
class KeyframeAnim;
class Material;
class Model;
class Sound;

class Resource {
public:
  Resource(const char *filename) :
    fname_(filename), ref_(0), luaRef_(false) { }
  Resource(const Resource &r) : fname_(r.fname_), ref_(0), luaRef_(false)
  { }
  virtual ~Resource() { }
  void ref() { ref_++; }
  void deref();
  const char *filename() const { return fname_.c_str(); }

  void luaRef() { if (! luaRef_) { ref(); luaRef_ = true; } }
  void luaGc() { if (luaRef_) { luaRef_ = false; deref(); } }

private:
  std::string fname_;
  int ref_;
  bool luaRef_;
};

template <class T>
class ResPtr {
public:
  ResPtr() { ptr_ = NULL; }
  ResPtr(const ResPtr &p) { ptr_ = p.ptr_; if (ptr_ != NULL) ptr_->ref(); }
  ResPtr(T* ptr) { ptr_ = ptr; if (ptr_ != NULL) ptr_->ref(); }
  operator T*() { return ptr_; }
  T& operator *() { return *ptr_; }
  T* operator ->() { return ptr_; }
  ResPtr& operator =(T* ptr) {
    if (ptr_ != NULL) ptr_->deref();
    ptr_ = ptr;
    if (ptr_ != NULL) ptr_->ref();
    return *this;
  }
  ResPtr& operator =(const ResPtr& p) {
    if (ptr_ != NULL) ptr_->deref();
    ptr_ = p.ptr_;
    if (ptr_ != NULL) ptr_->ref();
    return *this;
  }
  ~ResPtr() { if (ptr_ != NULL) ptr_->deref(); }

private:
  T* ptr_;
};

class ResourceLoader {
public:
  bool fileExists(const char *filename) const;
  Block *getFileBlock(const char *filename) const;
  std::FILE *openNewStream(const char *filename) const;
  int fileLength(const char *filename) const;

  static ResourceLoader *instance() {
    if (instance_ == NULL)
      instance_ = new ResourceLoader;
    return instance_;
  }

  Bitmap *loadBitmap(const char *fname);
  Colormap *loadColormap(const char *fname);
  Costume *loadCostume(const char *fname);
  KeyframeAnim *loadKeyframe(const char *fname);
  Material *loadMaterial(const char *fname, const Colormap &c);
  Model *loadModel(const char *fname, const Colormap &c);
  Sound *loadSound(const char *fname);
  void uncache(const char *fname);

private:
  ResourceLoader();
  ResourceLoader(const ResourceLoader &);
  ~ResourceLoader();

  static ResourceLoader *instance_;

  typedef std::list<Lab *> LabList;
  LabList labs_;

  const Lab *findFile(const char *filename) const;

  typedef std::hash_map<std::string, Resource *> cache_type;
  cache_type cache_;

  // Shut up pointless g++ warning
  friend class dummy;
};

inline void Resource::deref() {
  if (--ref_ == 0) {
    ResourceLoader::instance()->uncache(fname_.c_str());
    delete this;
  }
}

#endif

--- NEW FILE: scene.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "scene.h"
#include "textsplit.h"
#include "resource.h"
#include "debug.h"
#include "bitmap.h"
#include "colormap.h"
#include "vector3d.h"
#include <SDL.h>
#include <SDL_opengl.h>
#include <cmath>

Scene::Scene(const char *name, const char *buf, int len) :
  name_(name) {
  TextSplitter ts(buf, len);
  char tempBuf[256];

  ts.expectString("section: colormaps");
  ts.scanString(" numcolormaps %d", 1, &numCmaps_);
  cmaps_ = new ResPtr<Colormap>[numCmaps_];
  char cmap_name[256];
  for (int i = 0; i < numCmaps_; i++) {
    ts.scanString(" colormap %256s", 1, cmap_name);
    cmaps_[i] = ResourceLoader::instance()->loadColormap(cmap_name);
  }

  ts.expectString("section: setups");
  ts.scanString(" numsetups %d", 1, &numSetups_);
  setups_ = new Setup[numSetups_];
  for (int i = 0; i < numSetups_; i++)
    setups_[i].load(ts);
  currSetup_ = setups_;

  ts.expectString("section: lights");
  ts.scanString(" numlights %d", 1, &numLights_);
  lights_ = new Light[numLights_];
  for (int i = 0; i < numLights_; i++)
    lights_[i].load(ts);

  // Calculate the number of sectors
  ts.expectString("section: sectors");
  ts.scanString(" sector %256s", 1, tempBuf);
  ts.scanString(" id %d", 1, &numSectors_);
  sectors_ = new Sector[numSectors_];

  // FIXME: This would be nicer if we could rewind the textsplitter
  // stream...
  sectors_[0].load0(ts, tempBuf, numSectors_);
  for (int i = 1; i < numSectors_; i++)
    sectors_[i].load(ts);
}

Scene::~Scene() {
  delete [] cmaps_;
  delete [] setups_;
  delete [] lights_;
  delete [] sectors_;
}

void Scene::Sector::load(TextSplitter &ts) {
  char buf[256];
  int id = 0;
  ts.scanString(" sector %256s", 1, buf);
  ts.scanString(" id %d", 1, &id);
  load0(ts, buf, id);
}

void Scene::Sector::load0(TextSplitter &ts, char *name, int id) {
  char buf[256];
  int i = 0;
  Vector3d tempVert;

  name_ = name;
  id_ = id;
  ts.scanString(" type %256s", 1, buf);
  type_ = buf;
  ts.scanString(" default visibility %256s", 1, buf);
  visibility_ = buf;
  ts.scanString(" height %f", 1, &height_);
  ts.scanString(" numvertices %d", 1, &numVertices_);
  vertices_ = new Vector3d[numVertices_];

  ts.scanString(" vertices: %f %f %f", 3, &vertices_[0].x(), &vertices_[0].y(), &vertices_[0].z());
  for (i=1;i<numVertices_;i++)
    ts.scanString(" %f %f %f", 3, &vertices_[i].x(), &vertices_[i].y(), &vertices_[i].z());
}

void Scene::Setup::load(TextSplitter &ts) {
  char buf[256];
  ts.scanString(" setup %256s", 1, buf);
  name_ = buf;

  ts.scanString(" background %256s", 1, buf);
  bkgnd_bm_ = ResourceLoader::instance()->loadBitmap(buf);
  ts.scanString(" zbuffer %256s", 1, buf);
  bkgnd_zbm_ = ResourceLoader::instance()->loadBitmap(buf);

  ts.scanString(" position %f %f %f", 3, &pos_.x(), &pos_.y(), &pos_.z());
  ts.scanString(" interest %f %f %f", 3, &interest_.x(), &interest_.y(),
		&interest_.z());
  ts.scanString(" roll %f", 1, &roll_);
  ts.scanString(" fov %f", 1, &fov_);
  ts.scanString(" nclip %f", 1, &nclip_);
  ts.scanString(" fclip %f", 1, &fclip_);
}

void Scene::Light::load(TextSplitter &ts) {
  char buf[256];
  ts.scanString(" light %256s", 1, buf);
  name_ = buf;

  ts.scanString(" type %256s", 1, buf);
  type_ = buf;

  ts.scanString(" position %f %f %f", 3, &pos_.x(), &pos_.y(), &pos_.z());
  ts.scanString(" direction %f %f %f", 3, &dir_.x(), &dir_.y(), &dir_.z());
  ts.scanString(" intensity %f", 1, &intensity_);
  ts.scanString(" umbraangle %f", 1, &umbraangle_);
  ts.scanString(" penumbraangle %f", 1, &penumbraangle_);
  ts.scanString(" color %d %d %d", 3, &color_.red(), &color_.green(), &color_.blue());
}

void Scene::Setup::setupCamera() const {
  // Set perspective transformation
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  //  gluPerspective(std::atan(std::tan(fov_ / 2 * (M_PI/180)) * 0.75) * 2 * (180/M_PI), 4.0f / 3, nclip_, fclip_);
  float right = nclip_ * std::tan(fov_ / 2 * (M_PI/180));
  glFrustum(-right, right, -right * 0.75, right * 0.75, nclip_, fclip_);

  // Apply camera roll
  glRotatef(roll_, 0, 0, -1);

  // Set camera position and direction
  Vector3d up_vec(0, 0, 1);
  if (pos_.x() == interest_.x() && pos_.y() == interest_.y())
    up_vec = Vector3d(0, 1, 0);
  gluLookAt(pos_.x(), pos_.y(), pos_.z(),
	    interest_.x(), interest_.y(), interest_.z(),
	    up_vec.x(), up_vec.y(), up_vec.z());

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

--- NEW FILE: scene.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 SCENE_H
#define SCENE_H

#include "vector3d.h"
#include "bitmap.h"
#include "color.h"
#include <SDL.h>
#include <SDL_opengl.h>
#include <string>

class Colormap;
class TextSplitter;

// The Lua code calls this a "set".

class Scene {
public:
  Scene(const char *name, const char *buf, int len);
  ~Scene();

  void drawBackground() const {
    glEnable(GL_DEPTH_TEST);
    //    if (currSetup_->bkgnd_zbm_ != NULL)
    //      currSetup_->bkgnd_zbm_->draw();
    glDepthMask(GL_FALSE);
    currSetup_->bkgnd_bm_->draw();
    glDepthMask(GL_TRUE);
  }
  void setupCamera() {
    currSetup_->setupCamera();
  }

  const char *name() const { return name_.c_str(); }

  void setSetup(int num) { currSetup_ = setups_ + num; }
  int setup() const { return currSetup_ - setups_; }

private:
  struct Setup {		// Camera setup data
    void load(TextSplitter &ts);
    void setupCamera() const;
    std::string name_;
    ResPtr<Bitmap> bkgnd_bm_, bkgnd_zbm_;
    Vector3d pos_, interest_;
    float roll_, fov_, nclip_, fclip_;
  };

  struct Light {		// Scene lighting data
    void load(TextSplitter &ts);
    std::string name_;
    std::string type_;
    Vector3d pos_, dir_;
    Color color_;
    float intensity_, umbraangle_, penumbraangle_;
  };

  struct Sector {		// Walkarea 'sectors'
    void load(TextSplitter &ts);
    void load0(TextSplitter &ts, char *name, int id);
    int numVertices_, id_;
    std::string name_;
    std::string type_;
    std::string visibility_;
    Vector3d *vertices_;
    float height_;
  };

  std::string name_;
  int numCmaps_;
  ResPtr<Colormap> *cmaps_;
  int numSetups_, numLights_, numSectors_;
  Sector *sectors_;
  Light *lights_;
  Setup *setups_;
  Setup *currSetup_;
};

#endif

--- NEW FILE: sound.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "sound.h"
#include "bits.h"
#include "debug.h"
#include <cstring>
#include <SDL_endian.h>

static uint16_t imcTable1[] = {
  0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e,
  0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001c, 0x001f,
  0x0022, 0x0025, 0x0029, 0x002d, 0x0032, 0x0037, 0x003c, 0x0042,
  0x0049, 0x0050, 0x0058, 0x0061, 0x006b, 0x0076, 0x0082, 0x008f,
  0x009d, 0x00ad, 0x00be, 0x00d1, 0x00e6, 0x00fd, 0x0117, 0x0133,
  0x0151, 0x0173, 0x0198, 0x01c1, 0x01ee, 0x0220, 0x0256, 0x0292,
  0x02d4, 0x031c, 0x036c, 0x03c3, 0x0424, 0x048e, 0x0502, 0x0583,
  0x0610, 0x06ab, 0x0756, 0x0812, 0x08e0, 0x09c3, 0x0abd, 0x0bd0,
  0x0cff, 0x0e4c, 0x0fba, 0x114c, 0x1307, 0x14ee, 0x1706, 0x1954,
  0x1bdc, 0x1ea5, 0x21b6, 0x2515, 0x28ca, 0x2cdf, 0x315b, 0x364b,
  0x3bb9, 0x41b2, 0x4844, 0x4f7e, 0x5771, 0x602f, 0x69ce, 0x7462,
  0x7fff
};

static int8_t imcTable2[] = {
  0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
  0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
  0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
  0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
  0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05,
  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06,
  0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
  0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
  0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07
};

static int8_t imcOtherTable1[] = {
  -1, 4, -1, 4
};
static int8_t imcOtherTable2[] = {
  -1, -1, 2, 6, -1, -1, 2, 6
};
static int8_t imcOtherTable3[] = {
  -1, -1, -1, -1, 1, 2, 4, 6,
  -1, -1, -1, -1, 1, 2, 4, 6
};
static int8_t imcOtherTable4[] = {
  -1, -1, -1, -1, -1, -1, -1, -1,
  1, 1, 1, 2, 2, 4, 5, 6,
  -1, -1, -1, -1, -1, -1, -1, -1,
  1, 1, 1, 2, 2, 4, 5, 6
};
static int8_t imcOtherTable5[] = {
  -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1,
  1, 1, 1, 1, 1, 2, 2, 2,
  2, 4, 4, 4, 5, 5, 6, 6,
  -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1,
  1, 1, 1, 1, 1, 2, 2, 2,
  2, 4, 4, 4, 5, 5, 6, 6
};
static int8_t imcOtherTable6[] = {
  -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1,
  1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 2, 2, 2, 2, 2, 2,
  2, 2, 4, 4, 4, 4, 4, 4,
  5, 5, 5, 5, 6, 6, 6, 6,
  -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1,
  1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 2, 2, 2, 2, 2, 2,
  2, 2, 4, 4, 4, 4, 4, 4,
  5, 5, 5, 5, 6, 6, 6, 6
};
static int8_t *offsets[] = {
  imcOtherTable1, imcOtherTable2, imcOtherTable3,
  imcOtherTable4, imcOtherTable5, imcOtherTable6
};

static uint16_t destTable[5786];

void Sound::init() {
  int destTableStartPos, incer;
  for (destTableStartPos = 0, incer = 0; destTableStartPos < 64;
       destTableStartPos++, incer++) {
    unsigned int destTablePos, imcTable1Pos;
    for (imcTable1Pos = 0, destTablePos = destTableStartPos;
	 imcTable1Pos < sizeof(imcTable1) / sizeof(imcTable1[0]);
	 imcTable1Pos++, destTablePos += 64) {
      int put = 0, count, tableValue;
      for (count = 32, tableValue = imcTable1[imcTable1Pos]; count != 0;
	   count >>= 1, tableValue >>= 1) {
	if ((incer & count) != 0)
	  put += tableValue;
      }
      destTable[destTablePos] = put;
    }
  }
}

static void decompressVima(const char *src, int16_t *dest, int destLen) {
  int numChannels = 1;
  uint8_t sBytes[2];
  int16_t sWords[2];

  sBytes[0] = get_uint8(src++);
  if (sBytes[0] & 0x80) {
    sBytes[0] = ~sBytes[0];
    numChannels = 2;
  }
  sWords[0] = get_BE_uint16(src);
  src += 2;
  if (numChannels > 1) {
    sBytes[1] = get_uint8(src++);
    sWords[1] = get_BE_uint16(src);
    src += 2;
  }

  int numSamples = destLen / (numChannels * 2);
  int bits = get_BE_uint16(src);
  int bitPtr = 0;
  src += 2;

  for (int channel = 0; channel < numChannels; channel++) {
    int16_t *destPos = dest + channel;
    int currTablePos = sBytes[channel];
    int outputWord = sWords[channel];

    for (int sample = 0; sample < numSamples; sample++) {
      int numBits = imcTable2[currTablePos];
      bitPtr += numBits;
      int highBit = 1 << (numBits - 1);
      int lowBits = highBit - 1;
      int val = (bits >> (16 - bitPtr)) & (highBit | lowBits);
      if (bitPtr > 7) {
	bits = ((bits & 0xff) << 8) | get_uint8(src++);
	bitPtr -= 8;
      }
      if (val & highBit)
	val ^= highBit;
      else
	highBit = 0;
      if (val == lowBits) {
	outputWord = ((signed short) (bits << bitPtr) & 0xffffff00);
	bits = ((bits & 0xff) << 8) | get_uint8(src++);
	outputWord |= ((bits >> (8 - bitPtr)) & 0xff);
	bits = ((bits & 0xff) << 8) | get_uint8(src++);
      }
      else {
	int index = (val << (7 - numBits)) | (currTablePos << 6);
	int delta = destTable[index];
	if (val != 0)
	  delta += (imcTable1[currTablePos] >> (numBits - 1));
	if (highBit != 0)
	  delta = -delta;
	outputWord += delta;
	if (outputWord < -0x8000)
	  outputWord = -0x8000;
	else if (outputWord > 0x7fff)
	  outputWord = 0x7fff;
      }
      *destPos = outputWord;
      destPos += numChannels;
      currTablePos += offsets[numBits - 2][val];
      if (currTablePos < 0)
	currTablePos = 0;
      else if (currTablePos > 88)
	currTablePos = 88;
    }
  }
}

Sound::Sound(const char *filename, const char *data, int /* len */) :
  Resource(filename)
{
  const char *extension = filename + std::strlen(filename) - 3;
  const char *dataStart;
  int numBlocks, codecsLen;
  const char *codecsStart;
  const char *headerPos = data;
  int dataSize;

  if (strcasecmp(extension, "wav") == 0 || strcasecmp(extension, "imc") == 0) {
    // Read MCMP info
    if (std::memcmp(data, "MCMP", 4) != 0)
      error("Invalid file format in %s\n", filename);

    // The first block is the WAVE or IMUS header
    numBlocks = get_BE_uint16(data + 4);
    codecsStart = data + 8 + numBlocks * 9;
    codecsLen = get_BE_uint16(codecsStart - 2);
    headerPos = codecsStart + codecsLen;
  }

  if (strcasecmp(extension, "wav") == 0) {
    numChannels_ = get_LE_uint16(headerPos + 22);
    dataStart = headerPos + 28 + get_LE_uint32(headerPos + 16);
    dataSize = get_LE_uint32(dataStart - 4);
  }
  else if (strcasecmp(extension, "imc") == 0 ||
	   strcasecmp(extension, "imu") == 0) {
    // Ignore MAP info for now...
    if (memcmp(headerPos + 16, "FRMT", 4) != 0)
      error("FRMT block not where it was expected\n");
    numChannels_ = get_BE_uint32(headerPos + 40);
    dataStart = headerPos + 24 + get_BE_uint32(headerPos + 12);
    dataSize = get_BE_uint32(dataStart - 4);
  }
  else {
    error("Unrecognized extension for sound file %s\n", filename);
  }

  if (strcasecmp(extension, "wav") == 0 || strcasecmp(extension, "imc") == 0) {
    // Uncompress the samples
    numSamples_ = dataSize / 2;
    samples_ = new int16_t[dataSize / 2];
    int16_t *destPos = samples_;
    const char *srcPos = dataStart;
    for (int i = 1; i < numBlocks; i++) { // Skip header block
      if (std::strcmp(codecsStart + 5 * get_uint8(data + 6 + i * 9),
		      "VIMA") != 0)
	error("Unsupported codec %s\n",
	      codecsStart + 5 * get_uint8(data + 6 + i * 9));
      decompressVima(srcPos, destPos, get_BE_uint32(data + 7 + i * 9));
      srcPos += get_BE_uint32(data + 11 + i * 9);
      destPos += get_BE_uint32(data + 7 + i * 9) / 2;
    }
  }
  else {
    numSamples_ = dataSize / 2;
    samples_ = new int16_t[dataSize / 2];
    std::memcpy(samples_, dataStart, dataSize);
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
    for (int i = 0; i < numSamples_; i++)
      samples_[i] = SDL_Swap16(samples_[i]);
#endif
  }

  currPos_ = 0;
}

void Sound::reset() {
  currPos_ = 0;
}

void Sound::mix(int16_t *data, int samples) {
  while (samples > 0 && currPos_ < numSamples_) {
    *data += samples_[currPos_];
    data++;
    if (numChannels_ == 1) {
      *data += samples_[currPos_];
      samples--;
      data++;
    }
    currPos_++;
    samples--;
  }
}

Sound::~Sound() {
  delete[] samples_;
}

--- NEW FILE: sound.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 SOUND_H
#define SOUND_H

#include "resource.h"
#include <stdint.h>

class Sound : public Resource {
public:
  Sound(const char *filename, const char *data, int len);
  ~Sound();

  bool done() const { return currPos_ >= numSamples_; }

private:
  int numSamples_, numChannels_, currPos_;
  int16_t *samples_;

  static void init();

  void reset();
  void mix(int16_t *data, int samples);

  friend class Mixer;
};

#endif

--- NEW FILE: textsplit.cpp ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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

#include "textsplit.h"
#include "debug.h"
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cstdarg>

TextSplitter::TextSplitter(const char *data, int len) {
  data_ = new char[len + 1];
  std::memcpy(data_, data, len);
  data_[len] = '\0';
  curr_line_ = data_;
  processLine();
}

void TextSplitter::expectString(const char *expected) {
  if (eof())
    error("Expected `%s', got EOF\n", expected);
  if (std::strcmp(currentLine(), expected) != 0)
    error("Expected `%s', got `%s'\n", expected, currentLine());
  nextLine();
}

void TextSplitter::scanString(const char *fmt, int field_count, ...) {
  if (eof())
    error("Expected line of format `%s', got EOF\n", fmt);

  std::va_list va;

  va_start(va, field_count);
  if (std::vsscanf(currentLine(), fmt, va) < field_count)
    error("Expected line of format `%s', got `%s'\n", fmt, currentLine());
  va_end(va);

  nextLine();
}

void TextSplitter::processLine() {
  if (eof())
    return;

  next_line_ = std::strchr(curr_line_, '\n');
  if (next_line_ != NULL) {
    *next_line_ = '\0';
    next_line_++;
  }

  // Cut off comments
  char *comment_start = std::strchr(curr_line_, '#');
  if (comment_start != NULL)
    *comment_start = '\0';

  // Cut off trailing whitespace (including '\r')
  char *strend = std::strchr(curr_line_, '\0');
  while (strend > curr_line_ && std::isspace(strend[-1]))
    strend--;
  *strend = '\0';

  // Skip blank lines
  if (*curr_line_ == '\0')
    nextLine();

  // Convert to lower case
  if (! eof())
    for (char *s = curr_line_; *s != '\0'; s++)
      *s = std::tolower(*s);
}

--- NEW FILE: textsplit.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 TEXTSPLIT_HH
#define TEXTSPLIT_HH

#include <cstdlib>

// A utility class to help in parsing the text-format files.  Splits
// the text data into lines, skipping comments, trailing whitespace,
// and empty lines.  Also folds everything to lowercase.

class TextSplitter {
public:
  TextSplitter(const char *data, int len);

  char *nextLine() {
    curr_line_ = next_line_;
    processLine();
    return curr_line_;
  }

  char *currentLine() { return curr_line_; }
  const char *currentLine() const { return curr_line_; }
  bool eof() const { return curr_line_ == NULL; }

  // Expect a certain fixed string; bail out with an error if not
  // found.  Advance to the next line.
  void expectString(const char *expected);

  // Scan a line according to the given format (compatible with
  // scanf); if not all fields are read (according to the field_count
  // argument), bail out with an error.  Advance to the next line.
  void scanString(const char *fmt, int field_count, ...)
#ifdef __GNUC__
    __attribute__((format (scanf, 2, 4)))
#endif
    ;

  ~TextSplitter() { delete[] data_; }

private:
  char *data_, *curr_line_, *next_line_;

  void processLine();
};

#endif

--- NEW FILE: vector3d.h ---
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 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 VECTOR3D_HH
#define VECTOR3D_HH

#include <cmath>

class Vector3d {
public:
  float coords_[3];		// Make sure this stays as an array so
				// it can be passed to GL functions

  float& x() { return coords_[0]; }
  float x() const { return coords_[0]; }
  float& y() { return coords_[1]; }
  float y() const { return coords_[1]; }
  float& z() { return coords_[2]; }
  float z() const { return coords_[2]; }

  Vector3d() { }
  Vector3d(float x, float y, float z) {
    this->x() = x; this->y() = y; this->z() = z;
  }
  Vector3d(const Vector3d &v) {
    x() = v.x(); y() = v.y(); z() = v.z();
  }

  void set(float x, float y, float z) {
    this->x() = x; this->y() = y; this->z() = z;
  }

  Vector3d& operator =(const Vector3d &v) {
    x() = v.x(); y() = v.y(); z() = v.z();
    return *this;
  }

  Vector3d& operator +=(const Vector3d &v) {
    x() += v.x(); y() += v.y(); z() += v.z();
    return *this;
  }

  Vector3d& operator -=(const Vector3d &v) {
    x() -= v.x(); y() -= v.y(); z() -= v.z();
    return *this;
  }

  Vector3d& operator *=(float s) {
    x() *= s; y() *= s; z() *= s;
    return *this;
  }

  Vector3d& operator /=(float s) {
    x() /= s; y() /= s; z() /= s;
    return *this;
  }

  float magnitude() const {
    return std::sqrt(x() * x() + y() * y() + z() * z());
  }
};

inline float dot(const Vector3d& v1, const Vector3d& v2) {
  return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z();
}

inline Vector3d cross(const Vector3d& v1, const Vector3d& v2) {
  return Vector3d(v1.y() * v2.z() - v1.z() * v2.y(),
		  v1.z() * v2.x() - v1.x() * v2.z(),
		  v1.x() * v2.y() - v1.y() * v2.x());
}

inline float angle(const Vector3d& v1, const Vector3d& v2) {
  return std::acos(dot(v1, v2) / (v1.magnitude() * v2.magnitude()));
}

inline Vector3d operator +(const Vector3d& v1, const Vector3d& v2) {
  Vector3d result = v1;
  result += v2;
  return result;
}

inline Vector3d operator -(const Vector3d& v1, const Vector3d& v2) {
  Vector3d result = v1;
  result -= v2;
  return result;
}

inline Vector3d operator *(float s, const Vector3d& v) {
  Vector3d result = v;
  result *= s;
  return result;
}

inline Vector3d operator *(const Vector3d& v, float s) {
  return s * v;
}

inline Vector3d operator /(const Vector3d& v, float s) {
  Vector3d result = v;
  result /= s;
  return result;
}

inline bool operator ==(const Vector3d& v1, const Vector3d& v2) {
  return v1.x() == v2.x() && v1.y() == v2.y() && v1.z() == v2.z();
}

#endif

Index: grimdialog.htm
===================================================================
RCS file: /cvsroot/scummvm/residual/grimdialog.htm,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -d -r1.1.1.1 -r1.2
--- grimdialog.htm	15 Aug 2003 18:00:22 -0000	1.1.1.1
+++ grimdialog.htm	15 Aug 2003 19:41:26 -0000	1.2
@@ -1,11 +1,11 @@
-<META Generated by Grim Fandango><TITLE>Grim Fandango Dialog Transcript</TITLE>
-<P>Grim Fandango Dialog Transcript<BR>© LucasArts Entertainment Company LLC.  All rights reserved.</P><BR>
+<META généré par Grim Fandango><TITLE>Transcription des dialogues de Grim Fandango</TITLE>
+<P>Transcription des dialogues de Grim Fandango<BR>© LucasArts Entertainment Company LLC.  Tous droits réservés.</P><BR>
 <BR>
 <BR>
 <CENTER>(Introduction)</CENTER>
 <BR>
 <BR>
-<P><B>INT.     Manny's Office     DAY</B></P>
+<P><B>INT.     Bureau de Manny     JOUR</B></P>
 <BR>
 <CENTER><B>Manny</B></CENTER>
 <CENTER></CENTER>

--- actor.cc DELETED ---

--- actor.hh DELETED ---

--- bitmap.cc DELETED ---

--- bitmap.hh DELETED ---

--- bits.hh DELETED ---

--- color.hh DELETED ---

--- colormap.hh DELETED ---

--- costume.cc DELETED ---

--- costume.hh DELETED ---

--- debug.cc DELETED ---

--- debug.hh DELETED ---

--- engine.cc DELETED ---

--- engine.hh DELETED ---

--- hash_map.hh DELETED ---

--- keyframe.cc DELETED ---

--- keyframe.hh DELETED ---

--- lab.cc DELETED ---

--- lab.hh DELETED ---

--- localize.cc DELETED ---

--- localize.hh DELETED ---

--- lua.cc DELETED ---

--- lua.hh DELETED ---

--- main.cc DELETED ---

--- material.cc DELETED ---

--- material.hh DELETED ---

--- mixer.cc DELETED ---

--- mixer.hh DELETED ---

--- model.cc DELETED ---

--- model.hh DELETED ---

--- registry.cc DELETED ---

--- registry.hh DELETED ---

--- resource.cc DELETED ---

--- resource.hh DELETED ---

--- scene.cc DELETED ---

--- scene.hh DELETED ---

--- sound.cc DELETED ---

--- sound.hh DELETED ---

--- textsplit.cc DELETED ---

--- textsplit.hh DELETED ---

--- vector3d.hh DELETED ---





More information about the Scummvm-git-logs mailing list