Mercurial > vba-linux
comparison src/common/movie.cpp @ 33:44974c3e093b
found source of problem for video recording
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Mon, 05 Mar 2012 01:25:11 -0600 |
parents | f9f4f1b99eed |
children | 6bde5b67a2b8 |
comparison
equal
deleted
inserted
replaced
32:0dc331ec7f27 | 33:44974c3e093b |
---|---|
71 static int prevEmulatorType, prevBorder, prevWinBorder, prevBorderAuto; | 71 static int prevEmulatorType, prevBorder, prevWinBorder, prevBorderAuto; |
72 | 72 |
73 // little-endian integer pop/push functions: | 73 // little-endian integer pop/push functions: |
74 static inline uint32 Pop32(const uint8 * &ptr) | 74 static inline uint32 Pop32(const uint8 * &ptr) |
75 { | 75 { |
76 uint32 v = (ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24)); | 76 uint32 v = (ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24)); |
77 ptr += 4; | 77 ptr += 4; |
78 return v; | 78 return v; |
79 } | 79 } |
80 | 80 |
81 static inline uint16 Pop16(const uint8 * &ptr) /* const version */ | 81 static inline uint16 Pop16(const uint8 * &ptr) /* const version */ |
82 { | 82 { |
83 uint16 v = (ptr[0] | (ptr[1] << 8)); | 83 uint16 v = (ptr[0] | (ptr[1] << 8)); |
84 ptr += 2; | 84 ptr += 2; |
85 return v; | 85 return v; |
86 } | 86 } |
87 | 87 |
88 static inline uint16 Pop16(uint8 * &ptr) /* non-const version */ | 88 static inline uint16 Pop16(uint8 * &ptr) /* non-const version */ |
89 { | 89 { |
90 uint16 v = (ptr[0] | (ptr[1] << 8)); | 90 uint16 v = (ptr[0] | (ptr[1] << 8)); |
91 ptr += 2; | 91 ptr += 2; |
92 return v; | 92 return v; |
93 } | 93 } |
94 | 94 |
95 static inline uint8 Pop8(const uint8 * &ptr) | 95 static inline uint8 Pop8(const uint8 * &ptr) |
96 { | 96 { |
97 return *(ptr)++; | 97 return *(ptr)++; |
98 } | 98 } |
99 | 99 |
100 static inline void Push32(uint32 v, uint8 * &ptr) | 100 static inline void Push32(uint32 v, uint8 * &ptr) |
101 { | 101 { |
102 ptr[0] = (uint8)(v & 0xff); | 102 ptr[0] = (uint8)(v & 0xff); |
103 ptr[1] = (uint8)((v >> 8) & 0xff); | 103 ptr[1] = (uint8)((v >> 8) & 0xff); |
104 ptr[2] = (uint8)((v >> 16) & 0xff); | 104 ptr[2] = (uint8)((v >> 16) & 0xff); |
105 ptr[3] = (uint8)((v >> 24) & 0xff); | 105 ptr[3] = (uint8)((v >> 24) & 0xff); |
106 ptr += 4; | 106 ptr += 4; |
107 } | 107 } |
108 | 108 |
109 static inline void Push16(uint16 v, uint8 * &ptr) | 109 static inline void Push16(uint16 v, uint8 * &ptr) |
110 { | 110 { |
111 ptr[0] = (uint8)(v & 0xff); | 111 ptr[0] = (uint8)(v & 0xff); |
112 ptr[1] = (uint8)((v >> 8) & 0xff); | 112 ptr[1] = (uint8)((v >> 8) & 0xff); |
113 ptr += 2; | 113 ptr += 2; |
114 } | 114 } |
115 | 115 |
116 static inline void Push8(uint8 v, uint8 * &ptr) | 116 static inline void Push8(uint8 v, uint8 * &ptr) |
117 { | 117 { |
118 *ptr++ = v; | 118 *ptr++ = v; |
119 } | 119 } |
120 | 120 |
121 // little-endian integer read/write functions: | 121 // little-endian integer read/write functions: |
122 static inline uint16 Read16(const uint8 *ptr) | 122 static inline uint16 Read16(const uint8 *ptr) |
123 { | 123 { |
124 return ptr[0] | (ptr[1] << 8); | 124 return ptr[0] | (ptr[1] << 8); |
125 } | 125 } |
126 | 126 |
127 static inline void Write16(uint16 v, uint8 *ptr) | 127 static inline void Write16(uint16 v, uint8 *ptr) |
128 { | 128 { |
129 ptr[0] = uint8(v & 0xff); | 129 ptr[0] = uint8(v & 0xff); |
130 ptr[1] = uint8((v >> 8) & 0xff); | 130 ptr[1] = uint8((v >> 8) & 0xff); |
131 } | 131 } |
132 | 132 |
133 static long file_length(FILE *fp) | 133 static long file_length(FILE *fp) |
134 { | 134 { |
135 long cur_pos = ftell(fp); | 135 long cur_pos = ftell(fp); |
136 fseek(fp, 0, SEEK_END); | 136 fseek(fp, 0, SEEK_END); |
137 long length = ftell(fp); | 137 long length = ftell(fp); |
138 fseek(fp, cur_pos, SEEK_SET); | 138 fseek(fp, cur_pos, SEEK_SET); |
139 return length; | 139 return length; |
140 } | 140 } |
141 | 141 |
142 static int bytes_per_frame(SMovie &mov) | 142 static int bytes_per_frame(SMovie &mov) |
143 { | 143 { |
144 int num_controllers = 0; | 144 int num_controllers = 0; |
145 | 145 |
146 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) | 146 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) |
147 if (mov.header.controllerFlags & MOVIE_CONTROLLER(i)) | 147 if (mov.header.controllerFlags & MOVIE_CONTROLLER(i)) |
148 ++num_controllers; | 148 ++num_controllers; |
149 | 149 |
150 return CONTROLLER_DATA_SIZE * num_controllers; | 150 return CONTROLLER_DATA_SIZE * num_controllers; |
151 } | 151 } |
152 | 152 |
153 static void reserve_buffer_space(uint32 space_needed) | 153 static void reserve_buffer_space(uint32 space_needed) |
154 { | 154 { |
155 if (space_needed > Movie.inputBufferSize) | 155 if (space_needed > Movie.inputBufferSize) |
156 { | 156 { |
157 uint32 ptr_offset = Movie.inputBufferPtr - Movie.inputBuffer; | 157 uint32 ptr_offset = Movie.inputBufferPtr - Movie.inputBuffer; |
158 uint32 alloc_chunks = (space_needed - 1) / BUFFER_GROWTH_SIZE + 1; | 158 uint32 alloc_chunks = (space_needed - 1) / BUFFER_GROWTH_SIZE + 1; |
159 uint32 old_size = Movie.inputBufferSize; | 159 uint32 old_size = Movie.inputBufferSize; |
160 Movie.inputBufferSize = BUFFER_GROWTH_SIZE * alloc_chunks; | 160 Movie.inputBufferSize = BUFFER_GROWTH_SIZE * alloc_chunks; |
161 Movie.inputBuffer = (uint8 *)realloc(Movie.inputBuffer, Movie.inputBufferSize); | 161 Movie.inputBuffer = (uint8 *)realloc(Movie.inputBuffer, Movie.inputBufferSize); |
162 // FIXME: this only fixes the random input problem during dma-frame-skip, but not the skip | 162 // FIXME: this only fixes the random input problem during dma-frame-skip, but not the skip |
163 memset(Movie.inputBuffer + old_size, 0, Movie.inputBufferSize - old_size); | 163 memset(Movie.inputBuffer + old_size, 0, Movie.inputBufferSize - old_size); |
164 Movie.inputBufferPtr = Movie.inputBuffer + ptr_offset; | 164 Movie.inputBufferPtr = Movie.inputBuffer + ptr_offset; |
165 } | 165 } |
166 } | 166 } |
167 | 167 |
168 static int read_movie_header(FILE *file, SMovie &movie) | 168 static int read_movie_header(FILE *file, SMovie &movie) |
169 { | 169 { |
170 assert(file != NULL); | 170 assert(file != NULL); |
171 assert(VBM_HEADER_SIZE == sizeof(SMovieFileHeader)); // sanity check on the header type definition | 171 assert(VBM_HEADER_SIZE == sizeof(SMovieFileHeader)); // sanity check on the header type definition |
172 | 172 |
173 uint8 headerData [VBM_HEADER_SIZE]; | 173 uint8 headerData [VBM_HEADER_SIZE]; |
174 | 174 |
175 if (fread(headerData, 1, VBM_HEADER_SIZE, file) != VBM_HEADER_SIZE) | 175 if (fread(headerData, 1, VBM_HEADER_SIZE, file) != VBM_HEADER_SIZE) |
176 return MOVIE_WRONG_FORMAT; // if we failed to read in all VBM_HEADER_SIZE bytes of the header | 176 return MOVIE_WRONG_FORMAT; // if we failed to read in all VBM_HEADER_SIZE bytes of the header |
177 | 177 |
178 const uint8 * ptr = headerData; | 178 const uint8 * ptr = headerData; |
179 SMovieFileHeader &header = movie.header; | 179 SMovieFileHeader &header = movie.header; |
180 | 180 |
181 header.magic = Pop32(ptr); | 181 header.magic = Pop32(ptr); |
182 if (header.magic != VBM_MAGIC) | 182 if (header.magic != VBM_MAGIC) |
183 return MOVIE_WRONG_FORMAT; | 183 return MOVIE_WRONG_FORMAT; |
184 | 184 |
185 header.version = Pop32(ptr); | 185 header.version = Pop32(ptr); |
186 if (header.version != VBM_VERSION) | 186 if (header.version != VBM_VERSION) |
187 return MOVIE_WRONG_VERSION; | 187 return MOVIE_WRONG_VERSION; |
188 | 188 |
189 header.uid = Pop32(ptr); | 189 header.uid = Pop32(ptr); |
190 header.length_frames = Pop32(ptr) + 1; // HACK: add 1 to the length for compatibility | 190 header.length_frames = Pop32(ptr) + 1; // HACK: add 1 to the length for compatibility |
191 header.rerecord_count = Pop32(ptr); | 191 header.rerecord_count = Pop32(ptr); |
192 | 192 |
193 header.startFlags = Pop8(ptr); | 193 header.startFlags = Pop8(ptr); |
194 header.controllerFlags = Pop8(ptr); | 194 header.controllerFlags = Pop8(ptr); |
195 header.typeFlags = Pop8(ptr); | 195 header.typeFlags = Pop8(ptr); |
196 header.optionFlags = Pop8(ptr); | 196 header.optionFlags = Pop8(ptr); |
197 | 197 |
198 header.saveType = Pop32(ptr); | 198 header.saveType = Pop32(ptr); |
199 header.flashSize = Pop32(ptr); | 199 header.flashSize = Pop32(ptr); |
200 header.gbEmulatorType = Pop32(ptr); | 200 header.gbEmulatorType = Pop32(ptr); |
201 | 201 |
202 for (int i = 0; i < 12; i++) | 202 for (int i = 0; i < 12; i++) |
203 header.romTitle[i] = Pop8(ptr); | 203 header.romTitle[i] = Pop8(ptr); |
204 | 204 |
205 header.minorVersion = Pop8(ptr); | 205 header.minorVersion = Pop8(ptr); |
206 | 206 |
207 header.romCRC = Pop8(ptr); | 207 header.romCRC = Pop8(ptr); |
208 header.romOrBiosChecksum = Pop16(ptr); | 208 header.romOrBiosChecksum = Pop16(ptr); |
209 header.romGameCode = Pop32(ptr); | 209 header.romGameCode = Pop32(ptr); |
210 | 210 |
211 header.offset_to_savestate = Pop32(ptr); | 211 header.offset_to_savestate = Pop32(ptr); |
212 header.offset_to_controller_data = Pop32(ptr); | 212 header.offset_to_controller_data = Pop32(ptr); |
213 | 213 |
214 return MOVIE_SUCCESS; | 214 return MOVIE_SUCCESS; |
215 } | 215 } |
216 | 216 |
217 static void write_movie_header(FILE *file, const SMovie &movie) | 217 static void write_movie_header(FILE *file, const SMovie &movie) |
218 { | 218 { |
219 assert(ftell(file) == 0); // we assume file points to beginning of movie file | 219 assert(ftell(file) == 0); // we assume file points to beginning of movie file |
220 | 220 |
221 uint8 headerData [VBM_HEADER_SIZE]; | 221 uint8 headerData [VBM_HEADER_SIZE]; |
222 uint8 *ptr = headerData; | 222 uint8 *ptr = headerData; |
223 const SMovieFileHeader &header = movie.header; | 223 const SMovieFileHeader &header = movie.header; |
224 | 224 |
225 Push32(header.magic, ptr); | 225 Push32(header.magic, ptr); |
226 Push32(header.version, ptr); | 226 Push32(header.version, ptr); |
227 | 227 |
228 Push32(header.uid, ptr); | 228 Push32(header.uid, ptr); |
229 Push32(header.length_frames - 1, ptr); // HACK: reduce the length by 1 for compatibility with certain faulty old tools | 229 Push32(header.length_frames - 1, ptr); // HACK: reduce the length by 1 for compatibility with certain faulty old tools |
230 // like TME | 230 // like TME |
231 Push32(header.rerecord_count, ptr); | 231 Push32(header.rerecord_count, ptr); |
232 | 232 |
233 Push8(header.startFlags, ptr); | 233 Push8(header.startFlags, ptr); |
234 Push8(header.controllerFlags, ptr); | 234 Push8(header.controllerFlags, ptr); |
235 Push8(header.typeFlags, ptr); | 235 Push8(header.typeFlags, ptr); |
236 Push8(header.optionFlags, ptr); | 236 Push8(header.optionFlags, ptr); |
237 | 237 |
238 Push32(header.saveType, ptr); | 238 Push32(header.saveType, ptr); |
239 Push32(header.flashSize, ptr); | 239 Push32(header.flashSize, ptr); |
240 Push32(header.gbEmulatorType, ptr); | 240 Push32(header.gbEmulatorType, ptr); |
241 | 241 |
242 for (int i = 0; i < 12; ++i) | 242 for (int i = 0; i < 12; ++i) |
243 Push8(header.romTitle[i], ptr); | 243 Push8(header.romTitle[i], ptr); |
244 | 244 |
245 Push8(header.minorVersion, ptr); | 245 Push8(header.minorVersion, ptr); |
246 | 246 |
247 Push8(header.romCRC, ptr); | 247 Push8(header.romCRC, ptr); |
248 Push16(header.romOrBiosChecksum, ptr); | 248 Push16(header.romOrBiosChecksum, ptr); |
249 Push32(header.romGameCode, ptr); | 249 Push32(header.romGameCode, ptr); |
250 | 250 |
251 Push32(header.offset_to_savestate, ptr); | 251 Push32(header.offset_to_savestate, ptr); |
252 Push32(header.offset_to_controller_data, ptr); | 252 Push32(header.offset_to_controller_data, ptr); |
253 | 253 |
254 fwrite(headerData, 1, VBM_HEADER_SIZE, file); | 254 fwrite(headerData, 1, VBM_HEADER_SIZE, file); |
255 } | 255 } |
256 | 256 |
257 static void flush_movie_header() | 257 static void flush_movie_header() |
258 { | 258 { |
259 assert(Movie.file != 0 && "logical error!"); | 259 assert(Movie.file != 0 && "logical error!"); |
260 if (!Movie.file) | 260 if (!Movie.file) |
261 return; | 261 return; |
262 | 262 |
263 long originalPos = ftell(Movie.file); | 263 long originalPos = ftell(Movie.file); |
264 | 264 |
265 // (over-)write the header | 265 // (over-)write the header |
266 fseek(Movie.file, 0, SEEK_SET); | 266 fseek(Movie.file, 0, SEEK_SET); |
267 write_movie_header(Movie.file, Movie); | 267 write_movie_header(Movie.file, Movie); |
268 | 268 |
269 fflush(Movie.file); | 269 fflush(Movie.file); |
270 | 270 |
271 fseek(Movie.file, originalPos, SEEK_SET); | 271 fseek(Movie.file, originalPos, SEEK_SET); |
272 } | 272 } |
273 | 273 |
274 static void flush_movie_frames() | 274 static void flush_movie_frames() |
275 { | 275 { |
276 assert(Movie.file && "logical error!"); | 276 assert(Movie.file && "logical error!"); |
277 if (!Movie.file) | 277 if (!Movie.file) |
278 return; | 278 return; |
279 | 279 |
280 long originalPos = ftell(Movie.file); | 280 long originalPos = ftell(Movie.file); |
281 | 281 |
282 // overwrite the controller data | 282 // overwrite the controller data |
283 fseek(Movie.file, Movie.header.offset_to_controller_data, SEEK_SET); | 283 fseek(Movie.file, Movie.header.offset_to_controller_data, SEEK_SET); |
284 fwrite(Movie.inputBuffer, 1, Movie.bytesPerFrame * Movie.header.length_frames, Movie.file); | 284 fwrite(Movie.inputBuffer, 1, Movie.bytesPerFrame * Movie.header.length_frames, Movie.file); |
285 | 285 |
286 fflush(Movie.file); | 286 fflush(Movie.file); |
287 | 287 |
288 fseek(Movie.file, originalPos, SEEK_SET); | 288 fseek(Movie.file, originalPos, SEEK_SET); |
289 } | 289 } |
290 | 290 |
291 static void truncate_movie(long length) | 291 static void truncate_movie(long length) |
292 { | 292 { |
293 // truncate movie to length | 293 // truncate movie to length |
294 // NOTE: it's certain that the savestate block is never after the | 294 // NOTE: it's certain that the savestate block is never after the |
295 // controller data block, because the VBM format decrees it. | 295 // controller data block, because the VBM format decrees it. |
296 | 296 |
297 assert(Movie.file && length >= 0); | 297 assert(Movie.file && length >= 0); |
298 if (!Movie.file || length < 0) | 298 if (!Movie.file || length < 0) |
299 return; | 299 return; |
300 | 300 |
301 assert(Movie.header.offset_to_savestate <= Movie.header.offset_to_controller_data); | 301 assert(Movie.header.offset_to_savestate <= Movie.header.offset_to_controller_data); |
302 if (Movie.header.offset_to_savestate > Movie.header.offset_to_controller_data) | 302 if (Movie.header.offset_to_savestate > Movie.header.offset_to_controller_data) |
303 return; | 303 return; |
304 | 304 |
305 Movie.header.length_frames = length; | 305 Movie.header.length_frames = length; |
306 flush_movie_header(); | 306 flush_movie_header(); |
307 const long truncLen = long(Movie.header.offset_to_controller_data + Movie.bytesPerFrame * length); | 307 const long truncLen = long(Movie.header.offset_to_controller_data + Movie.bytesPerFrame * length); |
308 if (file_length(Movie.file) != truncLen) | 308 if (file_length(Movie.file) != truncLen) |
309 { | 309 { |
310 ftruncate(fileno(Movie.file), truncLen); | 310 ftruncate(fileno(Movie.file), truncLen); |
311 } | 311 } |
312 } | 312 } |
313 | 313 |
314 static void remember_input_state() | 314 static void remember_input_state() |
315 { | 315 { |
316 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) | 316 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) |
317 { | 317 { |
318 if (systemCartridgeType == 0) | 318 if (systemCartridgeType == 0) |
319 { | |
320 initialInputs[i] = u16(~P1 & 0x03FF); | |
321 } | |
322 else | |
323 { | |
324 extern int32 gbJoymask[4]; | |
325 for (int i = 0; i < 4; ++i) | |
326 initialInputs[i] = u16(gbJoymask[i] & 0xFFFF); | |
327 } | |
328 } | |
329 } | |
330 | |
331 static void change_state(MovieState new_state) | |
332 { | |
333 #if (defined(WIN32) && !defined(SDL)) | |
334 theApp.frameSearching = false; | |
335 theApp.frameSearchSkipping = false; | |
336 #endif | |
337 | |
338 if (new_state == MOVIE_STATE_NONE) | |
339 { | |
340 Movie.pauseFrame = -1; | |
341 | |
342 if (Movie.state == MOVIE_STATE_NONE) | |
343 return; | |
344 | |
345 truncate_movie(Movie.header.length_frames); | |
346 | |
347 fclose(Movie.file); | |
348 Movie.file = NULL; | |
349 Movie.currentFrame = 0; | |
350 #if (defined(WIN32) && !defined(SDL)) | |
351 // undo changes to border settings | |
352 { | |
353 gbBorderOn = prevBorder; | |
354 theApp.winGbBorderOn = prevWinBorder; | |
355 gbBorderAutomatic = prevBorderAuto; | |
356 systemGbBorderOn(); | |
357 } | |
358 #endif | |
359 gbEmulatorType = prevEmulatorType; | |
360 | |
361 extern int32 gbDMASpeedVersion; | |
362 gbDMASpeedVersion = 1; | |
363 | |
364 extern int32 gbEchoRAMFixOn; | |
365 gbEchoRAMFixOn = 1; | |
366 | |
367 gbNullInputHackTempEnabled = gbNullInputHackEnabled; | |
368 | |
369 if (Movie.inputBuffer) | |
370 { | |
371 free(Movie.inputBuffer); | |
372 Movie.inputBuffer = NULL; | |
373 } | |
374 } | |
375 else if (new_state == MOVIE_STATE_PLAY) | |
376 { | |
377 assert(Movie.file); | |
378 | |
379 // this would cause problems if not dealt with | |
380 if (Movie.currentFrame >= Movie.header.length_frames) | |
381 { | |
382 new_state = MOVIE_STATE_END; | |
383 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * Movie.header.length_frames; | |
384 } | |
385 } | |
386 else if (new_state == MOVIE_STATE_RECORD) | |
387 { | |
388 assert(Movie.file); | |
389 | |
390 // this would cause problems if not dealt with | |
391 if (Movie.currentFrame > Movie.header.length_frames) | |
392 { | |
393 new_state = MOVIE_STATE_END; | |
394 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * Movie.header.length_frames; | |
395 } | |
396 | |
397 fseek(Movie.file, Movie.header.offset_to_controller_data + Movie.bytesPerFrame * Movie.currentFrame, SEEK_SET); | |
398 } | |
399 | |
400 if (new_state == MOVIE_STATE_END && Movie.state != MOVIE_STATE_END) | |
401 { | |
402 #if defined(SDL) | |
403 systemClearJoypads(); | |
404 #endif | |
405 systemScreenMessage("Movie end"); | |
406 } | |
407 | |
408 Movie.state = new_state; | |
409 | |
410 // checking for movie end | |
411 bool willPause = false; | |
412 | |
413 // if the movie's been set to pause at a certain frame | |
414 if (Movie.state != MOVIE_STATE_NONE && Movie.pauseFrame >= 0 && Movie.currentFrame == (uint32)Movie.pauseFrame) | |
415 { | |
416 Movie.pauseFrame = -1; | |
417 willPause = true; | |
418 } | |
419 | |
420 if (Movie.state == MOVIE_STATE_END) | |
421 { | |
422 if (Movie.currentFrame == Movie.header.length_frames) | |
423 { | |
424 #if (defined(WIN32) && !defined(SDL)) | |
425 if (theApp.movieOnEndPause) | |
426 { | |
427 willPause = true; | |
428 } | |
429 #else | |
430 // SDL FIXME | |
431 #endif | |
432 | |
433 #if (defined(WIN32) && !defined(SDL)) | |
434 switch (theApp.movieOnEndBehavior) | |
435 { | |
436 case 1: | |
437 // the old behavior | |
438 //VBAMovieRestart(); | |
439 break; | |
440 case 2: | |
441 #else | |
442 // SDL FIXME | |
443 #endif | |
444 if (Movie.RecordedThisSession) | |
319 { | 445 { |
320 initialInputs[i] = u16(~P1 & 0x03FF); | 446 // if user has been recording this movie since the last time it started playing, |
447 // they probably don't want the movie to end now during playback, | |
448 // so switch back to recording when it reaches the end | |
449 VBAMovieSwitchToRecording(); | |
450 systemScreenMessage("Recording resumed"); | |
451 willPause = true; | |
321 } | 452 } |
322 else | |
323 { | |
324 extern int32 gbJoymask[4]; | |
325 for (int i = 0; i < 4; ++i) | |
326 initialInputs[i] = u16(gbJoymask[i] & 0xFFFF); | |
327 } | |
328 } | |
329 } | |
330 | |
331 static void change_state(MovieState new_state) | |
332 { | |
333 #if (defined(WIN32) && !defined(SDL)) | 453 #if (defined(WIN32) && !defined(SDL)) |
334 theApp.frameSearching = false; | 454 break; |
335 theApp.frameSearchSkipping = false; | 455 case 3: |
336 #endif | 456 // keep open |
337 | 457 break; |
338 if (new_state == MOVIE_STATE_NONE) | 458 case 0: |
339 { | 459 // fall through |
340 Movie.pauseFrame = -1; | 460 default: |
341 | 461 // close movie |
342 if (Movie.state == MOVIE_STATE_NONE) | 462 //VBAMovieStop(false); |
343 return; | 463 break; |
344 | 464 } |
345 truncate_movie(Movie.header.length_frames); | 465 #else |
346 | 466 // SDL FIXME |
347 fclose(Movie.file); | 467 #endif |
348 Movie.file = NULL; | 468 } |
349 Movie.currentFrame = 0; | 469 #if 1 |
470 else if (Movie.currentFrame > Movie.header.length_frames) | |
471 { | |
350 #if (defined(WIN32) && !defined(SDL)) | 472 #if (defined(WIN32) && !defined(SDL)) |
351 // undo changes to border settings | 473 switch (theApp.movieOnEndBehavior) |
352 { | 474 { |
353 gbBorderOn = prevBorder; | 475 case 1: |
354 theApp.winGbBorderOn = prevWinBorder; | 476 // FIXME: this should be delayed till the current frame ends |
355 gbBorderAutomatic = prevBorderAuto; | 477 VBAMovieRestart(); |
356 systemGbBorderOn(); | 478 break; |
357 } | 479 case 2: |
358 #endif | 480 // nothing |
359 gbEmulatorType = prevEmulatorType; | 481 break; |
360 | 482 case 3: |
361 extern int32 gbDMASpeedVersion; | 483 // keep open |
362 gbDMASpeedVersion = 1; | 484 break; |
363 | 485 case 0: |
364 extern int32 gbEchoRAMFixOn; | 486 // fall through |
365 gbEchoRAMFixOn = 1; | 487 default: |
366 | 488 // close movie |
367 gbNullInputHackTempEnabled = gbNullInputHackEnabled; | 489 VBAMovieStop(false); |
368 | 490 break; |
369 if (Movie.inputBuffer) | 491 } |
370 { | |
371 free(Movie.inputBuffer); | |
372 Movie.inputBuffer = NULL; | |
373 } | |
374 } | |
375 else if (new_state == MOVIE_STATE_PLAY) | |
376 { | |
377 assert(Movie.file); | |
378 | |
379 // this would cause problems if not dealt with | |
380 if (Movie.currentFrame >= Movie.header.length_frames) | |
381 { | |
382 new_state = MOVIE_STATE_END; | |
383 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * Movie.header.length_frames; | |
384 } | |
385 } | |
386 else if (new_state == MOVIE_STATE_RECORD) | |
387 { | |
388 assert(Movie.file); | |
389 | |
390 // this would cause problems if not dealt with | |
391 if (Movie.currentFrame > Movie.header.length_frames) | |
392 { | |
393 new_state = MOVIE_STATE_END; | |
394 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * Movie.header.length_frames; | |
395 } | |
396 | |
397 fseek(Movie.file, Movie.header.offset_to_controller_data + Movie.bytesPerFrame * Movie.currentFrame, SEEK_SET); | |
398 } | |
399 | |
400 if (new_state == MOVIE_STATE_END && Movie.state != MOVIE_STATE_END) | |
401 { | |
402 #if defined(SDL) | |
403 systemClearJoypads(); | |
404 #endif | |
405 systemScreenMessage("Movie end"); | |
406 } | |
407 | |
408 Movie.state = new_state; | |
409 | |
410 // checking for movie end | |
411 bool willPause = false; | |
412 | |
413 // if the movie's been set to pause at a certain frame | |
414 if (Movie.state != MOVIE_STATE_NONE && Movie.pauseFrame >= 0 && Movie.currentFrame == (uint32)Movie.pauseFrame) | |
415 { | |
416 Movie.pauseFrame = -1; | |
417 willPause = true; | |
418 } | |
419 | |
420 if (Movie.state == MOVIE_STATE_END) | |
421 { | |
422 if (Movie.currentFrame == Movie.header.length_frames) | |
423 { | |
424 #if (defined(WIN32) && !defined(SDL)) | |
425 if (theApp.movieOnEndPause) | |
426 { | |
427 willPause = true; | |
428 } | |
429 #else | 492 #else |
430 // SDL FIXME | 493 // SDLFIXME |
431 #endif | 494 #endif |
432 | 495 } |
433 #if (defined(WIN32) && !defined(SDL)) | 496 #endif |
434 switch (theApp.movieOnEndBehavior) | 497 } // end if (Movie.state == MOVIE_STATE_END) |
435 { | 498 |
436 case 1: | 499 if (willPause) |
437 // the old behavior | 500 { |
438 //VBAMovieRestart(); | 501 systemSetPause(true); |
439 break; | 502 } |
440 case 2: | |
441 #else | |
442 // SDL FIXME | |
443 #endif | |
444 if (Movie.RecordedThisSession) | |
445 { | |
446 // if user has been recording this movie since the last time it started playing, | |
447 // they probably don't want the movie to end now during playback, | |
448 // so switch back to recording when it reaches the end | |
449 VBAMovieSwitchToRecording(); | |
450 systemScreenMessage("Recording resumed"); | |
451 willPause = true; | |
452 } | |
453 #if (defined(WIN32) && !defined(SDL)) | |
454 break; | |
455 case 3: | |
456 // keep open | |
457 break; | |
458 case 0: | |
459 // fall through | |
460 default: | |
461 // close movie | |
462 //VBAMovieStop(false); | |
463 break; | |
464 } | |
465 #else | |
466 // SDL FIXME | |
467 #endif | |
468 } | |
469 #if 1 | |
470 else if (Movie.currentFrame > Movie.header.length_frames) | |
471 { | |
472 #if (defined(WIN32) && !defined(SDL)) | |
473 switch (theApp.movieOnEndBehavior) | |
474 { | |
475 case 1: | |
476 // FIXME: this should be delayed till the current frame ends | |
477 VBAMovieRestart(); | |
478 break; | |
479 case 2: | |
480 // nothing | |
481 break; | |
482 case 3: | |
483 // keep open | |
484 break; | |
485 case 0: | |
486 // fall through | |
487 default: | |
488 // close movie | |
489 VBAMovieStop(false); | |
490 break; | |
491 } | |
492 #else | |
493 // SDLFIXME | |
494 #endif | |
495 } | |
496 #endif | |
497 } // end if (Movie.state == MOVIE_STATE_END) | |
498 | |
499 if (willPause) | |
500 { | |
501 systemSetPause(true); | |
502 } | |
503 } | 503 } |
504 | 504 |
505 void VBAMovieInit() | 505 void VBAMovieInit() |
506 { | 506 { |
507 memset(&Movie, 0, sizeof(Movie)); | 507 memset(&Movie, 0, sizeof(Movie)); |
508 Movie.state = MOVIE_STATE_NONE; | 508 Movie.state = MOVIE_STATE_NONE; |
509 Movie.pauseFrame = -1; | 509 Movie.pauseFrame = -1; |
510 | 510 |
511 resetSignaled = false; | 511 resetSignaled = false; |
512 resetSignaledLast = false; | 512 resetSignaledLast = false; |
513 } | 513 } |
514 | 514 |
515 void VBAMovieGetRomInfo(const SMovie &movieInfo, char romTitle [12], uint32 &romGameCode, uint16 &checksum, uint8 &crc) | 515 void VBAMovieGetRomInfo(const SMovie &movieInfo, char romTitle [12], uint32 &romGameCode, uint16 &checksum, uint8 &crc) |
516 { | 516 { |
517 if (systemCartridgeType == 0) // GBA | 517 if (systemCartridgeType == 0) // GBA |
518 { | 518 { |
519 extern u8 *bios, *rom; | 519 extern u8 *bios, *rom; |
520 memcpy(romTitle, &rom[0xa0], 12); // GBA TITLE | 520 memcpy(romTitle, &rom[0xa0], 12); // GBA TITLE |
521 memcpy(&romGameCode, &rom[0xac], 4); // GBA ROM GAME CODE | 521 memcpy(&romGameCode, &rom[0xac], 4); // GBA ROM GAME CODE |
522 if ((movieInfo.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0) | 522 if ((movieInfo.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0) |
523 checksum = utilCalcBIOSChecksum(bios, 4); // GBA BIOS CHECKSUM | 523 checksum = utilCalcBIOSChecksum(bios, 4); // GBA BIOS CHECKSUM |
524 else | 524 else |
525 checksum = 0; | 525 checksum = 0; |
526 crc = rom[0xbd]; // GBA ROM CRC | 526 crc = rom[0xbd]; // GBA ROM CRC |
527 } | 527 } |
528 else // non-GBA | 528 else // non-GBA |
529 { | 529 { |
530 extern u8 *gbRom; | 530 extern u8 *gbRom; |
531 memcpy(romTitle, &gbRom[0x134], 12); // GB TITLE (note this can be 15 but is truncated to 12) | 531 memcpy(romTitle, &gbRom[0x134], 12); // GB TITLE (note this can be 15 but is truncated to 12) |
532 romGameCode = (uint32)gbRom[0x146]; // GB ROM UNIT CODE | 532 romGameCode = (uint32)gbRom[0x146]; // GB ROM UNIT CODE |
533 | 533 |
534 checksum = (gbRom[0x14e] << 8) | gbRom[0x14f]; // GB ROM CHECKSUM, read from big-endian | 534 checksum = (gbRom[0x14e] << 8) | gbRom[0x14f]; // GB ROM CHECKSUM, read from big-endian |
535 crc = gbRom[0x14d]; // GB ROM CRC | 535 crc = gbRom[0x14d]; // GB ROM CRC |
536 } | 536 } |
537 } | 537 } |
538 | 538 |
539 #ifdef SDL | 539 #ifdef SDL |
540 static void GetBatterySaveName(char *buffer) | 540 static void GetBatterySaveName(char *buffer) |
541 { | 541 { |
542 extern char batteryDir[2048], filename[2048]; // from SDL.cpp | 542 extern char batteryDir[2048], filename[2048]; // from SDL.cpp |
543 extern char *sdlGetFilename(char *name); // from SDL.cpp | 543 extern char *sdlGetFilename(char *name); // from SDL.cpp |
544 if (batteryDir[0]) | 544 if (batteryDir[0]) |
545 sprintf(buffer, "%s/%s.sav", batteryDir, sdlGetFilename(filename)); | 545 sprintf(buffer, "%s/%s.sav", batteryDir, sdlGetFilename(filename)); |
546 else | 546 else |
547 sprintf(buffer, "%s.sav", filename); | 547 sprintf(buffer, "%s.sav", filename); |
548 } | 548 } |
549 | 549 |
550 #endif | 550 #endif |
551 | 551 |
552 static void SetPlayEmuSettings() | 552 static void SetPlayEmuSettings() |
553 { | 553 { |
554 prevEmulatorType = gbEmulatorType; | 554 prevEmulatorType = gbEmulatorType; |
555 gbEmulatorType = Movie.header.gbEmulatorType; | 555 gbEmulatorType = Movie.header.gbEmulatorType; |
556 | 556 |
557 #if (defined(WIN32) && !defined(SDL)) | 557 #if (defined(WIN32) && !defined(SDL)) |
558 // theApp.removeIntros = false; | 558 // theApp.removeIntros = false; |
559 theApp.skipBiosFile = (Movie.header.optionFlags & MOVIE_SETTING_SKIPBIOSFILE) != 0; | 559 theApp.skipBiosFile = (Movie.header.optionFlags & MOVIE_SETTING_SKIPBIOSFILE) != 0; |
560 theApp.useBiosFile = (Movie.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0; | 560 theApp.useBiosFile = (Movie.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0; |
561 #else | 561 #else |
562 extern int saveType, sdlRtcEnable, sdlFlashSize; // from SDL.cpp | 562 extern int saveType, sdlRtcEnable, sdlFlashSize; // from SDL.cpp |
563 extern bool8 useBios, skipBios, removeIntros; // from SDL.cpp | 563 extern bool8 useBios, skipBios, removeIntros; // from SDL.cpp |
564 useBios = (Movie.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0; | 564 useBios = (Movie.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0; |
565 skipBios = (Movie.header.optionFlags & MOVIE_SETTING_SKIPBIOSFILE) != 0; | 565 skipBios = (Movie.header.optionFlags & MOVIE_SETTING_SKIPBIOSFILE) != 0; |
566 removeIntros = false /*(Movie.header.optionFlags & MOVIE_SETTING_REMOVEINTROS) != 0*/; | 566 removeIntros = false /*(Movie.header.optionFlags & MOVIE_SETTING_REMOVEINTROS) != 0*/; |
567 #endif | 567 #endif |
568 | 568 |
569 extern void SetPrefetchHack(bool); | 569 extern void SetPrefetchHack(bool); |
570 if (systemCartridgeType == 0) // lag disablement applies only to GBA | 570 if (systemCartridgeType == 0) // lag disablement applies only to GBA |
571 SetPrefetchHack((Movie.header.optionFlags & MOVIE_SETTING_LAGHACK) != 0); | 571 SetPrefetchHack((Movie.header.optionFlags & MOVIE_SETTING_LAGHACK) != 0); |
572 | 572 |
573 gbNullInputHackTempEnabled = ((Movie.header.optionFlags & MOVIE_SETTING_GBINPUTHACK) != 0); | 573 gbNullInputHackTempEnabled = ((Movie.header.optionFlags & MOVIE_SETTING_GBINPUTHACK) != 0); |
574 | 574 |
575 // some GB/GBC games depend on the sound rate, so just use the highest one | 575 // some GB/GBC games depend on the sound rate, so just use the highest one |
576 systemSoundSetQuality(1); | 576 systemSoundSetQuality(1); |
577 useOldFrameTiming = false; | 577 useOldFrameTiming = false; |
578 | 578 |
579 extern int32 gbDMASpeedVersion; | 579 extern int32 gbDMASpeedVersion; |
580 if ((Movie.header.optionFlags & MOVIE_SETTING_GBCFF55FIX) != 0) | 580 if ((Movie.header.optionFlags & MOVIE_SETTING_GBCFF55FIX) != 0) |
581 gbDMASpeedVersion = 1; | 581 gbDMASpeedVersion = 1; |
582 else | 582 else |
583 gbDMASpeedVersion = 0; // old CGB HDMA5 timing was used | 583 gbDMASpeedVersion = 0; // old CGB HDMA5 timing was used |
584 | 584 |
585 extern int32 gbEchoRAMFixOn; | 585 extern int32 gbEchoRAMFixOn; |
586 if ((Movie.header.optionFlags & MOVIE_SETTING_GBECHORAMFIX) != 0) | 586 if ((Movie.header.optionFlags & MOVIE_SETTING_GBECHORAMFIX) != 0) |
587 gbEchoRAMFixOn = 1; | 587 gbEchoRAMFixOn = 1; |
588 else | 588 else |
589 gbEchoRAMFixOn = 0; | 589 gbEchoRAMFixOn = 0; |
590 | 590 |
591 #if (defined(WIN32) && !defined(SDL)) | 591 #if (defined(WIN32) && !defined(SDL)) |
592 rtcEnable((Movie.header.optionFlags & MOVIE_SETTING_RTCENABLE) != 0); | 592 rtcEnable((Movie.header.optionFlags & MOVIE_SETTING_RTCENABLE) != 0); |
593 theApp.winSaveType = Movie.header.saveType; | 593 theApp.winSaveType = Movie.header.saveType; |
594 theApp.winFlashSize = Movie.header.flashSize; | 594 theApp.winFlashSize = Movie.header.flashSize; |
595 | 595 |
596 prevBorder = gbBorderOn; | 596 prevBorder = gbBorderOn; |
597 prevWinBorder = theApp.winGbBorderOn; | 597 prevWinBorder = theApp.winGbBorderOn; |
598 prevBorderAuto = gbBorderAutomatic; | 598 prevBorderAuto = gbBorderAutomatic; |
599 if ((gbEmulatorType == 2 || gbEmulatorType == 5) | 599 if ((gbEmulatorType == 2 || gbEmulatorType == 5) |
600 && !theApp.hideMovieBorder) // games played in SGB mode can have a border | 600 && !theApp.hideMovieBorder) // games played in SGB mode can have a border |
601 { | 601 { |
602 gbBorderOn = true; | 602 gbBorderOn = true; |
603 theApp.winGbBorderOn = true; | 603 theApp.winGbBorderOn = true; |
604 gbBorderAutomatic = false; | 604 gbBorderAutomatic = false; |
605 } | 605 } |
606 else | 606 else |
607 { | 607 { |
608 gbBorderOn = false; | 608 gbBorderOn = false; |
609 theApp.winGbBorderOn = false; | 609 theApp.winGbBorderOn = false; |
610 gbBorderAutomatic = false; | 610 gbBorderAutomatic = false; |
611 if (theApp.hideMovieBorder) | 611 if (theApp.hideMovieBorder) |
612 { | 612 { |
613 theApp.hideMovieBorder = false; | 613 theApp.hideMovieBorder = false; |
614 prevBorder = false; // it might be expected behaviour that it stays hidden after the movie | 614 prevBorder = false; // it might be expected behaviour that it stays hidden after the movie |
615 } | 615 } |
616 } | 616 } |
617 systemGbBorderOn(); | 617 systemGbBorderOn(); |
618 #else | 618 #else |
619 sdlRtcEnable = (Movie.header.optionFlags & MOVIE_SETTING_RTCENABLE) != 0; | 619 sdlRtcEnable = (Movie.header.optionFlags & MOVIE_SETTING_RTCENABLE) != 0; |
620 saveType = Movie.header.saveType; | 620 saveType = Movie.header.saveType; |
621 sdlFlashSize = Movie.header.flashSize; | 621 sdlFlashSize = Movie.header.flashSize; |
622 #endif | 622 #endif |
623 } | 623 } |
624 | 624 |
625 static void HardResetAndSRAMClear() | 625 static void HardResetAndSRAMClear() |
626 { | 626 { |
627 #if (defined(WIN32) && !defined(SDL)) | 627 #if (defined(WIN32) && !defined(SDL)) |
628 winEraseBatteryFile(); // delete the damn SRAM file and keep it from being resurrected from RAM | 628 winEraseBatteryFile(); // delete the damn SRAM file and keep it from being resurrected from RAM |
629 MainWnd *temp = ((MainWnd *)theApp.m_pMainWnd); | 629 MainWnd *temp = ((MainWnd *)theApp.m_pMainWnd); |
630 if (!temp->winFileRun(true)) // restart running the game | 630 if (!temp->winFileRun(true)) // restart running the game |
631 { | 631 { |
632 temp->winFileClose(); | 632 temp->winFileClose(); |
633 } | 633 } |
634 #else | 634 #else |
635 char fname [1024]; | 635 char fname [1024]; |
636 GetBatterySaveName(fname); | 636 GetBatterySaveName(fname); |
637 remove(fname); // delete the damn SRAM file | 637 remove(fname); // delete the damn SRAM file |
638 | 638 |
639 // Henceforth, emuCleanUp means "clear out SRAM" | 639 // Henceforth, emuCleanUp means "clear out SRAM" |
640 //theEmulator.emuCleanUp(); // keep it from being resurrected from RAM <--This is wrong, it'll deallocate all variables --Felipe | 640 //theEmulator.emuCleanUp(); // keep it from being resurrected from RAM <--This is wrong, it'll deallocate all variables --Felipe |
641 | 641 |
642 /// FIXME the correct SDL code to call for a full restart isn't in a function yet | 642 /// FIXME the correct SDL code to call for a full restart isn't in a function yet |
643 theEmulator.emuReset(false); | 643 theEmulator.emuReset(false); |
644 #endif | 644 #endif |
645 } | 645 } |
646 | 646 |
647 int VBAMovieOpen(const char *filename, bool8 read_only) | 647 int VBAMovieOpen(const char *filename, bool8 read_only) |
648 { | 648 { |
649 loadingMovie = true; | 649 loadingMovie = true; |
650 uint8 movieReadOnly = read_only ? 1 : 0; | 650 uint8 movieReadOnly = read_only ? 1 : 0; |
651 | 651 |
652 FILE * file; | 652 FILE * file; |
653 STREAM stream; | 653 STREAM stream; |
654 int result; | 654 int result; |
655 int fn; | 655 int fn; |
656 | 656 |
657 char movie_filename[_MAX_PATH]; | 657 char movie_filename[_MAX_PATH]; |
658 #ifdef WIN32 | 658 #ifdef WIN32 |
659 _fullpath(movie_filename, filename, _MAX_PATH); | 659 _fullpath(movie_filename, filename, _MAX_PATH); |
660 #else | 660 #else |
661 // SDL FIXME: convert to fullpath | 661 // SDL FIXME: convert to fullpath |
662 strncpy(movie_filename, filename, _MAX_PATH); | 662 strncpy(movie_filename, filename, _MAX_PATH); |
663 movie_filename[_MAX_PATH - 1] = '\0'; | 663 movie_filename[_MAX_PATH - 1] = '\0'; |
664 #endif | 664 #endif |
665 | 665 |
666 if (movie_filename[0] == '\0') | 666 if (movie_filename[0] == '\0') |
667 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | |
668 | |
669 if (!emulating) | |
670 { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } | |
671 | |
672 // bool alreadyOpen = (Movie.file != NULL && _stricmp(movie_filename, Movie.filename) == 0); | |
673 | |
674 // if (alreadyOpen) | |
675 change_state(MOVIE_STATE_NONE); // have to stop current movie before trying to re-open it | |
676 | |
677 if (!(file = fopen(movie_filename, "rb+"))) | |
678 if (!(file = fopen(movie_filename, "rb"))) | |
679 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | |
680 //else | |
681 // movieReadOnly = 2; // we have to open the movie twice, no need to do this both times | |
682 | |
683 // if (!alreadyOpen) | |
684 // change_state(MOVIE_STATE_NONE); // stop current movie when we're able to open the other one | |
685 // | |
686 // if (!(file = fopen(movie_filename, "rb+"))) | |
687 // if(!(file = fopen(movie_filename, "rb"))) | |
688 // {loadingMovie = false; return MOVIE_FILE_NOT_FOUND;} | |
689 // else | |
690 // movieReadOnly = 2; | |
691 | |
692 // clear out the current movie | |
693 VBAMovieInit(); | |
694 | |
695 // read header | |
696 if ((result = read_movie_header(file, Movie)) != MOVIE_SUCCESS) | |
697 { | |
698 fclose(file); | |
699 { loadingMovie = false; return result; } | |
700 } | |
701 | |
702 // set emulator settings that make the movie more likely to stay synchronized | |
703 SetPlayEmuSettings(); | |
704 | |
705 // extern bool systemLoadBIOS(); | |
706 // if (!systemLoadBIOS()) | |
707 // { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } | |
708 | |
709 // read the metadata / author info from file | |
710 fread(Movie.authorInfo, 1, MOVIE_METADATA_SIZE, file); | |
711 fn = dup(fileno(file)); // XXX: why does this fail?? it returns -1 but errno == 0 | |
712 fclose(file); | |
713 | |
714 // apparently this lseek is necessary | |
715 lseek(fn, Movie.header.offset_to_savestate, SEEK_SET); | |
716 if (!(stream = utilGzReopen(fn, "rb"))) | |
717 if (!(stream = utilGzOpen(movie_filename, "rb"))) | |
718 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | |
719 else | |
720 fn = dup(fileno(file)); | |
721 // in case the above dup failed but opening the file normally doesn't fail | |
722 | |
723 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) | |
724 { | |
725 // load the snapshot | |
726 result = theEmulator.emuReadStateFromStream(stream) ? MOVIE_SUCCESS : MOVIE_WRONG_FORMAT; | |
727 | |
728 // FIXME: Kludge for conversion | |
729 remember_input_state(); | |
730 } | |
731 else if (Movie.header.startFlags & MOVIE_START_FROM_SRAM) | |
732 { | |
733 // 'soft' reset: | |
734 theEmulator.emuReset(false); | |
735 | |
736 // load the SRAM | |
737 result = theEmulator.emuReadBatteryFromStream(stream) ? MOVIE_SUCCESS : MOVIE_WRONG_FORMAT; | |
738 } | |
739 else | |
740 { | |
741 HardResetAndSRAMClear(); | |
742 } | |
743 | |
744 utilGzClose(stream); | |
745 | |
746 if (result != MOVIE_SUCCESS) | |
747 { loadingMovie = false; return result; } | |
748 | |
749 // if (!(file = fopen(movie_filename, /*read_only ? "rb" :*/ "rb+"))) // want to be able to switch out of read-only later | |
750 // { | |
751 // if(!Movie.readOnly || !(file = fopen(movie_filename, "rb"))) // try read-only if failed | |
752 // return MOVIE_FILE_NOT_FOUND; | |
753 // } | |
754 if (!(file = fopen(movie_filename, "rb+"))) | |
755 if (!(file = fopen(movie_filename, "rb"))) | |
756 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | |
757 else | |
758 movieReadOnly = 2; | |
759 | |
760 // recalculate length of movie from the file size | |
761 Movie.bytesPerFrame = bytes_per_frame(Movie); | |
762 fseek(file, 0, SEEK_END); | |
763 long fileSize = ftell(file); | |
764 Movie.header.length_frames = (fileSize - Movie.header.offset_to_controller_data) / Movie.bytesPerFrame; | |
765 | |
766 if (fseek(file, Movie.header.offset_to_controller_data, SEEK_SET)) | |
767 { fclose(file); loadingMovie = false; return MOVIE_WRONG_FORMAT; } | |
768 | |
769 strcpy(Movie.filename, movie_filename); | |
770 Movie.file = file; | |
771 Movie.inputBufferPtr = Movie.inputBuffer; | |
772 Movie.currentFrame = 0; | |
773 Movie.readOnly = movieReadOnly; | |
774 Movie.RecordedThisSession = false; | |
775 | |
776 // read controller data | |
777 uint32 to_read = Movie.bytesPerFrame * Movie.header.length_frames; | |
778 reserve_buffer_space(to_read); | |
779 fread(Movie.inputBuffer, 1, to_read, file); | |
780 | |
781 change_state(MOVIE_STATE_PLAY); | |
782 | |
783 char messageString[64] = "Movie "; | |
784 bool converted = false; | |
785 if (autoConvertMovieWhenPlaying) | |
786 { | |
787 int result = VBAMovieConvertCurrent(); | |
788 if (result == MOVIE_SUCCESS) | |
789 strcat(messageString, "converted and "); | |
790 else if (result == MOVIE_WRONG_VERSION) | |
791 strcat(messageString, "higher revision "); | |
792 } | |
793 | |
794 if (Movie.state == MOVIE_STATE_PLAY) | |
795 strcat(messageString, "replaying "); | |
796 else | |
797 strcat(messageString, "finished "); | |
798 if (Movie.readOnly) | |
799 strcat(messageString, "(read)"); | |
800 else | |
801 strcat(messageString, "(edit)"); | |
802 systemScreenMessage(messageString); | |
803 | |
804 VBAUpdateButtonPressDisplay(); | |
805 VBAUpdateFrameCountDisplay(); | |
806 systemRefreshScreen(); | |
807 | |
808 { loadingMovie = false; return MOVIE_SUCCESS; } | |
809 } | |
810 | |
811 static void SetRecordEmuSettings() | |
812 { | |
813 Movie.header.optionFlags = 0; | |
814 #if (defined(WIN32) && !defined(SDL)) | |
815 if (theApp.useBiosFile) | |
816 Movie.header.optionFlags |= MOVIE_SETTING_USEBIOSFILE; | |
817 if (theApp.skipBiosFile) | |
818 Movie.header.optionFlags |= MOVIE_SETTING_SKIPBIOSFILE; | |
819 if (rtcIsEnabled()) | |
820 Movie.header.optionFlags |= MOVIE_SETTING_RTCENABLE; | |
821 Movie.header.saveType = theApp.winSaveType; | |
822 Movie.header.flashSize = theApp.winFlashSize; | |
823 #else | |
824 extern int saveType, sdlRtcEnable, sdlFlashSize; // from SDL.cpp | |
825 extern bool8 useBios, skipBios; // from SDL.cpp | |
826 if (useBios) | |
827 Movie.header.optionFlags |= MOVIE_SETTING_USEBIOSFILE; | |
828 if (skipBios) | |
829 Movie.header.optionFlags |= MOVIE_SETTING_SKIPBIOSFILE; | |
830 if (sdlRtcEnable) | |
831 Movie.header.optionFlags |= MOVIE_SETTING_RTCENABLE; | |
832 Movie.header.saveType = saveType; | |
833 Movie.header.flashSize = sdlFlashSize; | |
834 #endif | |
835 prevEmulatorType = Movie.header.gbEmulatorType = gbEmulatorType; | |
836 | |
837 if (!memLagTempEnabled) | |
838 Movie.header.optionFlags |= MOVIE_SETTING_LAGHACK; | |
839 | |
840 if (gbNullInputHackTempEnabled) | |
841 Movie.header.optionFlags |= MOVIE_SETTING_GBINPUTHACK; | |
842 | |
843 Movie.header.optionFlags |= MOVIE_SETTING_GBCFF55FIX; | |
844 extern int32 gbDMASpeedVersion; | |
845 gbDMASpeedVersion = 1; | |
846 | |
847 Movie.header.optionFlags |= MOVIE_SETTING_GBECHORAMFIX; | |
848 extern int32 gbEchoRAMFixOn; | |
849 gbEchoRAMFixOn = 1; | |
850 | |
851 // some GB/GBC games depend on the sound rate, so just use the highest one | |
852 systemSoundSetQuality(1); | |
853 | |
854 useOldFrameTiming = false; | |
855 | |
856 #if (defined(WIN32) && !defined(SDL)) | |
857 // theApp.removeIntros = false; | |
858 | |
859 prevBorder = gbBorderOn; | |
860 prevWinBorder = theApp.winGbBorderOn; | |
861 prevBorderAuto = gbBorderAutomatic; | |
862 if (gbEmulatorType == 2 || gbEmulatorType == 5) // only games played in SGB mode will have a border | |
863 { | |
864 gbBorderOn = true; | |
865 theApp.winGbBorderOn = true; | |
866 gbBorderAutomatic = false; | |
867 } | |
868 else | |
869 { | |
870 gbBorderOn = false; | |
871 theApp.winGbBorderOn = false; | |
872 gbBorderAutomatic = false; | |
873 } | |
874 systemGbBorderOn(); | |
875 #else | |
876 /// SDLFIXME | |
877 #endif | |
878 } | |
879 | |
880 uint16 VBAMovieGetCurrentInputOf(int controllerNum, bool normalOnly) | |
881 { | |
882 if (controllerNum < 0 || controllerNum >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) | |
883 return 0; | |
884 | |
885 return normalOnly ? (currentButtons[controllerNum] & BUTTON_REGULAR_MASK) : currentButtons[controllerNum]; | |
886 } | |
887 | |
888 int VBAMovieCreate(const char *filename, const char *authorInfo, uint8 startFlags, uint8 controllerFlags, uint8 typeFlags) | |
889 { | |
890 // make sure at least one controller is enabled | |
891 if ((controllerFlags & MOVIE_CONTROLLERS_ANY_MASK) == 0) | |
892 return MOVIE_WRONG_FORMAT; | |
893 | |
894 if (!emulating) | |
895 return MOVIE_UNKNOWN_ERROR; | |
896 | |
897 loadingMovie = true; | |
898 | |
899 FILE * file; | |
900 STREAM stream; | |
901 int fn; | |
902 | |
903 char movie_filename [_MAX_PATH]; | |
904 #ifdef WIN32 | |
905 _fullpath(movie_filename, filename, _MAX_PATH); | |
906 #else | |
907 // FIXME: convert to fullpath | |
908 strncpy(movie_filename, filename, _MAX_PATH); | |
909 movie_filename[_MAX_PATH - 1] = '\0'; | |
910 #endif | |
911 | |
912 bool alreadyOpen = (Movie.file != NULL && stricmp(movie_filename, Movie.filename) == 0); | |
913 | |
914 if (alreadyOpen) | |
915 change_state(MOVIE_STATE_NONE); // have to stop current movie before trying to re-open it | |
916 | |
917 if (movie_filename[0] == '\0') | |
918 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | |
919 | |
920 if (!(file = fopen(movie_filename, "wb"))) | |
921 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | |
922 | |
923 if (!alreadyOpen) | |
924 change_state(MOVIE_STATE_NONE); // stop current movie when we're able to open the other one | |
925 | |
926 // clear out the current movie | |
927 printf("RLM: movie init\n"); | |
928 | |
929 VBAMovieInit(); | |
930 | |
931 // fill in the movie's header | |
932 Movie.header.uid = (uint32)time(NULL); | |
933 Movie.header.magic = VBM_MAGIC; | |
934 Movie.header.version = VBM_VERSION; | |
935 Movie.header.rerecord_count = 0; | |
936 Movie.header.length_frames = 0; | |
937 Movie.header.startFlags = startFlags; | |
938 Movie.header.controllerFlags = controllerFlags; | |
939 Movie.header.typeFlags = typeFlags; | |
940 Movie.header.minorVersion = VBM_REVISION; | |
941 | |
942 // set emulator settings that make the movie more likely to stay synchronized when it's later played back | |
943 SetRecordEmuSettings(); | |
944 | |
945 // set ROM and BIOS checksums and stuff | |
946 VBAMovieGetRomInfo(Movie, Movie.header.romTitle, Movie.header.romGameCode, Movie.header.romOrBiosChecksum, Movie.header.romCRC); | |
947 | |
948 printf("RLM: Writing movie header\n"); | |
949 // write the header to file | |
950 write_movie_header(file, Movie); | |
951 | |
952 printf("RLM: setting metadata\n"); | |
953 | |
954 // copy over the metadata / author info | |
955 VBAMovieSetMetadata("________________Robert McIntyre______________________________________________________________________________________________________________________________________________________________________________________________________________________"); | |
956 | |
957 printf("RLM: writing metadata\n"); | |
958 | |
959 // write the metadata / author info to file | |
960 | |
961 | |
962 fwrite(Movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, file); | |
963 | |
964 // write snapshot or SRAM if applicable | |
965 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT | |
966 || Movie.header.startFlags & MOVIE_START_FROM_SRAM) | |
967 { | |
968 Movie.header.offset_to_savestate = (uint32)ftell(file); | |
969 | |
970 // close the file and reopen it as a stream: | |
971 | |
972 fn = dup(fileno(file)); | |
973 fclose(file); | |
974 | |
975 if (!(stream = utilGzReopen(fn, "ab"))) // append mode to start at end, no seek necessary | |
667 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | 976 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } |
668 | 977 |
669 if (!emulating) | 978 // write the save data: |
670 { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } | 979 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) |
671 | 980 { |
672 // bool alreadyOpen = (Movie.file != NULL && _stricmp(movie_filename, Movie.filename) == 0); | 981 // save snapshot |
673 | 982 if (!theEmulator.emuWriteStateToStream(stream)) |
674 // if (alreadyOpen) | 983 { |
675 change_state(MOVIE_STATE_NONE); // have to stop current movie before trying to re-open it | 984 utilGzClose(stream); |
676 | 985 { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } |
677 if (!(file = fopen(movie_filename, "rb+"))) | 986 } |
678 if (!(file = fopen(movie_filename, "rb"))) | 987 } |
679 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | 988 else if (Movie.header.startFlags & MOVIE_START_FROM_SRAM) |
680 //else | 989 { |
681 // movieReadOnly = 2; // we have to open the movie twice, no need to do this both times | 990 // save SRAM |
682 | 991 if (!theEmulator.emuWriteBatteryToStream(stream)) |
683 // if (!alreadyOpen) | 992 { |
684 // change_state(MOVIE_STATE_NONE); // stop current movie when we're able to open the other one | 993 utilGzClose(stream); |
685 // | 994 { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } |
686 // if (!(file = fopen(movie_filename, "rb+"))) | 995 } |
687 // if(!(file = fopen(movie_filename, "rb"))) | 996 |
688 // {loadingMovie = false; return MOVIE_FILE_NOT_FOUND;} | 997 // 'soft' reset: |
689 // else | 998 theEmulator.emuReset(false); |
690 // movieReadOnly = 2; | 999 } |
691 | 1000 |
692 // clear out the current movie | 1001 utilGzClose(stream); |
693 VBAMovieInit(); | 1002 |
694 | 1003 // reopen the file and seek back to the end |
695 // read header | 1004 |
696 if ((result = read_movie_header(file, Movie)) != MOVIE_SUCCESS) | 1005 if (!(file = fopen(movie_filename, "rb+"))) |
697 { | 1006 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } |
698 fclose(file); | 1007 |
699 { loadingMovie = false; return result; } | 1008 fseek(file, 0, SEEK_END); |
700 } | 1009 } |
701 | 1010 else // no snapshot or SRAM |
702 // set emulator settings that make the movie more likely to stay synchronized | 1011 { |
703 SetPlayEmuSettings(); | 1012 HardResetAndSRAMClear(); |
704 | 1013 } |
705 // extern bool systemLoadBIOS(); | 1014 |
706 // if (!systemLoadBIOS()) | 1015 Movie.header.offset_to_controller_data = (uint32)ftell(file); |
707 // { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } | 1016 |
708 | 1017 strcpy(Movie.filename, movie_filename); |
709 // read the metadata / author info from file | 1018 Movie.file = file; |
710 fread(Movie.authorInfo, 1, MOVIE_METADATA_SIZE, file); | 1019 Movie.bytesPerFrame = bytes_per_frame(Movie); |
711 fn = dup(fileno(file)); // XXX: why does this fail?? it returns -1 but errno == 0 | 1020 Movie.inputBufferPtr = Movie.inputBuffer; |
712 fclose(file); | 1021 Movie.currentFrame = 0; |
713 | 1022 Movie.readOnly = false; |
714 // apparently this lseek is necessary | 1023 Movie.RecordedThisSession = true; |
715 lseek(fn, Movie.header.offset_to_savestate, SEEK_SET); | 1024 |
716 if (!(stream = utilGzReopen(fn, "rb"))) | 1025 change_state(MOVIE_STATE_RECORD); |
717 if (!(stream = utilGzOpen(movie_filename, "rb"))) | 1026 |
718 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | 1027 systemScreenMessage("Recording movie..."); |
719 else | 1028 { loadingMovie = false; return MOVIE_SUCCESS; } |
720 fn = dup(fileno(file)); | 1029 } |
721 // in case the above dup failed but opening the file normally doesn't fail | 1030 |
722 | 1031 void VBAUpdateButtonPressDisplay() |
723 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) | 1032 { |
724 { | 1033 uint32 keys = currentButtons[0] & BUTTON_REGULAR_RECORDING_MASK; |
725 // load the snapshot | 1034 |
726 result = theEmulator.emuReadStateFromStream(stream) ? MOVIE_SUCCESS : MOVIE_WRONG_FORMAT; | 1035 const static char KeyMap[] = { 'A', 'B', 's', 'S', '>', '<', '^', 'v', 'R', 'L', '!', '?', '{', '}', 'v', '^' }; |
727 | 1036 const static int KeyOrder[] = { 5, 6, 4, 7, 0, 1, 9, 8, 3, 2, 12, 15, 13, 14, 11, 10 }; // < ^ > v A B L R S s { = } _ |
728 // FIXME: Kludge for conversion | 1037 // ? ! |
729 remember_input_state(); | 1038 char buffer[256]; |
730 } | 1039 sprintf(buffer, " "); |
731 else if (Movie.header.startFlags & MOVIE_START_FROM_SRAM) | 1040 |
732 { | 1041 #ifndef WIN32 |
733 // 'soft' reset: | 1042 // don't bother color-coding autofire and such |
734 theEmulator.emuReset(false); | 1043 int i; |
735 | 1044 for (i = 0; i < 15; i++) |
736 // load the SRAM | 1045 { |
737 result = theEmulator.emuReadBatteryFromStream(stream) ? MOVIE_SUCCESS : MOVIE_WRONG_FORMAT; | 1046 int j = KeyOrder[i]; |
738 } | 1047 int mask = (1 << (j)); |
739 else | 1048 buffer[strlen(" ") + i] = ((keys & mask) != 0) ? KeyMap[j] : ' '; |
740 { | 1049 } |
741 HardResetAndSRAMClear(); | 1050 |
742 } | 1051 systemScreenMessage(buffer, 2, -1); |
743 | 1052 #else |
744 utilGzClose(stream); | 1053 const bool eraseAll = !theApp.inputDisplay; |
745 | 1054 uint32 autoHeldKeys = eraseAll ? 0 : theApp.autoHold & BUTTON_REGULAR_RECORDING_MASK; |
746 if (result != MOVIE_SUCCESS) | 1055 uint32 autoFireKeys = eraseAll ? 0 : (theApp.autoFire | theApp.autoFire2) & BUTTON_REGULAR_RECORDING_MASK; |
747 { loadingMovie = false; return result; } | 1056 uint32 pressedKeys = eraseAll ? 0 : keys; |
748 | 1057 |
749 // if (!(file = fopen(movie_filename, /*read_only ? "rb" :*/ "rb+"))) // want to be able to switch out of read-only later | 1058 char colorList[64]; |
750 // { | 1059 memset(colorList, 1, strlen(buffer)); |
751 // if(!Movie.readOnly || !(file = fopen(movie_filename, "rb"))) // try read-only if failed | 1060 |
752 // return MOVIE_FILE_NOT_FOUND; | 1061 if (!eraseAll) |
753 // } | 1062 { |
754 if (!(file = fopen(movie_filename, "rb+"))) | 1063 for (int i = 0; i < 15; i++) |
755 if (!(file = fopen(movie_filename, "rb"))) | 1064 { |
756 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | 1065 const int j = KeyOrder[i]; |
757 else | 1066 const int mask = (1 << (j)); |
758 movieReadOnly = 2; | 1067 bool pressed = (pressedKeys & mask) != 0; |
759 | 1068 const bool autoHeld = (autoHeldKeys & mask) != 0; |
760 // recalculate length of movie from the file size | 1069 const bool autoFired = (autoFireKeys & mask) != 0; |
761 Movie.bytesPerFrame = bytes_per_frame(Movie); | 1070 const bool erased = (lastKeys & mask) != 0 && (!pressed && !autoHeld && !autoFired); |
762 fseek(file, 0, SEEK_END); | 1071 extern int textMethod; |
763 long fileSize = ftell(file); | 1072 if (textMethod != 2 && (autoHeld || (autoFired && !pressed) || erased)) |
764 Movie.header.length_frames = (fileSize - Movie.header.offset_to_controller_data) / Movie.bytesPerFrame; | 1073 { |
765 | 1074 int colorNum = 1; // default is white |
766 if (fseek(file, Movie.header.offset_to_controller_data, SEEK_SET)) | 1075 if (autoHeld) |
767 { fclose(file); loadingMovie = false; return MOVIE_WRONG_FORMAT; } | 1076 colorNum += (pressed ? 2 : 1); // yellow if pressed, red if not |
768 | 1077 else if (autoFired) |
769 strcpy(Movie.filename, movie_filename); | 1078 colorNum += 5; // blue if autofired and not currently pressed |
770 Movie.file = file; | 1079 else if (erased) |
771 Movie.inputBufferPtr = Movie.inputBuffer; | 1080 colorNum += 8; // black on black |
772 Movie.currentFrame = 0; | 1081 |
773 Movie.readOnly = movieReadOnly; | 1082 colorList[strlen(" ") + i] = colorNum; |
774 Movie.RecordedThisSession = false; | 1083 pressed = true; |
775 | 1084 } |
776 // read controller data | 1085 buffer[strlen(" ") + i] = pressed ? KeyMap[j] : ' '; |
777 uint32 to_read = Movie.bytesPerFrame * Movie.header.length_frames; | 1086 } |
778 reserve_buffer_space(to_read); | 1087 } |
779 fread(Movie.inputBuffer, 1, to_read, file); | 1088 |
780 | 1089 lastKeys = currentButtons[0]; |
781 change_state(MOVIE_STATE_PLAY); | 1090 lastKeys |= theApp.autoHold & BUTTON_REGULAR_RECORDING_MASK; |
782 | 1091 lastKeys |= (theApp.autoFire | theApp.autoFire2) & BUTTON_REGULAR_RECORDING_MASK; |
783 char messageString[64] = "Movie "; | 1092 |
784 bool converted = false; | 1093 systemScreenMessage(buffer, 2, -1, colorList); |
785 if (autoConvertMovieWhenPlaying) | 1094 #endif |
786 { | 1095 } |
787 int result = VBAMovieConvertCurrent(); | 1096 |
788 if (result == MOVIE_SUCCESS) | 1097 void VBAUpdateFrameCountDisplay() |
789 strcat(messageString, "converted and "); | 1098 { |
790 else if (result == MOVIE_WRONG_VERSION) | 1099 const int MAGICAL_NUMBER = 64; // FIXME: this won't do any better, but only to remind you of sz issues |
791 strcat(messageString, "higher revision "); | 1100 char frameDisplayString[MAGICAL_NUMBER]; |
792 } | 1101 char lagFrameDisplayString[MAGICAL_NUMBER]; |
793 | 1102 char extraCountDisplayString[MAGICAL_NUMBER]; |
794 if (Movie.state == MOVIE_STATE_PLAY) | 1103 |
795 strcat(messageString, "replaying "); | |
796 else | |
797 strcat(messageString, "finished "); | |
798 if (Movie.readOnly) | |
799 strcat(messageString, "(read)"); | |
800 else | |
801 strcat(messageString, "(edit)"); | |
802 systemScreenMessage(messageString); | |
803 | |
804 VBAUpdateButtonPressDisplay(); | |
805 VBAUpdateFrameCountDisplay(); | |
806 systemRefreshScreen(); | |
807 | |
808 { loadingMovie = false; return MOVIE_SUCCESS; } | |
809 } | |
810 | |
811 static void SetRecordEmuSettings() | |
812 { | |
813 Movie.header.optionFlags = 0; | |
814 #if (defined(WIN32) && !defined(SDL)) | 1104 #if (defined(WIN32) && !defined(SDL)) |
815 if (theApp.useBiosFile) | 1105 if (theApp.frameCounter) |
816 Movie.header.optionFlags |= MOVIE_SETTING_USEBIOSFILE; | |
817 if (theApp.skipBiosFile) | |
818 Movie.header.optionFlags |= MOVIE_SETTING_SKIPBIOSFILE; | |
819 if (rtcIsEnabled()) | |
820 Movie.header.optionFlags |= MOVIE_SETTING_RTCENABLE; | |
821 Movie.header.saveType = theApp.winSaveType; | |
822 Movie.header.flashSize = theApp.winFlashSize; | |
823 #else | 1106 #else |
824 extern int saveType, sdlRtcEnable, sdlFlashSize; // from SDL.cpp | 1107 /// SDL FIXME |
825 extern bool8 useBios, skipBios; // from SDL.cpp | 1108 #endif |
826 if (useBios) | 1109 { |
827 Movie.header.optionFlags |= MOVIE_SETTING_USEBIOSFILE; | 1110 switch (Movie.state) |
828 if (skipBios) | 1111 { |
829 Movie.header.optionFlags |= MOVIE_SETTING_SKIPBIOSFILE; | 1112 case MOVIE_STATE_PLAY: |
830 if (sdlRtcEnable) | 1113 case MOVIE_STATE_END: |
831 Movie.header.optionFlags |= MOVIE_SETTING_RTCENABLE; | 1114 { |
832 Movie.header.saveType = saveType; | 1115 sprintf(frameDisplayString, "%d / %d", Movie.currentFrame, Movie.header.length_frames); |
833 Movie.header.flashSize = sdlFlashSize; | 1116 if (!Movie.readOnly) |
834 #endif | 1117 strcat(frameDisplayString, " (edit)"); |
835 prevEmulatorType = Movie.header.gbEmulatorType = gbEmulatorType; | 1118 break; |
836 | 1119 } |
837 if (!memLagTempEnabled) | 1120 case MOVIE_STATE_RECORD: |
838 Movie.header.optionFlags |= MOVIE_SETTING_LAGHACK; | 1121 { |
839 | 1122 sprintf(frameDisplayString, "%d (record)", Movie.currentFrame); |
840 if (gbNullInputHackTempEnabled) | 1123 break; |
841 Movie.header.optionFlags |= MOVIE_SETTING_GBINPUTHACK; | 1124 } |
842 | 1125 default: |
843 Movie.header.optionFlags |= MOVIE_SETTING_GBCFF55FIX; | 1126 { |
844 extern int32 gbDMASpeedVersion; | 1127 sprintf(frameDisplayString, "%d (no movie)", systemCounters.frameCount); |
845 gbDMASpeedVersion = 1; | 1128 break; |
846 | 1129 } |
847 Movie.header.optionFlags |= MOVIE_SETTING_GBECHORAMFIX; | 1130 } |
848 extern int32 gbEchoRAMFixOn; | |
849 gbEchoRAMFixOn = 1; | |
850 | |
851 // some GB/GBC games depend on the sound rate, so just use the highest one | |
852 systemSoundSetQuality(1); | |
853 | |
854 useOldFrameTiming = false; | |
855 | 1131 |
856 #if (defined(WIN32) && !defined(SDL)) | 1132 #if (defined(WIN32) && !defined(SDL)) |
857 // theApp.removeIntros = false; | 1133 if (theApp.lagCounter) |
858 | |
859 prevBorder = gbBorderOn; | |
860 prevWinBorder = theApp.winGbBorderOn; | |
861 prevBorderAuto = gbBorderAutomatic; | |
862 if (gbEmulatorType == 2 || gbEmulatorType == 5) // only games played in SGB mode will have a border | |
863 { | |
864 gbBorderOn = true; | |
865 theApp.winGbBorderOn = true; | |
866 gbBorderAutomatic = false; | |
867 } | |
868 else | |
869 { | |
870 gbBorderOn = false; | |
871 theApp.winGbBorderOn = false; | |
872 gbBorderAutomatic = false; | |
873 } | |
874 systemGbBorderOn(); | |
875 #else | |
876 /// SDLFIXME | |
877 #endif | |
878 } | |
879 | |
880 uint16 VBAMovieGetCurrentInputOf(int controllerNum, bool normalOnly) | |
881 { | |
882 if (controllerNum < 0 || controllerNum >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) | |
883 return 0; | |
884 | |
885 return normalOnly ? (currentButtons[controllerNum] & BUTTON_REGULAR_MASK) : currentButtons[controllerNum]; | |
886 } | |
887 | |
888 int VBAMovieCreate(const char *filename, const char *authorInfo, uint8 startFlags, uint8 controllerFlags, uint8 typeFlags) | |
889 { | |
890 // make sure at least one controller is enabled | |
891 if ((controllerFlags & MOVIE_CONTROLLERS_ANY_MASK) == 0) | |
892 return MOVIE_WRONG_FORMAT; | |
893 | |
894 if (!emulating) | |
895 return MOVIE_UNKNOWN_ERROR; | |
896 | |
897 loadingMovie = true; | |
898 | |
899 FILE * file; | |
900 STREAM stream; | |
901 int fn; | |
902 | |
903 char movie_filename [_MAX_PATH]; | |
904 #ifdef WIN32 | |
905 _fullpath(movie_filename, filename, _MAX_PATH); | |
906 #else | |
907 // FIXME: convert to fullpath | |
908 strncpy(movie_filename, filename, _MAX_PATH); | |
909 movie_filename[_MAX_PATH - 1] = '\0'; | |
910 #endif | |
911 | |
912 bool alreadyOpen = (Movie.file != NULL && stricmp(movie_filename, Movie.filename) == 0); | |
913 | |
914 if (alreadyOpen) | |
915 change_state(MOVIE_STATE_NONE); // have to stop current movie before trying to re-open it | |
916 | |
917 if (movie_filename[0] == '\0') | |
918 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | |
919 | |
920 if (!(file = fopen(movie_filename, "wb"))) | |
921 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | |
922 | |
923 if (!alreadyOpen) | |
924 change_state(MOVIE_STATE_NONE); // stop current movie when we're able to open the other one | |
925 | |
926 // clear out the current movie | |
927 VBAMovieInit(); | |
928 | |
929 // fill in the movie's header | |
930 Movie.header.uid = (uint32)time(NULL); | |
931 Movie.header.magic = VBM_MAGIC; | |
932 Movie.header.version = VBM_VERSION; | |
933 Movie.header.rerecord_count = 0; | |
934 Movie.header.length_frames = 0; | |
935 Movie.header.startFlags = startFlags; | |
936 Movie.header.controllerFlags = controllerFlags; | |
937 Movie.header.typeFlags = typeFlags; | |
938 Movie.header.minorVersion = VBM_REVISION; | |
939 | |
940 // set emulator settings that make the movie more likely to stay synchronized when it's later played back | |
941 SetRecordEmuSettings(); | |
942 | |
943 // set ROM and BIOS checksums and stuff | |
944 VBAMovieGetRomInfo(Movie, Movie.header.romTitle, Movie.header.romGameCode, Movie.header.romOrBiosChecksum, Movie.header.romCRC); | |
945 | |
946 // write the header to file | |
947 write_movie_header(file, Movie); | |
948 | |
949 // copy over the metadata / author info | |
950 VBAMovieSetMetadata(authorInfo); | |
951 | |
952 // write the metadata / author info to file | |
953 fwrite(Movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, file); | |
954 | |
955 // write snapshot or SRAM if applicable | |
956 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT | |
957 || Movie.header.startFlags & MOVIE_START_FROM_SRAM) | |
958 { | |
959 Movie.header.offset_to_savestate = (uint32)ftell(file); | |
960 | |
961 // close the file and reopen it as a stream: | |
962 | |
963 fn = dup(fileno(file)); | |
964 fclose(file); | |
965 | |
966 if (!(stream = utilGzReopen(fn, "ab"))) // append mode to start at end, no seek necessary | |
967 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | |
968 | |
969 // write the save data: | |
970 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) | |
971 { | |
972 // save snapshot | |
973 if (!theEmulator.emuWriteStateToStream(stream)) | |
974 { | |
975 utilGzClose(stream); | |
976 { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } | |
977 } | |
978 } | |
979 else if (Movie.header.startFlags & MOVIE_START_FROM_SRAM) | |
980 { | |
981 // save SRAM | |
982 if (!theEmulator.emuWriteBatteryToStream(stream)) | |
983 { | |
984 utilGzClose(stream); | |
985 { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } | |
986 } | |
987 | |
988 // 'soft' reset: | |
989 theEmulator.emuReset(false); | |
990 } | |
991 | |
992 utilGzClose(stream); | |
993 | |
994 // reopen the file and seek back to the end | |
995 | |
996 if (!(file = fopen(movie_filename, "rb+"))) | |
997 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } | |
998 | |
999 fseek(file, 0, SEEK_END); | |
1000 } | |
1001 else // no snapshot or SRAM | |
1002 { | |
1003 HardResetAndSRAMClear(); | |
1004 } | |
1005 | |
1006 Movie.header.offset_to_controller_data = (uint32)ftell(file); | |
1007 | |
1008 strcpy(Movie.filename, movie_filename); | |
1009 Movie.file = file; | |
1010 Movie.bytesPerFrame = bytes_per_frame(Movie); | |
1011 Movie.inputBufferPtr = Movie.inputBuffer; | |
1012 Movie.currentFrame = 0; | |
1013 Movie.readOnly = false; | |
1014 Movie.RecordedThisSession = true; | |
1015 | |
1016 change_state(MOVIE_STATE_RECORD); | |
1017 | |
1018 systemScreenMessage("Recording movie..."); | |
1019 { loadingMovie = false; return MOVIE_SUCCESS; } | |
1020 } | |
1021 | |
1022 void VBAUpdateButtonPressDisplay() | |
1023 { | |
1024 uint32 keys = currentButtons[0] & BUTTON_REGULAR_RECORDING_MASK; | |
1025 | |
1026 const static char KeyMap[] = { 'A', 'B', 's', 'S', '>', '<', '^', 'v', 'R', 'L', '!', '?', '{', '}', 'v', '^' }; | |
1027 const static int KeyOrder[] = { 5, 6, 4, 7, 0, 1, 9, 8, 3, 2, 12, 15, 13, 14, 11, 10 }; // < ^ > v A B L R S s { = } _ | |
1028 // ? ! | |
1029 char buffer[256]; | |
1030 sprintf(buffer, " "); | |
1031 | |
1032 #ifndef WIN32 | |
1033 // don't bother color-coding autofire and such | |
1034 int i; | |
1035 for (i = 0; i < 15; i++) | |
1036 { | |
1037 int j = KeyOrder[i]; | |
1038 int mask = (1 << (j)); | |
1039 buffer[strlen(" ") + i] = ((keys & mask) != 0) ? KeyMap[j] : ' '; | |
1040 } | |
1041 | |
1042 systemScreenMessage(buffer, 2, -1); | |
1043 #else | |
1044 const bool eraseAll = !theApp.inputDisplay; | |
1045 uint32 autoHeldKeys = eraseAll ? 0 : theApp.autoHold & BUTTON_REGULAR_RECORDING_MASK; | |
1046 uint32 autoFireKeys = eraseAll ? 0 : (theApp.autoFire | theApp.autoFire2) & BUTTON_REGULAR_RECORDING_MASK; | |
1047 uint32 pressedKeys = eraseAll ? 0 : keys; | |
1048 | |
1049 char colorList[64]; | |
1050 memset(colorList, 1, strlen(buffer)); | |
1051 | |
1052 if (!eraseAll) | |
1053 { | |
1054 for (int i = 0; i < 15; i++) | |
1055 { | |
1056 const int j = KeyOrder[i]; | |
1057 const int mask = (1 << (j)); | |
1058 bool pressed = (pressedKeys & mask) != 0; | |
1059 const bool autoHeld = (autoHeldKeys & mask) != 0; | |
1060 const bool autoFired = (autoFireKeys & mask) != 0; | |
1061 const bool erased = (lastKeys & mask) != 0 && (!pressed && !autoHeld && !autoFired); | |
1062 extern int textMethod; | |
1063 if (textMethod != 2 && (autoHeld || (autoFired && !pressed) || erased)) | |
1064 { | |
1065 int colorNum = 1; // default is white | |
1066 if (autoHeld) | |
1067 colorNum += (pressed ? 2 : 1); // yellow if pressed, red if not | |
1068 else if (autoFired) | |
1069 colorNum += 5; // blue if autofired and not currently pressed | |
1070 else if (erased) | |
1071 colorNum += 8; // black on black | |
1072 | |
1073 colorList[strlen(" ") + i] = colorNum; | |
1074 pressed = true; | |
1075 } | |
1076 buffer[strlen(" ") + i] = pressed ? KeyMap[j] : ' '; | |
1077 } | |
1078 } | |
1079 | |
1080 lastKeys = currentButtons[0]; | |
1081 lastKeys |= theApp.autoHold & BUTTON_REGULAR_RECORDING_MASK; | |
1082 lastKeys |= (theApp.autoFire | theApp.autoFire2) & BUTTON_REGULAR_RECORDING_MASK; | |
1083 | |
1084 systemScreenMessage(buffer, 2, -1, colorList); | |
1085 #endif | |
1086 } | |
1087 | |
1088 void VBAUpdateFrameCountDisplay() | |
1089 { | |
1090 const int MAGICAL_NUMBER = 64; // FIXME: this won't do any better, but only to remind you of sz issues | |
1091 char frameDisplayString[MAGICAL_NUMBER]; | |
1092 char lagFrameDisplayString[MAGICAL_NUMBER]; | |
1093 char extraCountDisplayString[MAGICAL_NUMBER]; | |
1094 | |
1095 #if (defined(WIN32) && !defined(SDL)) | |
1096 if (theApp.frameCounter) | |
1097 #else | 1134 #else |
1098 /// SDL FIXME | 1135 /// SDL FIXME |
1099 #endif | 1136 #endif |
1100 { | 1137 { |
1101 switch (Movie.state) | 1138 // sprintf(lagFrameDisplayString, " %c %d", systemCounters.laggedLast ? '*' : '|', systemCounters.lagCount); |
1102 { | 1139 sprintf(lagFrameDisplayString, " | %d%s", systemCounters.lagCount, systemCounters.laggedLast ? " *" : ""); |
1103 case MOVIE_STATE_PLAY: | 1140 strcat(frameDisplayString, lagFrameDisplayString); |
1104 case MOVIE_STATE_END: | 1141 } |
1105 { | |
1106 sprintf(frameDisplayString, "%d / %d", Movie.currentFrame, Movie.header.length_frames); | |
1107 if (!Movie.readOnly) | |
1108 strcat(frameDisplayString, " (edit)"); | |
1109 break; | |
1110 } | |
1111 case MOVIE_STATE_RECORD: | |
1112 { | |
1113 sprintf(frameDisplayString, "%d (record)", Movie.currentFrame); | |
1114 break; | |
1115 } | |
1116 default: | |
1117 { | |
1118 sprintf(frameDisplayString, "%d (no movie)", systemCounters.frameCount); | |
1119 break; | |
1120 } | |
1121 } | |
1122 | 1142 |
1123 #if (defined(WIN32) && !defined(SDL)) | 1143 #if (defined(WIN32) && !defined(SDL)) |
1124 if (theApp.lagCounter) | 1144 if (theApp.extraCounter) |
1125 #else | |
1126 /// SDL FIXME | |
1127 #endif | |
1128 { | |
1129 // sprintf(lagFrameDisplayString, " %c %d", systemCounters.laggedLast ? '*' : '|', systemCounters.lagCount); | |
1130 sprintf(lagFrameDisplayString, " | %d%s", systemCounters.lagCount, systemCounters.laggedLast ? " *" : ""); | |
1131 strcat(frameDisplayString, lagFrameDisplayString); | |
1132 } | |
1133 | |
1134 #if (defined(WIN32) && !defined(SDL)) | |
1135 if (theApp.extraCounter) | |
1136 #else | |
1137 /// SDL FIXME | |
1138 #endif | |
1139 { | |
1140 sprintf(extraCountDisplayString, " | %d", systemCounters.frameCount - systemCounters.extraCount); | |
1141 strcat(frameDisplayString, extraCountDisplayString); | |
1142 } | |
1143 } | |
1144 #if (defined(WIN32) && !defined(SDL)) | |
1145 else | |
1146 { | |
1147 frameDisplayString[0] = '\0'; | |
1148 } | |
1149 #else | 1145 #else |
1150 /// SDL FIXME | 1146 /// SDL FIXME |
1151 #endif | 1147 #endif |
1152 systemScreenMessage(frameDisplayString, 1, -1); | 1148 { |
1149 sprintf(extraCountDisplayString, " | %d", systemCounters.frameCount - systemCounters.extraCount); | |
1150 strcat(frameDisplayString, extraCountDisplayString); | |
1151 } | |
1152 } | |
1153 #if (defined(WIN32) && !defined(SDL)) | |
1154 else | |
1155 { | |
1156 frameDisplayString[0] = '\0'; | |
1157 } | |
1158 #else | |
1159 /// SDL FIXME | |
1160 #endif | |
1161 systemScreenMessage(frameDisplayString, 1, -1); | |
1153 } | 1162 } |
1154 | 1163 |
1155 // this function should only be called once every frame | 1164 // this function should only be called once every frame |
1156 void VBAMovieUpdateState() | 1165 void VBAMovieUpdateState() |
1157 { | 1166 { |
1158 ++Movie.currentFrame; | 1167 ++Movie.currentFrame; |
1159 | 1168 printf("RLM: inside updateState\n"); |
1160 if (Movie.state == MOVIE_STATE_PLAY) | 1169 if (Movie.state == MOVIE_STATE_PLAY) |
1161 { | 1170 { |
1162 Movie.inputBufferPtr += Movie.bytesPerFrame; | 1171 Movie.inputBufferPtr += Movie.bytesPerFrame; |
1163 if (Movie.currentFrame >= Movie.header.length_frames) | 1172 if (Movie.currentFrame >= Movie.header.length_frames) |
1164 { | 1173 { |
1165 // the movie ends anyway; what to do next depends on the settings | 1174 // the movie ends anyway; what to do next depends on the settings |
1166 change_state(MOVIE_STATE_END); | 1175 change_state(MOVIE_STATE_END); |
1167 } | 1176 } |
1168 } | 1177 } |
1169 else if (Movie.state == MOVIE_STATE_RECORD) | 1178 else if (Movie.state == MOVIE_STATE_RECORD) |
1170 { | 1179 { |
1171 // use first fseek? | 1180 printf("RLM: Movie_STATE_RECORD\n"); |
1172 fwrite(Movie.inputBufferPtr, 1, Movie.bytesPerFrame, Movie.file); | 1181 // use first fseek? |
1173 Movie.header.length_frames = Movie.currentFrame; | 1182 //TODO: THis is the problem. |
1174 Movie.inputBufferPtr += Movie.bytesPerFrame; | 1183 //fwrite(Movie.inputBufferPtr, 1, Movie.bytesPerFrame, Movie.file); |
1175 Movie.RecordedThisSession = true; | 1184 printf("RLM: fuck.\n"); |
1176 flush_movie_header(); | 1185 Movie.header.length_frames = Movie.currentFrame; |
1177 } | 1186 Movie.inputBufferPtr += Movie.bytesPerFrame; |
1178 else if (Movie.state == MOVIE_STATE_END) | 1187 Movie.RecordedThisSession = true; |
1179 { | 1188 flush_movie_header(); |
1180 change_state(MOVIE_STATE_END); | 1189 } |
1181 } | 1190 else if (Movie.state == MOVIE_STATE_END) |
1191 { | |
1192 change_state(MOVIE_STATE_END); | |
1193 } | |
1182 } | 1194 } |
1183 | 1195 |
1184 void VBAMovieRead(int i, bool /*sensor*/) | 1196 void VBAMovieRead(int i, bool /*sensor*/) |
1185 { | 1197 { |
1186 if (Movie.state != MOVIE_STATE_PLAY) | 1198 if (Movie.state != MOVIE_STATE_PLAY) |
1187 return; | 1199 return; |
1188 | 1200 |
1189 if (i < 0 || i >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) | 1201 if (i < 0 || i >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) |
1190 return; // not a controller we're recognizing | 1202 return; // not a controller we're recognizing |
1191 | 1203 |
1192 if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) | 1204 if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) |
1193 { | 1205 { |
1194 currentButtons[i] = Read16(Movie.inputBufferPtr + CONTROLLER_DATA_SIZE * i); | 1206 currentButtons[i] = Read16(Movie.inputBufferPtr + CONTROLLER_DATA_SIZE * i); |
1195 } | 1207 } |
1196 else | 1208 else |
1197 { | 1209 { |
1198 currentButtons[i] = 0; // pretend the controller is disconnected | 1210 currentButtons[i] = 0; // pretend the controller is disconnected |
1199 } | 1211 } |
1200 | 1212 |
1201 if ((currentButtons[i] & BUTTON_MASK_NEW_RESET) != 0) | 1213 if ((currentButtons[i] & BUTTON_MASK_NEW_RESET) != 0) |
1202 resetSignaled = true; | 1214 resetSignaled = true; |
1203 } | 1215 } |
1204 | 1216 |
1205 void VBAMovieWrite(int i, bool /*sensor*/) | 1217 void VBAMovieWrite(int i, bool /*sensor*/) |
1206 { | 1218 { |
1207 if (Movie.state != MOVIE_STATE_RECORD) | 1219 if (Movie.state != MOVIE_STATE_RECORD) |
1208 return; | 1220 return; |
1209 | 1221 |
1210 if (i < 0 || i >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) | 1222 if (i < 0 || i >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) |
1211 return; // not a controller we're recognizing | 1223 return; // not a controller we're recognizing |
1212 | 1224 |
1213 reserve_buffer_space((uint32)((Movie.inputBufferPtr - Movie.inputBuffer) + Movie.bytesPerFrame)); | 1225 reserve_buffer_space((uint32)((Movie.inputBufferPtr - Movie.inputBuffer) + Movie.bytesPerFrame)); |
1214 | 1226 |
1215 if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) | 1227 if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) |
1216 { | 1228 { |
1217 // get the current controller data | 1229 // get the current controller data |
1218 uint16 buttonData = currentButtons[i]; | 1230 uint16 buttonData = currentButtons[i]; |
1219 | 1231 |
1220 // mask away the irrelevent bits | 1232 // mask away the irrelevent bits |
1221 buttonData &= BUTTON_REGULAR_MASK | BUTTON_MOTION_MASK; | 1233 buttonData &= BUTTON_REGULAR_MASK | BUTTON_MOTION_MASK; |
1222 | 1234 |
1223 // soft-reset "button" for 1 frame if the game is reset while recording | 1235 // soft-reset "button" for 1 frame if the game is reset while recording |
1224 if (resetSignaled) | 1236 if (resetSignaled) |
1225 { | 1237 { |
1226 buttonData |= BUTTON_MASK_NEW_RESET; | 1238 buttonData |= BUTTON_MASK_NEW_RESET; |
1227 } | 1239 } |
1228 | 1240 |
1229 // backward compatibility kludge | 1241 // backward compatibility kludge |
1230 if (resetSignaledLast) | 1242 if (resetSignaledLast) |
1231 { | 1243 { |
1232 buttonData |= BUTTON_MASK_OLD_RESET; | 1244 buttonData |= BUTTON_MASK_OLD_RESET; |
1233 } | 1245 } |
1234 | 1246 |
1235 Write16(buttonData, Movie.inputBufferPtr + CONTROLLER_DATA_SIZE * i); | 1247 Write16(buttonData, Movie.inputBufferPtr + CONTROLLER_DATA_SIZE * i); |
1236 | 1248 |
1237 // and for display | 1249 // and for display |
1238 currentButtons[i] = buttonData; | 1250 currentButtons[i] = buttonData; |
1239 } | 1251 } |
1240 else | 1252 else |
1241 { | 1253 { |
1242 // pretend the controller is disconnected (otherwise input it gives could cause desync since we're not writing it to the | 1254 // pretend the controller is disconnected (otherwise input it gives could cause desync since we're not writing it to the |
1243 // movie) | 1255 // movie) |
1244 currentButtons[i] = 0; | 1256 currentButtons[i] = 0; |
1245 } | 1257 } |
1246 } | 1258 } |
1247 | 1259 |
1248 void VBAMovieStop(bool8 suppress_message) | 1260 void VBAMovieStop(bool8 suppress_message) |
1249 { | 1261 { |
1250 if (Movie.state != MOVIE_STATE_NONE) | 1262 if (Movie.state != MOVIE_STATE_NONE) |
1251 { | 1263 { |
1252 change_state(MOVIE_STATE_NONE); | 1264 change_state(MOVIE_STATE_NONE); |
1253 if (!suppress_message) | 1265 if (!suppress_message) |
1254 systemScreenMessage("Movie stop"); | 1266 systemScreenMessage("Movie stop"); |
1255 } | 1267 } |
1256 } | 1268 } |
1257 | 1269 |
1258 int VBAMovieGetInfo(const char *filename, SMovie *info) | 1270 int VBAMovieGetInfo(const char *filename, SMovie *info) |
1259 { | 1271 { |
1260 assert(info != NULL); | 1272 assert(info != NULL); |
1261 if (info == NULL) | 1273 if (info == NULL) |
1262 return -1; | 1274 return -1; |
1263 | 1275 |
1264 FILE * file; | 1276 FILE * file; |
1265 int result; | 1277 int result; |
1266 SMovie &local_movie = *info; | 1278 SMovie &local_movie = *info; |
1267 | 1279 |
1268 memset(info, 0, sizeof(*info)); | 1280 memset(info, 0, sizeof(*info)); |
1269 if (filename[0] == '\0') | 1281 if (filename[0] == '\0') |
1270 return MOVIE_FILE_NOT_FOUND; | 1282 return MOVIE_FILE_NOT_FOUND; |
1271 if (!(file = fopen(filename, "rb"))) | 1283 if (!(file = fopen(filename, "rb"))) |
1272 return MOVIE_FILE_NOT_FOUND; | 1284 return MOVIE_FILE_NOT_FOUND; |
1273 | 1285 |
1274 // read header | 1286 // read header |
1275 if ((result = (read_movie_header(file, local_movie))) != MOVIE_SUCCESS) | 1287 if ((result = (read_movie_header(file, local_movie))) != MOVIE_SUCCESS) |
1276 { | 1288 { |
1277 fclose(file); | 1289 fclose(file); |
1278 return result; | 1290 return result; |
1279 } | 1291 } |
1280 | 1292 |
1281 // read the metadata / author info from file | 1293 // read the metadata / author info from file |
1282 fread(local_movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, file); | 1294 fread(local_movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, file); |
1283 | 1295 |
1284 strncpy(local_movie.filename, filename, _MAX_PATH); | 1296 strncpy(local_movie.filename, filename, _MAX_PATH); |
1285 local_movie.filename[_MAX_PATH - 1] = '\0'; | 1297 local_movie.filename[_MAX_PATH - 1] = '\0'; |
1286 | 1298 |
1287 if (Movie.file != NULL && stricmp(local_movie.filename, Movie.filename) == 0) // alreadyOpen | 1299 if (Movie.file != NULL && stricmp(local_movie.filename, Movie.filename) == 0) // alreadyOpen |
1288 { | 1300 { |
1289 local_movie.bytesPerFrame = Movie.bytesPerFrame; | 1301 local_movie.bytesPerFrame = Movie.bytesPerFrame; |
1290 local_movie.header.length_frames = Movie.header.length_frames; | 1302 local_movie.header.length_frames = Movie.header.length_frames; |
1291 } | 1303 } |
1292 else | 1304 else |
1293 { | 1305 { |
1294 // recalculate length of movie from the file size | 1306 // recalculate length of movie from the file size |
1295 local_movie.bytesPerFrame = bytes_per_frame(local_movie); | 1307 local_movie.bytesPerFrame = bytes_per_frame(local_movie); |
1296 fseek(file, 0, SEEK_END); | 1308 fseek(file, 0, SEEK_END); |
1297 int fileSize = ftell(file); | 1309 int fileSize = ftell(file); |
1298 local_movie.header.length_frames = | 1310 local_movie.header.length_frames = |
1299 (fileSize - local_movie.header.offset_to_controller_data) / local_movie.bytesPerFrame; | 1311 (fileSize - local_movie.header.offset_to_controller_data) / local_movie.bytesPerFrame; |
1300 } | 1312 } |
1301 | 1313 |
1302 fclose(file); | 1314 fclose(file); |
1303 | 1315 |
1304 if (access(filename, W_OK)) | 1316 if (access(filename, W_OK)) |
1305 info->readOnly = true; | 1317 info->readOnly = true; |
1306 | 1318 |
1307 return MOVIE_SUCCESS; | 1319 return MOVIE_SUCCESS; |
1308 } | 1320 } |
1309 | 1321 |
1310 bool8 VBAMovieActive() | 1322 bool8 VBAMovieActive() |
1311 { | 1323 { |
1312 return (Movie.state != MOVIE_STATE_NONE); | 1324 return (Movie.state != MOVIE_STATE_NONE); |
1313 } | 1325 } |
1314 | 1326 |
1315 bool8 VBAMovieLoading() | 1327 bool8 VBAMovieLoading() |
1316 { | 1328 { |
1317 return loadingMovie; | 1329 return loadingMovie; |
1318 } | 1330 } |
1319 | 1331 |
1320 bool8 VBAMoviePlaying() | 1332 bool8 VBAMoviePlaying() |
1321 { | 1333 { |
1322 return (Movie.state == MOVIE_STATE_PLAY); | 1334 return (Movie.state == MOVIE_STATE_PLAY); |
1323 } | 1335 } |
1324 | 1336 |
1325 bool8 VBAMovieRecording() | 1337 bool8 VBAMovieRecording() |
1326 { | 1338 { |
1327 return (Movie.state == MOVIE_STATE_RECORD); | 1339 return (Movie.state == MOVIE_STATE_RECORD); |
1328 } | 1340 } |
1329 | 1341 |
1330 bool8 VBAMovieReadOnly() | 1342 bool8 VBAMovieReadOnly() |
1331 { | 1343 { |
1332 if (!VBAMovieActive()) | 1344 if (!VBAMovieActive()) |
1333 return false; | 1345 return false; |
1334 | 1346 |
1335 return Movie.readOnly; | 1347 return Movie.readOnly; |
1336 } | 1348 } |
1337 | 1349 |
1338 void VBAMovieToggleReadOnly() | 1350 void VBAMovieToggleReadOnly() |
1339 { | 1351 { |
1340 if (!VBAMovieActive()) | 1352 if (!VBAMovieActive()) |
1341 return; | 1353 return; |
1342 | 1354 |
1343 if (Movie.readOnly != 2) | 1355 if (Movie.readOnly != 2) |
1344 { | 1356 { |
1345 Movie.readOnly = !Movie.readOnly; | 1357 Movie.readOnly = !Movie.readOnly; |
1346 | 1358 |
1347 systemScreenMessage(Movie.readOnly ? "Movie now read-only" : "Movie now editable"); | 1359 systemScreenMessage(Movie.readOnly ? "Movie now read-only" : "Movie now editable"); |
1348 } | 1360 } |
1349 else | 1361 else |
1350 { | 1362 { |
1351 systemScreenMessage("Can't toggle read-only movie"); | 1363 systemScreenMessage("Can't toggle read-only movie"); |
1352 } | 1364 } |
1353 } | 1365 } |
1354 | 1366 |
1355 uint32 VBAMovieGetVersion() | 1367 uint32 VBAMovieGetVersion() |
1356 { | 1368 { |
1357 if (!VBAMovieActive()) | 1369 if (!VBAMovieActive()) |
1358 return 0; | 1370 return 0; |
1359 | 1371 |
1360 return Movie.header.version; | 1372 return Movie.header.version; |
1361 } | 1373 } |
1362 | 1374 |
1363 uint32 VBAMovieGetMinorVersion() | 1375 uint32 VBAMovieGetMinorVersion() |
1364 { | 1376 { |
1365 if (!VBAMovieActive()) | 1377 if (!VBAMovieActive()) |
1366 return 0; | 1378 return 0; |
1367 | 1379 |
1368 return Movie.header.minorVersion; | 1380 return Movie.header.minorVersion; |
1369 } | 1381 } |
1370 | 1382 |
1371 uint32 VBAMovieGetId() | 1383 uint32 VBAMovieGetId() |
1372 { | 1384 { |
1373 if (!VBAMovieActive()) | 1385 if (!VBAMovieActive()) |
1374 return 0; | 1386 return 0; |
1375 | 1387 |
1376 return Movie.header.uid; | 1388 return Movie.header.uid; |
1377 } | 1389 } |
1378 | 1390 |
1379 uint32 VBAMovieGetLength() | 1391 uint32 VBAMovieGetLength() |
1380 { | 1392 { |
1381 if (!VBAMovieActive()) | 1393 if (!VBAMovieActive()) |
1382 return 0; | 1394 return 0; |
1383 | 1395 |
1384 return Movie.header.length_frames; | 1396 return Movie.header.length_frames; |
1385 } | 1397 } |
1386 | 1398 |
1387 uint32 VBAMovieGetFrameCounter() | 1399 uint32 VBAMovieGetFrameCounter() |
1388 { | 1400 { |
1389 if (!VBAMovieActive()) | 1401 if (!VBAMovieActive()) |
1390 return 0; | 1402 return 0; |
1391 | 1403 |
1392 return Movie.currentFrame; | 1404 return Movie.currentFrame; |
1393 } | 1405 } |
1394 | 1406 |
1395 uint32 VBAMovieGetRerecordCount() | 1407 uint32 VBAMovieGetRerecordCount() |
1396 { | 1408 { |
1397 if (!VBAMovieActive()) | 1409 if (!VBAMovieActive()) |
1398 return 0; | 1410 return 0; |
1399 | 1411 |
1400 return Movie.header.rerecord_count; | 1412 return Movie.header.rerecord_count; |
1401 } | 1413 } |
1402 | 1414 |
1403 uint32 VBAMovieSetRerecordCount(uint32 newRerecordCount) | 1415 uint32 VBAMovieSetRerecordCount(uint32 newRerecordCount) |
1404 { | 1416 { |
1405 uint32 oldRerecordCount = 0; | 1417 uint32 oldRerecordCount = 0; |
1406 if (!VBAMovieActive()) | 1418 if (!VBAMovieActive()) |
1407 return 0; | 1419 return 0; |
1408 | 1420 |
1409 oldRerecordCount = Movie.header.rerecord_count; | 1421 oldRerecordCount = Movie.header.rerecord_count; |
1410 Movie.header.rerecord_count = newRerecordCount; | 1422 Movie.header.rerecord_count = newRerecordCount; |
1411 return oldRerecordCount; | 1423 return oldRerecordCount; |
1412 } | 1424 } |
1413 | 1425 |
1414 std::string VBAMovieGetAuthorInfo() | 1426 std::string VBAMovieGetAuthorInfo() |
1415 { | 1427 { |
1416 if (!VBAMovieActive()) | 1428 if (!VBAMovieActive()) |
1417 return ""; | 1429 return ""; |
1418 | 1430 |
1419 return Movie.authorInfo; | 1431 return Movie.authorInfo; |
1420 } | 1432 } |
1421 | 1433 |
1422 std::string VBAMovieGetFilename() | 1434 std::string VBAMovieGetFilename() |
1423 { | 1435 { |
1424 if (!VBAMovieActive()) | 1436 if (!VBAMovieActive()) |
1425 return ""; | 1437 return ""; |
1426 | 1438 |
1427 return Movie.filename; | 1439 return Movie.filename; |
1428 } | 1440 } |
1429 | 1441 |
1430 void VBAMovieFreeze(uint8 * *buf, uint32 *size) | 1442 void VBAMovieFreeze(uint8 * *buf, uint32 *size) |
1431 { | 1443 { |
1432 // sanity check | 1444 // sanity check |
1433 if (!VBAMovieActive()) | 1445 if (!VBAMovieActive()) |
1434 { | 1446 { |
1435 return; | 1447 return; |
1436 } | 1448 } |
1437 | 1449 |
1438 *buf = NULL; | 1450 *buf = NULL; |
1439 *size = 0; | 1451 *size = 0; |
1440 | 1452 |
1441 // compute size needed for the buffer | 1453 // compute size needed for the buffer |
1442 // room for header.uid, currentFrame, and header.length_frames | 1454 // room for header.uid, currentFrame, and header.length_frames |
1443 uint32 size_needed = sizeof(Movie.header.uid) + sizeof(Movie.currentFrame) + sizeof(Movie.header.length_frames); | 1455 uint32 size_needed = sizeof(Movie.header.uid) + sizeof(Movie.currentFrame) + sizeof(Movie.header.length_frames); |
1444 size_needed += (uint32)(Movie.bytesPerFrame * Movie.header.length_frames); | 1456 size_needed += (uint32)(Movie.bytesPerFrame * Movie.header.length_frames); |
1445 *buf = new uint8[size_needed]; | 1457 *buf = new uint8[size_needed]; |
1446 *size = size_needed; | 1458 *size = size_needed; |
1447 | 1459 |
1448 uint8 *ptr = *buf; | 1460 uint8 *ptr = *buf; |
1449 if (!ptr) | 1461 if (!ptr) |
1450 { | 1462 { |
1451 return; | 1463 return; |
1452 } | 1464 } |
1453 | 1465 |
1454 Push32(Movie.header.uid, ptr); | 1466 Push32(Movie.header.uid, ptr); |
1455 Push32(Movie.currentFrame, ptr); | 1467 Push32(Movie.currentFrame, ptr); |
1456 Push32(Movie.header.length_frames - 1, ptr); // HACK: shorten the length by 1 for backward compatibility | 1468 Push32(Movie.header.length_frames - 1, ptr); // HACK: shorten the length by 1 for backward compatibility |
1457 | 1469 |
1458 memcpy(ptr, Movie.inputBuffer, Movie.bytesPerFrame * Movie.header.length_frames); | 1470 memcpy(ptr, Movie.inputBuffer, Movie.bytesPerFrame * Movie.header.length_frames); |
1459 } | 1471 } |
1460 | 1472 |
1461 int VBAMovieUnfreeze(const uint8 *buf, uint32 size) | 1473 int VBAMovieUnfreeze(const uint8 *buf, uint32 size) |
1462 { | 1474 { |
1463 // sanity check | 1475 // sanity check |
1464 if (!VBAMovieActive()) | 1476 if (!VBAMovieActive()) |
1465 { | 1477 { |
1466 return MOVIE_NOT_FROM_A_MOVIE; | 1478 return MOVIE_NOT_FROM_A_MOVIE; |
1467 } | 1479 } |
1468 | 1480 |
1469 const uint8 *ptr = buf; | 1481 const uint8 *ptr = buf; |
1470 if (size < sizeof(Movie.header.uid) + sizeof(Movie.currentFrame) + sizeof(Movie.header.length_frames)) | 1482 if (size < sizeof(Movie.header.uid) + sizeof(Movie.currentFrame) + sizeof(Movie.header.length_frames)) |
1471 { | 1483 { |
1472 return MOVIE_WRONG_FORMAT; | 1484 return MOVIE_WRONG_FORMAT; |
1473 } | 1485 } |
1474 | 1486 |
1475 uint32 movie_id = Pop32(ptr); | 1487 uint32 movie_id = Pop32(ptr); |
1476 uint32 current_frame = Pop32(ptr); | 1488 uint32 current_frame = Pop32(ptr); |
1477 uint32 end_frame = Pop32(ptr) + 1; // HACK: restore the length for backward compatibility | 1489 uint32 end_frame = Pop32(ptr) + 1; // HACK: restore the length for backward compatibility |
1478 uint32 space_needed = Movie.bytesPerFrame * end_frame; | 1490 uint32 space_needed = Movie.bytesPerFrame * end_frame; |
1479 | 1491 |
1480 if (movie_id != Movie.header.uid) | 1492 if (movie_id != Movie.header.uid) |
1481 return MOVIE_NOT_FROM_THIS_MOVIE; | 1493 return MOVIE_NOT_FROM_THIS_MOVIE; |
1482 | 1494 |
1483 if (space_needed > size) | 1495 if (space_needed > size) |
1484 return MOVIE_WRONG_FORMAT; | 1496 return MOVIE_WRONG_FORMAT; |
1485 | 1497 |
1486 if (Movie.readOnly) | 1498 if (Movie.readOnly) |
1487 { | 1499 { |
1488 // here, we are going to keep the input data from the movie file | 1500 // here, we are going to keep the input data from the movie file |
1489 // and simply rewind to the currentFrame pointer | 1501 // and simply rewind to the currentFrame pointer |
1490 // this will cause a desync if the savestate is not in sync // <-- NOT ANYMORE | 1502 // this will cause a desync if the savestate is not in sync // <-- NOT ANYMORE |
1491 // with the on-disk recording data, but it's easily solved | 1503 // with the on-disk recording data, but it's easily solved |
1492 // by loading another savestate or playing the movie from the beginning | 1504 // by loading another savestate or playing the movie from the beginning |
1493 | 1505 |
1494 // don't allow loading a state inconsistent with the current movie | 1506 // don't allow loading a state inconsistent with the current movie |
1495 uint32 length_history = min(current_frame, Movie.header.length_frames); | 1507 uint32 length_history = min(current_frame, Movie.header.length_frames); |
1496 if (end_frame < length_history) | 1508 if (end_frame < length_history) |
1497 return MOVIE_SNAPSHOT_INCONSISTENT; | 1509 return MOVIE_SNAPSHOT_INCONSISTENT; |
1498 | 1510 |
1499 uint32 space_shared = Movie.bytesPerFrame * length_history; | 1511 uint32 space_shared = Movie.bytesPerFrame * length_history; |
1500 if (memcmp(Movie.inputBuffer, ptr, space_shared)) | 1512 if (memcmp(Movie.inputBuffer, ptr, space_shared)) |
1501 return MOVIE_SNAPSHOT_INCONSISTENT; | 1513 return MOVIE_SNAPSHOT_INCONSISTENT; |
1502 | 1514 |
1503 Movie.currentFrame = current_frame; | 1515 Movie.currentFrame = current_frame; |
1504 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * min(current_frame, Movie.header.length_frames); | 1516 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * min(current_frame, Movie.header.length_frames); |
1505 } | 1517 } |
1506 else | 1518 else |
1507 { | 1519 { |
1508 // here, we are going to take the input data from the savestate | 1520 // here, we are going to take the input data from the savestate |
1509 // and make it the input data for the current movie, then continue | 1521 // and make it the input data for the current movie, then continue |
1510 // writing new input data at the currentFrame pointer | 1522 // writing new input data at the currentFrame pointer |
1511 Movie.currentFrame = current_frame; | 1523 Movie.currentFrame = current_frame; |
1512 Movie.header.length_frames = end_frame; | 1524 Movie.header.length_frames = end_frame; |
1513 if (!VBALuaRerecordCountSkip()) | 1525 if (!VBALuaRerecordCountSkip()) |
1514 ++Movie.header.rerecord_count; | 1526 ++Movie.header.rerecord_count; |
1515 | 1527 |
1516 Movie.RecordedThisSession = true; | 1528 Movie.RecordedThisSession = true; |
1517 | 1529 |
1518 // do this before calling reserve_buffer_space() | 1530 // do this before calling reserve_buffer_space() |
1519 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * min(current_frame, Movie.header.length_frames); | 1531 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * min(current_frame, Movie.header.length_frames); |
1520 reserve_buffer_space(space_needed); | 1532 reserve_buffer_space(space_needed); |
1521 memcpy(Movie.inputBuffer, ptr, space_needed); | 1533 memcpy(Movie.inputBuffer, ptr, space_needed); |
1522 | 1534 |
1523 // for consistency, no auto movie conversion here since we don't auto convert the corresponding savestate | 1535 // for consistency, no auto movie conversion here since we don't auto convert the corresponding savestate |
1524 flush_movie_header(); | 1536 flush_movie_header(); |
1525 flush_movie_frames(); | 1537 flush_movie_frames(); |
1526 } | 1538 } |
1527 | 1539 |
1528 change_state(MOVIE_STATE_PLAY); // check for movie end | 1540 change_state(MOVIE_STATE_PLAY); // check for movie end |
1529 | 1541 |
1530 // necessary! | 1542 // necessary! |
1531 resetSignaled = false; | 1543 resetSignaled = false; |
1532 resetSignaledLast = false; | 1544 resetSignaledLast = false; |
1533 | 1545 |
1534 // necessary to check if there's a reset signal at the previous frame | 1546 // necessary to check if there's a reset signal at the previous frame |
1535 if (current_frame > 0) | 1547 if (current_frame > 0) |
1536 { | 1548 { |
1537 const u8 NEW_RESET = u8(BUTTON_MASK_NEW_RESET >> 8); | 1549 const u8 NEW_RESET = u8(BUTTON_MASK_NEW_RESET >> 8); |
1538 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) | 1550 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) |
1539 { | 1551 { |
1540 if ((Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) && (*(Movie.inputBufferPtr+1- Movie.bytesPerFrame) & NEW_RESET)) | 1552 if ((Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) && (*(Movie.inputBufferPtr+1- Movie.bytesPerFrame) & NEW_RESET)) |
1541 { | 1553 { |
1542 resetSignaledLast = true; | 1554 resetSignaledLast = true; |
1543 break; | 1555 break; |
1544 } | 1556 } |
1545 } | 1557 } |
1546 } | 1558 } |
1547 | 1559 |
1548 return MOVIE_SUCCESS; | 1560 return MOVIE_SUCCESS; |
1549 } | 1561 } |
1550 | 1562 |
1551 bool VBAMovieEnded() | 1563 bool VBAMovieEnded() |
1552 { | 1564 { |
1553 return (Movie.state == MOVIE_STATE_END); | 1565 return (Movie.state == MOVIE_STATE_END); |
1554 // return (Movie.state != MOVIE_STATE_NONE && Movie.currentFrame >= Movie.header.length_frames); | 1566 // return (Movie.state != MOVIE_STATE_NONE && Movie.currentFrame >= Movie.header.length_frames); |
1555 } | 1567 } |
1556 | 1568 |
1557 bool VBAMovieAllowsRerecording() | 1569 bool VBAMovieAllowsRerecording() |
1558 { | 1570 { |
1559 bool allows = (Movie.state != MOVIE_STATE_NONE) && (Movie.currentFrame <= Movie.header.length_frames); | 1571 bool allows = (Movie.state != MOVIE_STATE_NONE) && (Movie.currentFrame <= Movie.header.length_frames); |
1560 return /*!VBAMovieReadOnly() &&*/ allows; | 1572 return /*!VBAMovieReadOnly() &&*/ allows; |
1561 } | 1573 } |
1562 | 1574 |
1563 bool VBAMovieSwitchToPlaying() | 1575 bool VBAMovieSwitchToPlaying() |
1564 { | 1576 { |
1565 if (!VBAMovieActive()) | 1577 if (!VBAMovieActive()) |
1566 return false; | 1578 return false; |
1567 | 1579 |
1568 if (!Movie.readOnly) | 1580 if (!Movie.readOnly) |
1569 { | 1581 { |
1570 VBAMovieToggleReadOnly(); | 1582 VBAMovieToggleReadOnly(); |
1571 } | 1583 } |
1572 | 1584 |
1573 change_state(MOVIE_STATE_PLAY); | 1585 change_state(MOVIE_STATE_PLAY); |
1574 if (Movie.state == MOVIE_STATE_PLAY) | 1586 if (Movie.state == MOVIE_STATE_PLAY) |
1575 systemScreenMessage("Movie replay (continue)"); | 1587 systemScreenMessage("Movie replay (continue)"); |
1576 else | 1588 else |
1577 systemScreenMessage("Movie end"); | 1589 systemScreenMessage("Movie end"); |
1578 | 1590 |
1579 return true; | 1591 return true; |
1580 } | 1592 } |
1581 | 1593 |
1582 bool VBAMovieSwitchToRecording() | 1594 bool VBAMovieSwitchToRecording() |
1583 { | 1595 { |
1584 if (!VBAMovieAllowsRerecording()) | 1596 if (!VBAMovieAllowsRerecording()) |
1585 return false; | 1597 return false; |
1586 | 1598 |
1587 if (Movie.readOnly) | 1599 if (Movie.readOnly) |
1588 { | 1600 { |
1589 VBAMovieToggleReadOnly(); | 1601 VBAMovieToggleReadOnly(); |
1590 } | 1602 } |
1591 | 1603 |
1592 if (!VBALuaRerecordCountSkip()) | 1604 if (!VBALuaRerecordCountSkip()) |
1593 ++Movie.header.rerecord_count; | 1605 ++Movie.header.rerecord_count; |
1594 | 1606 |
1595 change_state(MOVIE_STATE_RECORD); | 1607 change_state(MOVIE_STATE_RECORD); |
1596 systemScreenMessage("Movie re-record"); | 1608 systemScreenMessage("Movie re-record"); |
1597 | 1609 |
1598 //truncate_movie(Movie.currentFrame); | 1610 //truncate_movie(Movie.currentFrame); |
1599 | 1611 |
1600 return true; | 1612 return true; |
1601 } | 1613 } |
1602 | 1614 |
1603 uint32 VBAMovieGetState() | 1615 uint32 VBAMovieGetState() |
1604 { | 1616 { |
1605 // ? | 1617 // ? |
1606 if (!VBAMovieActive()) | 1618 if (!VBAMovieActive()) |
1607 return MOVIE_STATE_NONE; | 1619 return MOVIE_STATE_NONE; |
1608 | 1620 |
1609 return Movie.state; | 1621 return Movie.state; |
1610 } | 1622 } |
1611 | 1623 |
1612 void VBAMovieSignalReset() | 1624 void VBAMovieSignalReset() |
1613 { | 1625 { |
1614 if (VBAMovieActive()) | 1626 if (VBAMovieActive()) |
1615 resetSignaled = true; | 1627 resetSignaled = true; |
1616 } | 1628 } |
1617 | 1629 |
1618 void VBAMovieResetIfRequested() | 1630 void VBAMovieResetIfRequested() |
1619 { | 1631 { |
1620 if (resetSignaled) | 1632 if (resetSignaled) |
1621 { | 1633 { |
1622 theEmulator.emuReset(false); | 1634 theEmulator.emuReset(false); |
1623 resetSignaled = false; | 1635 resetSignaled = false; |
1624 resetSignaledLast = true; | 1636 resetSignaledLast = true; |
1625 } | 1637 } |
1626 else | 1638 else |
1627 { | 1639 { |
1628 resetSignaledLast = false; | 1640 resetSignaledLast = false; |
1629 } | 1641 } |
1630 } | 1642 } |
1631 | 1643 |
1632 void VBAMovieSetMetadata(const char *info) | 1644 void VBAMovieSetMetadata(const char *info) |
1633 { | 1645 { |
1634 if (!memcmp(Movie.authorInfo, info, MOVIE_METADATA_SIZE)) | 1646 if (!memcmp(Movie.authorInfo, info, MOVIE_METADATA_SIZE)) |
1635 return; | 1647 return; |
1636 | 1648 |
1637 memcpy(Movie.authorInfo, info, MOVIE_METADATA_SIZE); // strncpy would omit post-0 bytes | 1649 memcpy(Movie.authorInfo, info, MOVIE_METADATA_SIZE); // strncpy would omit post-0 bytes |
1638 Movie.authorInfo[MOVIE_METADATA_SIZE - 1] = '\0'; | 1650 Movie.authorInfo[MOVIE_METADATA_SIZE - 1] = '\0'; |
1639 | 1651 |
1640 if (Movie.file) | 1652 if (Movie.file) |
1641 { | 1653 { |
1642 // (over-)write the header | 1654 // (over-)write the header |
1643 fseek(Movie.file, 0, SEEK_SET); | 1655 fseek(Movie.file, 0, SEEK_SET); |
1644 write_movie_header(Movie.file, Movie); | 1656 |
1645 | 1657 write_movie_header(Movie.file, Movie); |
1646 // write the metadata / author info to file | 1658 |
1647 fwrite(Movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, Movie.file); | 1659 // write the metadata / author info to file |
1648 | 1660 fwrite(Movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, Movie.file); |
1649 fflush(Movie.file); | 1661 |
1650 } | 1662 fflush(Movie.file); |
1663 } | |
1664 printf("RLM: setMetadata called\n"); | |
1665 | |
1651 } | 1666 } |
1652 | 1667 |
1653 void VBAMovieRestart() | 1668 void VBAMovieRestart() |
1654 { | 1669 { |
1655 if (VBAMovieActive()) | 1670 if (VBAMovieActive()) |
1656 { | 1671 { |
1657 systemSoundClearBuffer(); | 1672 systemSoundClearBuffer(); |
1658 | 1673 |
1659 bool8 modified = Movie.RecordedThisSession; | 1674 bool8 modified = Movie.RecordedThisSession; |
1660 | 1675 |
1661 VBAMovieStop(true); | 1676 VBAMovieStop(true); |
1662 | 1677 |
1663 char movieName [_MAX_PATH]; | 1678 char movieName [_MAX_PATH]; |
1664 strncpy(movieName, Movie.filename, _MAX_PATH); | 1679 strncpy(movieName, Movie.filename, _MAX_PATH); |
1665 movieName[_MAX_PATH - 1] = '\0'; | 1680 movieName[_MAX_PATH - 1] = '\0'; |
1666 VBAMovieOpen(movieName, Movie.readOnly); // can't just pass in Movie.filename, since VBAMovieOpen clears out Movie's | 1681 VBAMovieOpen(movieName, Movie.readOnly); // can't just pass in Movie.filename, since VBAMovieOpen clears out Movie's |
1667 // variables | 1682 // variables |
1668 | 1683 |
1669 Movie.RecordedThisSession = modified; | 1684 Movie.RecordedThisSession = modified; |
1670 | 1685 |
1671 systemScreenMessage("Movie replay (restart)"); | 1686 systemScreenMessage("Movie replay (restart)"); |
1672 } | 1687 } |
1673 } | 1688 } |
1674 | 1689 |
1675 int VBAMovieGetPauseAt() | 1690 int VBAMovieGetPauseAt() |
1676 { | 1691 { |
1677 return Movie.pauseFrame; | 1692 return Movie.pauseFrame; |
1678 } | 1693 } |
1679 | 1694 |
1680 void VBAMovieSetPauseAt(int at) | 1695 void VBAMovieSetPauseAt(int at) |
1681 { | 1696 { |
1682 Movie.pauseFrame = at; | 1697 Movie.pauseFrame = at; |
1683 } | 1698 } |
1684 | 1699 |
1685 /////////////////////// | 1700 /////////////////////// |
1686 // movie tools | 1701 // movie tools |
1687 | 1702 |
1688 // FIXME: is it safe to convert/flush a movie while recording it (considering fseek() problem)? | 1703 // FIXME: is it safe to convert/flush a movie while recording it (considering fseek() problem)? |
1689 int VBAMovieConvertCurrent() | 1704 int VBAMovieConvertCurrent() |
1690 { | 1705 { |
1691 if (!VBAMovieActive()) | 1706 if (!VBAMovieActive()) |
1692 { | 1707 { |
1693 return MOVIE_NOTHING; | 1708 return MOVIE_NOTHING; |
1694 } | 1709 } |
1695 | 1710 |
1696 if (Movie.header.minorVersion > VBM_REVISION) | 1711 if (Movie.header.minorVersion > VBM_REVISION) |
1697 { | 1712 { |
1698 return MOVIE_WRONG_VERSION; | 1713 return MOVIE_WRONG_VERSION; |
1699 } | 1714 } |
1700 | 1715 |
1701 if (Movie.header.minorVersion == VBM_REVISION) | 1716 if (Movie.header.minorVersion == VBM_REVISION) |
1702 { | 1717 { |
1703 return MOVIE_NOTHING; | 1718 return MOVIE_NOTHING; |
1704 } | 1719 } |
1705 | 1720 |
1706 Movie.header.minorVersion = VBM_REVISION; | 1721 Movie.header.minorVersion = VBM_REVISION; |
1707 | 1722 |
1708 if (Movie.header.length_frames == 0) // this could happen | 1723 if (Movie.header.length_frames == 0) // this could happen |
1709 { | 1724 { |
1710 truncate_movie(0); | 1725 truncate_movie(0); |
1711 return MOVIE_SUCCESS; | 1726 return MOVIE_SUCCESS; |
1712 } | 1727 } |
1713 | 1728 |
1714 // fix movies recorded from snapshots | 1729 // fix movies recorded from snapshots |
1715 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) | 1730 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) |
1716 { | 1731 { |
1717 uint8 *firstFramePtr = Movie.inputBuffer; | 1732 uint8 *firstFramePtr = Movie.inputBuffer; |
1718 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) | 1733 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) |
1734 { | |
1735 if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) | |
1736 { | |
1737 Push16(initialInputs[i], firstFramePtr); | |
1738 // note: this is correct since Push16 advances the dest pointer by sizeof u16 | |
1739 } | |
1740 } | |
1741 } | |
1742 | |
1743 // convert old resets to new ones | |
1744 const u8 OLD_RESET = u8(BUTTON_MASK_OLD_RESET >> 8); | |
1745 const u8 NEW_RESET = u8(BUTTON_MASK_NEW_RESET >> 8); | |
1746 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) | |
1747 { | |
1748 if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) | |
1749 { | |
1750 uint8 *startPtr = Movie.inputBuffer + sizeof(u16) * i + 1; | |
1751 uint8 *endPtr = Movie.inputBuffer + Movie.bytesPerFrame * (Movie.header.length_frames - 1); | |
1752 for (; startPtr < endPtr; startPtr += Movie.bytesPerFrame) | |
1753 { | |
1754 if (startPtr[Movie.bytesPerFrame] & OLD_RESET) | |
1719 { | 1755 { |
1720 if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) | 1756 startPtr[0] |= NEW_RESET; |
1721 { | |
1722 Push16(initialInputs[i], firstFramePtr); | |
1723 // note: this is correct since Push16 advances the dest pointer by sizeof u16 | |
1724 } | |
1725 } | 1757 } |
1726 } | 1758 } |
1727 | 1759 } |
1728 // convert old resets to new ones | 1760 } |
1729 const u8 OLD_RESET = u8(BUTTON_MASK_OLD_RESET >> 8); | 1761 |
1730 const u8 NEW_RESET = u8(BUTTON_MASK_NEW_RESET >> 8); | 1762 flush_movie_header(); |
1731 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) | 1763 flush_movie_frames(); |
1732 { | 1764 return MOVIE_SUCCESS; |
1733 if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) | |
1734 { | |
1735 uint8 *startPtr = Movie.inputBuffer + sizeof(u16) * i + 1; | |
1736 uint8 *endPtr = Movie.inputBuffer + Movie.bytesPerFrame * (Movie.header.length_frames - 1); | |
1737 for (; startPtr < endPtr; startPtr += Movie.bytesPerFrame) | |
1738 { | |
1739 if (startPtr[Movie.bytesPerFrame] & OLD_RESET) | |
1740 { | |
1741 startPtr[0] |= NEW_RESET; | |
1742 } | |
1743 } | |
1744 } | |
1745 } | |
1746 | |
1747 flush_movie_header(); | |
1748 flush_movie_frames(); | |
1749 return MOVIE_SUCCESS; | |
1750 } | 1765 } |
1751 | 1766 |
1752 bool VBAMovieTuncateAtCurrentFrame() | 1767 bool VBAMovieTuncateAtCurrentFrame() |
1753 { | 1768 { |
1754 if (!VBAMovieActive()) | 1769 if (!VBAMovieActive()) |
1755 return false; | 1770 return false; |
1756 | 1771 |
1757 truncate_movie(Movie.currentFrame); | 1772 truncate_movie(Movie.currentFrame); |
1758 change_state(MOVIE_STATE_END); | 1773 change_state(MOVIE_STATE_END); |
1759 systemScreenMessage("Movie truncated"); | 1774 systemScreenMessage("Movie truncated"); |
1760 | 1775 |
1761 return true; | 1776 return true; |
1762 } | 1777 } |
1763 | 1778 |
1764 bool VBAMovieFixHeader() | 1779 bool VBAMovieFixHeader() |
1765 { | 1780 { |
1766 if (!VBAMovieActive()) | 1781 if (!VBAMovieActive()) |
1767 return false; | 1782 return false; |
1768 | 1783 |
1769 flush_movie_header(); | 1784 flush_movie_header(); |
1770 systemScreenMessage("Movie header fixed"); | 1785 systemScreenMessage("Movie header fixed"); |
1771 return true; | 1786 return true; |
1772 } | 1787 } |