ソースを参照

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

Thomas Buck 10年前
コミット
63765138fb
4個のファイルの変更68行の追加22行の削除
  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 ファイルの表示

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

+ 10
- 5
include/Script.h ファイルの表示

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

+ 48
- 17
src/Script.cpp ファイルの表示

@@ -81,7 +81,7 @@ int Script::load(const char *file) {
81 81
 
82 82
     // More strings...
83 83
     readStringPackage(f, gameStrings, numGameStrings);
84
-    readStringPackage(f, pcStrings, 41);
84
+    readStringPackage(f, pcStrings, numPCStrings);
85 85
     readStringPackage(f, puzzles[0], numLevels);
86 86
     readStringPackage(f, puzzles[1], numLevels);
87 87
     readStringPackage(f, puzzles[2], numLevels);
@@ -130,29 +130,51 @@ void Script::readScriptPackage(BinaryFile &f, std::vector<std::vector<uint16_t>>
130 130
     uint16_t numBytes = f.readU16();
131 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 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 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 171
     for (unsigned int i = 0; i < n; i++) {
154 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 178
         bool readingOperand = false;
157 179
         while (readingOperand || (list[end] != OP_END)) {
158 180
             if (readingOperand) {
@@ -192,6 +214,15 @@ std::string Script::getLevelFilename(unsigned int i) {
192 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 226
 unsigned int Script::cutsceneCount() {
196 227
     return numCutscenes;
197 228
 }
@@ -229,11 +260,11 @@ std::string Script::getGameString(unsigned int i) {
229 260
 }
230 261
 
231 262
 unsigned int Script::pcStringCount() {
232
-    return 41;
263
+    return numPCStrings;
233 264
 }
234 265
 
235 266
 std::string Script::getPCString(unsigned int i) {
236
-    assert(i < 41);
267
+    assert(i < numPCStrings);
237 268
     return pcStrings.at(i);
238 269
 }
239 270
 

+ 9
- 0
test/Script.cpp ファイルの表示

@@ -51,6 +51,7 @@ namespace {
51 51
         if (strings) {
52 52
             printStrings(s.levelCount(), s.getLevelName, "Level Names");
53 53
             printStrings(s.levelCount(), s.getLevelFilename, "Level Filenames");
54
+            printStrings(s.pictureCount(), s.getPictureFilename, "Picture Filenames");
54 55
             printStrings(s.cutsceneCount(), s.getCutsceneFilename, "Cutscenes");
55 56
             printStrings(s.titleCount(), s.getTitleFilename, "Titles");
56 57
             printStrings(s.videoCount(), s.getVideoFilename, "Videos");
@@ -123,6 +124,14 @@ int main(int argc, char *argv[]) {
123 124
     error = test(f, !((argc > 1) && (argv[1][0] == 's')));
124 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 135
     return error;
127 136
 }
128 137
 

読み込み中…
キャンセル
保存