Browse Source

Script parser can read all TR2/TR3 PC/PSX Scripts

Thomas Buck 9 years ago
parent
commit
63765138fb
4 changed files with 68 additions and 22 deletions
  1. 1
    0
      ChangeLog.md
  2. 10
    5
      include/Script.h
  3. 48
    17
      src/Script.cpp
  4. 9
    0
      test/Script.cpp

+ 1
- 0
ChangeLog.md View File

5
     [ 20140807 ]
5
     [ 20140807 ]
6
     * Script parser successfully loading level scripts
6
     * Script parser successfully loading level scripts
7
     * Can also read TR3 script now
7
     * Can also read TR3 script now
8
+    * Also working with TR2/TR3 PSX Script (TOMBPSX.DAT)
8
 
9
 
9
     [ 20140806 ]
10
     [ 20140806 ]
10
     * Improved Script reader and its Unit Test
11
     * Improved Script reader and its Unit Test

+ 10
- 5
include/Script.h View File

99
     std::string getLevelName(unsigned int i);
99
     std::string getLevelName(unsigned int i);
100
     std::string getLevelFilename(unsigned int i);
100
     std::string getLevelFilename(unsigned int i);
101
 
101
 
102
+    unsigned int pictureCount();
103
+    std::string getPictureFilename(unsigned int i);
104
+
102
     unsigned int cutsceneCount();
105
     unsigned int cutsceneCount();
103
     std::string getCutsceneFilename(unsigned int i);
106
     std::string getCutsceneFilename(unsigned int i);
104
 
107
 
149
     };
152
     };
150
 
153
 
151
     // Header
154
     // Header
152
-    uint32_t version;
155
+    uint32_t version; // Always 3, for TR2/3 on PC and PSX
153
     std::string description;
156
     std::string description;
154
 
157
 
155
     // Gameflow data
158
     // Gameflow data
168
     uint16_t numDemos;
171
     uint16_t numDemos;
169
     uint16_t titleTrack;
172
     uint16_t titleTrack;
170
     int16_t singleLevel;
173
     int16_t singleLevel;
171
-    uint16_t flags;
174
+    uint16_t flags; // See ScriptFlag enum
172
     uint8_t cypherCode;
175
     uint8_t cypherCode;
173
-    uint8_t language;
176
+    uint8_t language; // See ScriptLanguage enum
174
     uint16_t secretTrack;
177
     uint16_t secretTrack;
175
 
178
 
179
+    uint16_t numPCStrings;
180
+    uint16_t numGameStrings;
181
+
176
     // Strings
182
     // Strings
177
     std::vector<std::string> levelNames; // numLevels
183
     std::vector<std::string> levelNames; // numLevels
178
     std::vector<std::string> pictureFilenames; // numPictures
184
     std::vector<std::string> pictureFilenames; // numPictures
181
     std::vector<std::string> levelFilenames; // numLevels
187
     std::vector<std::string> levelFilenames; // numLevels
182
     std::vector<std::string> cutsceneFilenames; // numCutscenes
188
     std::vector<std::string> cutsceneFilenames; // numCutscenes
183
     std::vector<std::vector<uint16_t>> script; // numLevels + 1
189
     std::vector<std::vector<uint16_t>> script; // numLevels + 1
184
-    uint16_t numGameStrings;
185
     std::vector<std::string> gameStrings; // numGameStrings
190
     std::vector<std::string> gameStrings; // numGameStrings
186
-    std::vector<std::string> pcStrings; // 41
191
+    std::vector<std::string> pcStrings; // 41 for TR2/3 on PC; 80 for TR2 on PSX
187
     std::vector<std::vector<std::string>> puzzles; // 4 * numLevels
192
     std::vector<std::vector<std::string>> puzzles; // 4 * numLevels
188
     std::vector<std::vector<std::string>> pickups; // 2 * numLevels
193
     std::vector<std::vector<std::string>> pickups; // 2 * numLevels
189
     std::vector<std::vector<std::string>> keys; // 4 * numLevels
194
     std::vector<std::vector<std::string>> keys; // 4 * numLevels

+ 48
- 17
src/Script.cpp View File

81
 
81
 
82
     // More strings...
82
     // More strings...
83
     readStringPackage(f, gameStrings, numGameStrings);
83
     readStringPackage(f, gameStrings, numGameStrings);
84
-    readStringPackage(f, pcStrings, 41);
84
+    readStringPackage(f, pcStrings, numPCStrings);
85
     readStringPackage(f, puzzles[0], numLevels);
85
     readStringPackage(f, puzzles[0], numLevels);
86
     readStringPackage(f, puzzles[1], numLevels);
86
     readStringPackage(f, puzzles[1], numLevels);
87
     readStringPackage(f, puzzles[2], numLevels);
87
     readStringPackage(f, puzzles[2], numLevels);
130
     uint16_t numBytes = f.readU16();
130
     uint16_t numBytes = f.readU16();
131
     assertEqual(numBytes % 2, 0); // 16 bit opcodes and operands
131
     assertEqual(numBytes % 2, 0); // 16 bit opcodes and operands
132
 
132
 
133
-    // Only TR2, not TR3, has 6 "filler bytes" hex 13 00 14 00 15 00 here
134
-    // We need to skip these
135
-    uint16_t hack1 = f.readU16();
136
-    uint16_t hack2 = f.readU16();
137
-    uint16_t hack3 = f.readU16();
138
-    uint16_t hackStart = 0;
139
     uint16_t *list = new uint16_t[(numBytes + 6) / 2];
133
     uint16_t *list = new uint16_t[(numBytes + 6) / 2];
140
-    if ((hack1 == 19) && (hack2 == 20) && (hack3 == 21)) {
141
-        numBytes += 6;
134
+    for (uint16_t i = 0; i < (numBytes / 2); i++) {
135
+        list[i] = f.readU16();
136
+    }
137
+
138
+    // TR2 for PC and PSX has 6 "filler bytes" hex 13 00 14 00 15 00
139
+    // (for TR3 for PSX, the filler is hex 15 00 16 00 17 00 instead)
140
+    // at the end of the script block. We need to skip these...
141
+    uint16_t hack[3];
142
+    hack[0] = f.readU16();
143
+    hack[1] = f.readU16();
144
+    hack[2] = f.readU16();
145
+    if (((hack[0] == 19) && (hack[1] == 20) && (hack[2] == 21))
146
+            || ((hack[0] == 21) && (hack[1] == 22) && (hack[2] == 23))) {
147
+        list[numBytes / 2] = hack[0];
148
+        list[(numBytes / 2) + 1] = hack[1];
149
+        list[(numBytes / 2) + 2] = hack[2];
142
     } else {
150
     } else {
143
-        list[0] = hack1;
144
-        list[1] = hack2;
145
-        list[2] = hack3;
146
-        hackStart = 3;
151
+        f.seek(f.tell() - 6);
147
     }
152
     }
148
 
153
 
149
-    for (uint16_t i = hackStart; i < (numBytes / 2); i++) {
150
-        list[i] = f.readU16();
154
+    // TR2 for PSX has 64 bytes with unknown content (not zero!) here,
155
+    // TR3 for PSX has 40 bytes. We try to identify and skip them...
156
+    // This is also currently used to set the platform specific string count
157
+    hack[0] = f.readU16();
158
+    hack[1] = f.readU16();
159
+    hack[2] = f.readU16();
160
+    if ((hack[0] == 1) && (hack[1] == 0) && (hack[2] == 864)) {
161
+        f.seek(f.tell() + 58);
162
+        numPCStrings = 80; // TR2 has 80 PSX Strings
163
+    } else if ((hack[0] == 1) && (hack[1] == 0) && (hack[2] == 817)) {
164
+        f.seek(f.tell() + 34);
165
+        numPCStrings = 80; // TR3 also has 80 PSX Strings
166
+    } else {
167
+        f.seek(f.tell() - 6);
168
+        numPCStrings = 41;
151
     }
169
     }
152
 
170
 
153
     for (unsigned int i = 0; i < n; i++) {
171
     for (unsigned int i = 0; i < n; i++) {
154
         unsigned int end = offset[i] / 2;
172
         unsigned int end = offset[i] / 2;
155
 
173
 
174
+        // We need to detect the OP_END opcode marking the end of a
175
+        // script sequence (like the '\0' for the strings).
176
+        // However, the numerical value of OP_END could also be used
177
+        // as an operand for another opcode, so we have to check for this
156
         bool readingOperand = false;
178
         bool readingOperand = false;
157
         while (readingOperand || (list[end] != OP_END)) {
179
         while (readingOperand || (list[end] != OP_END)) {
158
             if (readingOperand) {
180
             if (readingOperand) {
192
     return levelFilenames.at(i);
214
     return levelFilenames.at(i);
193
 }
215
 }
194
 
216
 
217
+unsigned int Script::pictureCount() {
218
+    return numPictures;
219
+}
220
+
221
+std::string Script::getPictureFilename(unsigned int i) {
222
+    assert(i < numPictures);
223
+    return pictureFilenames.at(i);
224
+}
225
+
195
 unsigned int Script::cutsceneCount() {
226
 unsigned int Script::cutsceneCount() {
196
     return numCutscenes;
227
     return numCutscenes;
197
 }
228
 }
229
 }
260
 }
230
 
261
 
231
 unsigned int Script::pcStringCount() {
262
 unsigned int Script::pcStringCount() {
232
-    return 41;
263
+    return numPCStrings;
233
 }
264
 }
234
 
265
 
235
 std::string Script::getPCString(unsigned int i) {
266
 std::string Script::getPCString(unsigned int i) {
236
-    assert(i < 41);
267
+    assert(i < numPCStrings);
237
     return pcStrings.at(i);
268
     return pcStrings.at(i);
238
 }
269
 }
239
 
270
 

+ 9
- 0
test/Script.cpp View File

51
         if (strings) {
51
         if (strings) {
52
             printStrings(s.levelCount(), s.getLevelName, "Level Names");
52
             printStrings(s.levelCount(), s.getLevelName, "Level Names");
53
             printStrings(s.levelCount(), s.getLevelFilename, "Level Filenames");
53
             printStrings(s.levelCount(), s.getLevelFilename, "Level Filenames");
54
+            printStrings(s.pictureCount(), s.getPictureFilename, "Picture Filenames");
54
             printStrings(s.cutsceneCount(), s.getCutsceneFilename, "Cutscenes");
55
             printStrings(s.cutsceneCount(), s.getCutsceneFilename, "Cutscenes");
55
             printStrings(s.titleCount(), s.getTitleFilename, "Titles");
56
             printStrings(s.titleCount(), s.getTitleFilename, "Titles");
56
             printStrings(s.videoCount(), s.getVideoFilename, "Videos");
57
             printStrings(s.videoCount(), s.getVideoFilename, "Videos");
123
     error = test(f, !((argc > 1) && (argv[1][0] == 's')));
124
     error = test(f, !((argc > 1) && (argv[1][0] == 's')));
124
     delete [] f;
125
     delete [] f;
125
 
126
 
127
+    if (error != 0)
128
+        return error;
129
+
130
+    std::cout << std::endl << "Tomb Raider 3 PSX:" << std::endl;
131
+    f = fullPath("~/.OpenRaider/paks/tr3_psx/TOMBPSX.DAT", 0);
132
+    error = test(f, !((argc > 1) && (argv[1][0] == 's')));
133
+    delete [] f;
134
+
126
     return error;
135
     return error;
127
 }
136
 }
128
 
137
 

Loading…
Cancel
Save