~ubuntu-branches/ubuntu/utopic/rlvm/utopic-proposed

« back to all changes in this revision

Viewing changes to src/rlvm.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Ying-Chun Liu (PaulLiu), Ying-Chun Liu (PaulLiu), Elliot Glaysher
  • Date: 2011-05-19 00:28:44 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20110519002844-qszwmj7oiixww0eg
Tags: 0.12-1
[ Ying-Chun Liu (PaulLiu) <paulliu@debian.org> ]
* New upstream release

[ Elliot Glaysher <glaysher@umich.edu> ]
* New GTK+ interface with desktop integration and UI refinements
* Partial Japanese localizations
* Fix graphics corruption in in-game dialogs when a dialog is brought
  up, and then fullscreen mode activated
* Smooth the output of text in rlBabel using games
* Don't play voice samples while fast forwarding

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
 
// vi:tw=80:et:ts=2:sts=2
3
 
//
4
 
// -----------------------------------------------------------------------
5
 
//
6
 
// This file is part of RLVM, a RealLive virtual machine clone.
7
 
//
8
 
// -----------------------------------------------------------------------
9
 
//
10
 
// Copyright (C) 2006, 2007 Elliot Glaysher
11
 
//
12
 
// This program is free software; you can redistribute it and/or modify
13
 
// it under the terms of the GNU General Public License as published by
14
 
// the Free Software Foundation; either version 3 of the License, or
15
 
// (at your option) any later version.
16
 
//
17
 
// This program is distributed in the hope that it will be useful,
18
 
// but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 
// GNU General Public License for more details.
21
 
//
22
 
// You should have received a copy of the GNU General Public License
23
 
// along with this program; if not, write to the Free Software
24
 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
 
//
26
 
// -----------------------------------------------------------------------
27
 
 
28
 
#include <boost/program_options.hpp>
29
 
#include <boost/filesystem/operations.hpp>
30
 
 
31
 
// We include this here because SDL is retarded and works by #define
32
 
// main(inat argc, char* agrv[]). Loosers.
33
 
#include <SDL/SDL.h>
34
 
 
35
 
#include <iostream>
36
 
#include <sstream>
37
 
#include <string>
38
 
 
39
 
#include "MachineBase/GameHacks.hpp"
40
 
#include "MachineBase/RLMachine.hpp"
41
 
#include "MachineBase/Serialization.hpp"
42
 
#include "Modules/Modules.hpp"
43
 
#include "Modules/Module_Sys_Save.hpp"
44
 
#include "Platforms/gcn/GCNPlatform.hpp"
45
 
#include "Systems/Base/GraphicsSystem.hpp"
46
 
#include "Systems/Base/SoundSystem.hpp"
47
 
#include "Systems/Base/SystemError.hpp"
48
 
#include "Systems/SDL/SDLSystem.hpp"
49
 
#include "Utilities/Exception.hpp"
50
 
#include "Utilities/File.hpp"
51
 
#include "Utilities/findFontFile.h"
52
 
#include "libReallive/gameexe.h"
53
 
#include "libReallive/reallive.h"
54
 
 
55
 
using namespace std;
56
 
 
57
 
namespace po = boost::program_options;
58
 
namespace fs = boost::filesystem;
59
 
 
60
 
/**
61
 
 * @mainpage RLVM, a Reallive virtual machine clone
62
 
 *
63
 
 * @section Introduction
64
 
 *
65
 
 * RLVM is a clone of the official Reallive virtual machine, produced
66
 
 * by VisualArt's KK, meant to provide Linux and Macintosh users with
67
 
 * a compatible, portable interpreter for Realive games.
68
 
 *
69
 
 * RLVM would not exist if it weren't for the help of Haeleth, and,
70
 
 * indirectly, Jagarl, both of who have done amazing jobs documenting
71
 
 * the fine details of the RealLive system, along with doing most of
72
 
 * the really hard reverse engineering work that I'd rather not do.
73
 
 *
74
 
 * @section Table Table of Contents
75
 
 *
76
 
 * The documentation is divided into the following sections.
77
 
 *
78
 
 * - @subpage theProblemDomainOfVisualNovels "The problem Domain of Visual Novels"
79
 
 * - @subpage architectureReview "RLVM Architecture"
80
 
 */
81
 
 
82
 
// -----------------------------------------------------------------------
83
 
 
84
 
/**
85
 
 * @page theProblemDomainOfVisualNovels The problem Domain of Visual Novels
86
 
 *
87
 
 * @section Overview
88
 
 *
89
 
 * Visual Novels (referred to as NVL, AVG or ADV games in Japanese) are
90
 
 * a form of interactive fiction in Japan which never really became
91
 
 * popular in most English speaking countries. They are simple, plot
92
 
 * and character oriented games which are very text-heavy. Gameplay
93
 
 * wise, they are comparable to a large slide show with text, images
94
 
 * and sound, and can be thought of as massive, more serious, mature
95
 
 * versions of the Choose-Your-Own-Adventure series of children's books.
96
 
 *
97
 
 * @section IsAndIsnt What RLVM is and isn't
98
 
 *
99
 
 * RLVM is a clone of a specific visual novel interpreter, the
100
 
 * RealLive system developed by VisualArts KK. It aims to (eventually)
101
 
 * become a compatible, portable interpreter for non-Windows users
102
 
 * that will play a large variety of commercial visual novels written
103
 
 * in Reallive.
104
 
 *
105
 
 * RLVM is not intended to compete with VisualArts KK as a development
106
 
 * toolkit. While someone could theoretically combine RLVM with <a
107
 
 * href="http://www.haeleth.net">Haeleth</a>'s <a
108
 
 * href="http://dev.haeleth.net/rldev.shtml">RLdev</a> compiler
109
 
 * toolkit to produce games (at least after RLVM supports a base set
110
 
 * of operations), it would be overly cumbersome and I would recommend
111
 
 * one of the many free visual novel development systems, which would
112
 
 * be both easier to use and more featurefull.
113
 
 *
114
 
 * RLVM is not meant to facilitate piracy. Please buy these games;
115
 
 * many people put their hearts into writing these stories and they
116
 
 * deserve to be rewarded financially.
117
 
 *
118
 
 * Finally, RLVM is not a big truck...It's a series of tubes.
119
 
 */
120
 
 
121
 
// -----------------------------------------------------------------------
122
 
 
123
 
/**
124
 
 * @page architectureReview RLVM Architecture
125
 
 *
126
 
 * RLVM is divided into several high level directories:
127
 
 *
128
 
 * - A modified version of Haeleth's @c libReallive, which is
129
 
 *   responsible for reading and parsing the SEEN.TXT file and
130
 
 *   creating the corresponding object representation. There is also a
131
 
 *   class Gameexe which parses the Gameexe.ini file in every RealLive
132
 
 *   game.
133
 
 * - The Opcode Definitions / Modules, which can be found in the
134
 
 *   subidrectory @c src/Modules/ . These files contain the
135
 
 *   definitions for the individual Opcodes.
136
 
 * - The core of the virtual machine found in @c src/MachineBase/ :
137
 
 *   - RLMachine: the main class which contains all execution state
138
 
 *   - RLOperation: the base class of every opcode definition.
139
 
 *   - LongOperation: the base class for all operations that persist
140
 
 *     for multiple cycles through the game loop.
141
 
 * - The Base System classes found in @c src/Systems/Base , which define the
142
 
 *   generalized interface for system dependent operations like sound and
143
 
 *   graphics. It was originally meant to only define abstract interface which
144
 
 *   would be implemented in System/ specific directories. Said base classes
145
 
 *   have grown to hold significant cross-platform logic, implemented per
146
 
 *   system by subclassing. It has also grown to contain a whole lot of
147
 
 *   non-subclassed, cross platform code.
148
 
 * - The System subclasses, such as @c src/Systems/SDL , which
149
 
 *   implement the Base System interface for SDL. Additional
150
 
 *   subclasses could be written for DirectX, or some other game
151
 
 *   interface.
152
 
 *
153
 
 * The above was part of the original version of this document. In the
154
 
 * intervening time, I've also added:
155
 
 *
156
 
 * - LongOperations and Effects: LongOperations are functors that get run
157
 
 *   during the mainloop instead of a RealLive instruction. Sometimes execution
158
 
 *   of RealLive code needs to stop so a command that would run longer than a
159
 
 *   single run through the gameloop can take all the time it needs while still
160
 
 *   updating the screen. Effects are graphical LongOperations that transition
161
 
 *   between two images.
162
 
 * - Encodings: A combination of Haeleth's encoding functions from rlBabel and
163
 
 *   various encoding utility functions I've written.
164
 
 */
165
 
 
166
 
// -----------------------------------------------------------------------
167
 
 
168
 
void printVersionInformation() {
169
 
  cout
170
 
    << "rlvm (" << rlvm_version() <<  ")" << endl
171
 
    << "Copyright (C) 2006-2011 Elliot Glaysher, et all." << endl
172
 
    << endl
173
 
    << "Contains code that is: " << endl
174
 
    << "  Copyright (C) 2006-2007 Peter \"Haeleth\" Jolly" << endl
175
 
    << "  Copyright (C) 2004-2006 Kazunori \"jagarl\" Ueno" << endl
176
 
    << endl
177
 
    << "This program is free software: you can redistribute it and/or modify"
178
 
    << endl
179
 
    << "it under the terms of the GNU General Public License as published by"
180
 
    << endl
181
 
    << "the Free Software Foundation, either version 3 of the License, or"
182
 
    << endl
183
 
    << "(at your option) any later version."
184
 
    << endl << endl
185
 
    << "This program is distributed in the hope that it will be useful,"
186
 
    << endl
187
 
    << "but WITHOUT ANY WARRANTY; without even the implied warranty of"
188
 
    << endl
189
 
    << "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the"
190
 
    << endl
191
 
    << "GNU General Public License for more details."
192
 
    << endl << endl
193
 
    << "You should have received a copy of the GNU General Public License"
194
 
    << endl
195
 
    << "along with this program.  If not, see <http://www.gnu.org/licenses/>."
196
 
    << endl << endl;
197
 
}
198
 
 
199
 
// -----------------------------------------------------------------------
200
 
 
201
 
void printUsage(const string& name, po::options_description& opts) {
202
 
  cout << "Usage: " << name << " [options] <game root>" << endl;
203
 
  cout << opts << endl;
204
 
}
205
 
 
206
 
// -----------------------------------------------------------------------
207
 
 
208
 
int main(int argc, char* argv[]) {
209
 
  srand(time(NULL));
210
 
 
211
 
  // Set global state: allow spaces in game paths
212
 
  fs::path::default_name_check(fs::native);
213
 
 
214
 
  // -----------------------------------------------------------------------
215
 
  // Parse command line options
216
 
 
217
 
  // Declare the supported options.
218
 
  po::options_description opts("Options");
219
 
  opts.add_options()
220
 
      ("help", "Produce help message")
221
 
      ("help-debug", "Print help message for people working on rlvm")
222
 
      ("version", "Display version and license information")
223
 
      ("font", po::value<string>(), "Specifies TrueType font to use.");
224
 
 
225
 
  po::options_description debugOpts("Debugging Options");
226
 
  debugOpts.add_options()
227
 
      ("gameexe", po::value<string>(), "Override location of Gameexe.ini")
228
 
      ("seen", po::value<string>(), "Override location of SEEN.TXT")
229
 
      ("start-seen", po::value<int>(), "Force start at SEEN#")
230
 
      ("load-save", po::value<int>(), "Load a saved game on start")
231
 
      ("memory", "Forces debug mode (Sets #MEMORY=1 in the Gameexe.ini file)")
232
 
      ("undefined-opcodes", "Display a message on undefined opcodes")
233
 
      ("count-undefined",
234
 
       "On exit, present a summary table about how many times each undefined "
235
 
       "opcode was called");
236
 
 
237
 
  // Declare the final option to be game-root
238
 
  po::options_description hidden("Hidden");
239
 
  hidden.add_options()
240
 
      ("game-root", po::value<string>(), "Location of game root");
241
 
 
242
 
  po::positional_options_description p;
243
 
  p.add("game-root", -1);
244
 
 
245
 
  // Use these on the command line
246
 
  po::options_description commandLineOpts;
247
 
  commandLineOpts.add(opts).add(hidden).add(debugOpts);
248
 
 
249
 
  po::variables_map vm;
250
 
  try {
251
 
    po::store(po::basic_command_line_parser<char>(argc, argv).
252
 
              options(commandLineOpts).positional(p).run(),
253
 
              vm);
254
 
    po::notify(vm);
255
 
  } catch (boost::program_options::multiple_occurrences& e) {
256
 
    cerr << "Couldn't parse command line: multiple_occurances." << endl
257
 
         << " (Hint: this can happen when your shell doesn't escape properly,"
258
 
         << endl
259
 
         << "  e.g. \"/path/to/Clannad Full Voice/\" without the quotes.)"
260
 
         << endl;
261
 
    return -1;
262
 
  } catch (boost::program_options::error& e) {
263
 
    cerr << "Couldn't parse command line: " << e.what() << endl;
264
 
    return -1;
265
 
  }
266
 
 
267
 
  // -----------------------------------------------------------------------
268
 
 
269
 
  po::options_description allOpts("Allowed options");
270
 
  allOpts.add(opts).add(debugOpts);
271
 
 
272
 
  // -----------------------------------------------------------------------
273
 
  // Process command line options
274
 
  fs::path gamerootPath, gameexePath, seenPath;
275
 
 
276
 
  if (vm.count("help")) {
277
 
    printUsage(argv[0], opts);
278
 
    return 0;
279
 
  }
280
 
 
281
 
  if (vm.count("help-debug")) {
282
 
    printUsage(argv[0], allOpts);
283
 
    return 0;
284
 
  }
285
 
 
286
 
  if (vm.count("version")) {
287
 
    printVersionInformation();
288
 
    return 0;
289
 
  }
290
 
 
291
 
  if (vm.count("game-root")) {
292
 
    gamerootPath = vm["game-root"].as<string>();
293
 
 
294
 
    if (!fs::exists(gamerootPath)) {
295
 
      cerr << "ERROR: Path '" << gamerootPath << "' does not exist." << endl;
296
 
      return -1;
297
 
    }
298
 
 
299
 
    if (!fs::is_directory(gamerootPath)) {
300
 
      cerr << "ERROR: Path '" << gamerootPath << "' is not a directory."
301
 
           << endl;
302
 
      return -1;
303
 
    }
304
 
 
305
 
    // Some games hide data in a lower subdirectory.  A little hack to
306
 
    // make these behave as expected...
307
 
    if (correctPathCase(gamerootPath / "Gameexe.ini").empty()) {
308
 
      if (!correctPathCase(gamerootPath / "KINETICDATA" /
309
 
                           "Gameexe.ini").empty()) {
310
 
        gamerootPath /= "KINETICDATA/";
311
 
      } else if (!correctPathCase(gamerootPath / "REALLIVEDATA" /
312
 
                                  "Gameexe.ini").empty()) {
313
 
        gamerootPath /= "REALLIVEDATA/";
314
 
      } else {
315
 
        cerr << "WARNING: Path '" << gamerootPath << "' may not contain a "
316
 
             << "RealLive game." << endl;
317
 
      }
318
 
    }
319
 
  } else {
320
 
    printUsage(argv[0], opts);
321
 
    return -1;
322
 
  }
323
 
 
324
 
  // --gameexe
325
 
  if (vm.count("gameexe"))
326
 
    gameexePath = correctPathCase(vm["gameexe"].as<string>());
327
 
  else
328
 
    gameexePath = correctPathCase(gamerootPath / "Gameexe.ini");
329
 
 
330
 
  // --seen
331
 
  if (vm.count("seen"))
332
 
    seenPath = correctPathCase(vm["seen"].as<string>());
333
 
  else
334
 
    seenPath = correctPathCase(gamerootPath / "Seen.txt");
335
 
 
336
 
  try {
337
 
    cerr << "gameexePath: " << gameexePath << endl;
338
 
    Gameexe gameexe(gameexePath);
339
 
    gameexe("__GAMEPATH") = gamerootPath.file_string();
340
 
 
341
 
    if (vm.count("font")) {
342
 
      string font = vm["font"].as<string>();
343
 
      if (fs::exists(font)) {
344
 
        gameexe("__GAMEFONT") = font;
345
 
        cerr << "Using custom font " << vm["font"].as<string>() << endl;
346
 
      } else {
347
 
        cerr << "Couldn't open font file \"" << font << "\"" << endl;
348
 
        return -1;
349
 
      }
350
 
    }
351
 
 
352
 
    // Possibly force starting at a different seen
353
 
    if (vm.count("start-seen"))
354
 
      gameexe("SEEN_START") = vm["start-seen"].as<int>();
355
 
 
356
 
    if (vm.count("memory"))
357
 
      gameexe("MEMORY") = 1;
358
 
 
359
 
    SDLSystem sdlSystem(gameexe);
360
 
    libReallive::Archive arc(seenPath.file_string(), gameexe("REGNAME"));
361
 
    RLMachine rlmachine(sdlSystem, arc);
362
 
    addAllModules(rlmachine);
363
 
    addGameHacks(rlmachine);
364
 
 
365
 
    // Validate our font file
366
 
    fs::path fontFile = findFontFile(sdlSystem);
367
 
    if (fontFile.empty() || !fs::exists(fontFile)) {
368
 
      cerr << "Could not open font file. Please either: " << endl
369
 
           << endl
370
 
           << "1) Place a copy of msgothic.ttc in your home directory." << endl
371
 
           << "2) Place a copy of msgothic.ttc in \""
372
 
           << gamerootPath.file_string() << "\"" << endl
373
 
           << "3) Specify an alternate font with the --font option." << endl;
374
 
      return -2;
375
 
    }
376
 
 
377
 
    // Initialize our platform dialogs (we have to do this after
378
 
    // looking for a font because we use that font internally).
379
 
    boost::shared_ptr<Platform> platform(
380
 
        new GCNPlatform(sdlSystem, sdlSystem.graphics().screenRect()));
381
 
    sdlSystem.setPlatform(platform);
382
 
 
383
 
    if (vm.count("undefined-opcodes"))
384
 
      rlmachine.setPrintUndefinedOpcodes(true);
385
 
 
386
 
    if (vm.count("count-undefined"))
387
 
      rlmachine.recordUndefinedOpcodeCounts();
388
 
 
389
 
    Serialization::loadGlobalMemory(rlmachine);
390
 
    rlmachine.setHaltOnException(false);
391
 
 
392
 
    if (vm.count("load-save")) {
393
 
      Sys_load()(rlmachine, vm["load-save"].as<int>());
394
 
    }
395
 
 
396
 
    while (!rlmachine.halted()) {
397
 
      // Give SDL a chance to respond to events, redraw the screen,
398
 
      // etc.
399
 
      sdlSystem.run(rlmachine);
400
 
 
401
 
      // Run the rlmachine through another instruction
402
 
      rlmachine.executeNextInstruction();
403
 
    }
404
 
 
405
 
    Serialization::saveGlobalMemory(rlmachine);
406
 
  } catch (rlvm::Exception& e) {
407
 
    cerr << "Fatal RLVM error: " << e.what() << endl;
408
 
    return 1;
409
 
  } catch (libReallive::Error& e) {
410
 
    cerr << "Fatal libReallive error: " << e.what() << endl;
411
 
    return 1;
412
 
  } catch (SystemError& e) {
413
 
    cerr << "Fatal local system error: " << e.what() << endl;
414
 
    return 1;
415
 
  } catch (std::exception& e) {
416
 
    cout << "Uncaught exception: " << e.what() << endl;
417
 
    return 1;
418
 
  } catch (const char* e) {
419
 
    cout << "Uncaught exception: " << e << endl;
420
 
    return 1;
421
 
  }
422
 
 
423
 
  return 0;
424
 
}