/* Copyright (C) 1997-2005 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach ) http://www.zsnes.com http://sourceforge.net/projects/zsnes This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __LINUX__ #include "gblhdr.h" #define DIR_SLASH "/" #else #include #include #include #include #define DIR_SLASH "\\" #endif #include "gblvars.h" #include "asm_call.h" /* Nach's insane subtitle library for movies files :) The filename would be gamename.sub in the same directory the ZMV would be in. If you're playing gamename.zm1, then the sub file will be gamename.su1 etc... Format of the sub file: Start Frame:Frame Duration:Message Example: 1:180:Hi how are you? 300:180:Isn't this cool? 700:180:This is great :) 2500:375:Kill 'em! 3500:20:Did you see this? */ struct { FILE *fp; char linebuf[256]; size_t frame_current; size_t message_start; size_t message_duration; } MovieSub; void MovieSub_Open(const char *filename) { MovieSub.fp = fopen(filename, "r"); MovieSub.frame_current = 0; MovieSub.message_start = 0; MovieSub.message_duration = 0; } void MovieSub_Close() { if (MovieSub.fp) { fclose(MovieSub.fp); MovieSub.fp = 0; } } char *MovieSub_GetData() { if (MovieSub.fp) { char *i, *num; MovieSub.frame_current++; if (MovieSub.frame_current > MovieSub.message_start + MovieSub.message_duration) { MovieSub.message_duration = 0; fgets(MovieSub.linebuf, 256, MovieSub.fp); if (!(num = strtok(MovieSub.linebuf, ":"))) { return(0); } for (i = num; *i; i++) { if (!isdigit(*i)) { return(0); } } MovieSub.message_start = atoi(num); if (!(num = strtok(0, ":"))) { return(0); } for (i = num; *i; i++) { if (!isdigit(*i)) { MovieSub.message_start = 0; return(0); } } MovieSub.message_duration = atoi(num); } if (MovieSub.frame_current == MovieSub.message_start) { return(strtok(0, ":")); } } return(0); } size_t MovieSub_GetDuration() { return(MovieSub.message_duration); } ///////////////////////////////////////////////////////// extern unsigned int PJoyAOrig, PJoyBOrig, PJoyCOrig, PJoyDOrig, PJoyEOrig; extern unsigned int JoyAOrig, JoyBOrig, JoyCOrig, JoyDOrig, JoyEOrig; extern unsigned int MsgCount, MessageOn; extern unsigned char MovieTemp, txtmovieended[15], MovieProcessing, *Msgptr; static FILE *movfhandle; void Replay() { if (fread(&MovieTemp, 1, 1, movfhandle)) { if (MovieTemp < 2) // 1 or 0 are correct values { char *sub; if (MovieTemp == 0) // 0 means the input has changed { fread(&PJoyAOrig, 1, 4, movfhandle); fread(&PJoyBOrig, 1, 4, movfhandle); fread(&PJoyCOrig, 1, 4, movfhandle); fread(&PJoyDOrig, 1, 4, movfhandle); fread(&PJoyEOrig, 1, 4, movfhandle); } JoyAOrig = PJoyAOrig; JoyBOrig = PJoyBOrig; JoyCOrig = PJoyCOrig; JoyDOrig = PJoyDOrig; JoyEOrig = PJoyEOrig; if ((sub = MovieSub_GetData())) { Msgptr = sub; MessageOn = MovieSub_GetDuration(); } } else // anything else is bad - the file isn't a movie. { MovieProcessing = 0; fclose(movfhandle); MovieSub_Close(); } } else { Msgptr = txtmovieended; MessageOn = MsgCount; MovieProcessing = 0; fclose(movfhandle); MovieSub_Close(); } } extern unsigned int MovieBuffFrame, MovieBuffSize; unsigned char MovieBuffer[21*60]; void IncFrameWriteBuffer() { MovieBuffFrame++; if (MovieBuffFrame == 60) { fwrite(MovieBuffer, 1, MovieBuffSize, movfhandle); MovieBuffSize = 0; MovieBuffFrame = 0; } } void intsplitter (unsigned char *buffer, unsigned int offset, unsigned int value) { unsigned char i; for (i=0 ; i<4 ; i++) { buffer[offset + i] = ((value >> i*8) & 0xFF); } } unsigned int bytemerger (unsigned char heaviest, unsigned char heavy, unsigned char light, unsigned char lightest) { return ((heaviest << 24) | (heavy << 16) | (light << 8) | (lightest)); } extern unsigned int CReadHead, ReadHead, CFWriteStart, CFWriteHead; extern unsigned char BackState, CNetType, StoreBuffer[128*32]; void Record() { unsigned int offst, PJoyATemp, PJoyBTemp, PJoyCTemp, PJoyDTemp, PJoyETemp; if ((BackState == 1) && (CNetType >= 20)) { if (CReadHead == ReadHead) { CFWriteStart++; CFWriteStart &= 0x7F; if (CFWriteStart == CFWriteHead) { offst = (CFWriteHead << 5); offst++; PJoyATemp = bytemerger (StoreBuffer[offst+3],StoreBuffer[offst+2],StoreBuffer[offst+1],StoreBuffer[offst]); offst+=4; PJoyBTemp = bytemerger (StoreBuffer[offst+3],StoreBuffer[offst+2],StoreBuffer[offst+1],StoreBuffer[offst]); offst+=4; PJoyCTemp = bytemerger (StoreBuffer[offst+3],StoreBuffer[offst+2],StoreBuffer[offst+1],StoreBuffer[offst]); offst+=4; PJoyDTemp = bytemerger (StoreBuffer[offst+3],StoreBuffer[offst+2],StoreBuffer[offst+1],StoreBuffer[offst]); offst+=4; PJoyETemp = bytemerger (StoreBuffer[offst+3],StoreBuffer[offst+2],StoreBuffer[offst+1],StoreBuffer[offst]); offst+=4; // if (StoreBuffer[offst]) - commented out in the ASM. if ((PJoyAOrig == PJoyATemp) && (PJoyBOrig == PJoyBTemp) && (PJoyCOrig == PJoyCTemp) && (PJoyDOrig == PJoyDTemp) && (PJoyEOrig == PJoyETemp)) { MovieBuffer[MovieBuffSize] = 1; MovieBuffSize++; } else { PJoyAOrig = PJoyATemp; PJoyBOrig = PJoyBTemp; PJoyCOrig = PJoyCTemp; PJoyDOrig = PJoyDTemp; PJoyEOrig = PJoyETemp; MovieBuffer[MovieBuffSize] = 0; MovieBuffSize++; intsplitter (MovieBuffer, MovieBuffSize, PJoyAOrig); MovieBuffSize += 4; intsplitter (MovieBuffer, MovieBuffSize, PJoyBOrig); MovieBuffSize += 4; intsplitter (MovieBuffer, MovieBuffSize, PJoyCOrig); MovieBuffSize += 4; intsplitter (MovieBuffer, MovieBuffSize, PJoyDOrig); MovieBuffSize += 4; intsplitter (MovieBuffer, MovieBuffSize, PJoyEOrig); MovieBuffSize += 4; } IncFrameWriteBuffer(); CFWriteHead++; CFWriteHead &= 0x7F; } CReadHead++; CReadHead &= 0x7F; } offst = (ReadHead << 5); StoreBuffer[offst] = 0; offst++; intsplitter (StoreBuffer, offst, JoyAOrig); offst += 4; intsplitter (StoreBuffer, offst, JoyBOrig); offst += 4; intsplitter (StoreBuffer, offst, JoyCOrig); offst += 4; intsplitter (StoreBuffer, offst, JoyDOrig); offst += 4; intsplitter (StoreBuffer, offst, JoyEOrig); offst +=4; ReadHead++; ReadHead &= 0x7F; } else { if ((PJoyAOrig != JoyAOrig) || (PJoyBOrig != JoyBOrig) || (PJoyCOrig != JoyCOrig) || (PJoyDOrig != JoyDOrig) || (PJoyEOrig != JoyEOrig)) { PJoyAOrig = JoyAOrig; PJoyBOrig = JoyBOrig; PJoyCOrig = JoyCOrig; PJoyDOrig = JoyDOrig; PJoyEOrig = JoyEOrig; MovieTemp = 0; MovieBuffer[MovieBuffSize] = 0; MovieBuffSize++; intsplitter (MovieBuffer, MovieBuffSize, JoyAOrig); MovieBuffSize += 4; intsplitter (MovieBuffer, MovieBuffSize, JoyBOrig); MovieBuffSize += 4; intsplitter (MovieBuffer, MovieBuffSize, JoyCOrig); MovieBuffSize += 4; intsplitter (MovieBuffer, MovieBuffSize, JoyDOrig); MovieBuffSize += 4; intsplitter (MovieBuffer, MovieBuffSize, JoyEOrig); MovieBuffSize += 4; } else { MovieTemp = 1; MovieBuffer[MovieBuffSize] = 1; MovieBuffSize++; } IncFrameWriteBuffer(); } } void ProcessMovies() { if (MovieProcessing == 2) { Record(); } else { Replay(); } } // The following will maybe end up in guic.c once we get it started. // It came from guiwindp.inc and gui.asm, after all extern unsigned char MovieRecordWinVal; extern unsigned int GUICBHold; void SkipMovie() { MovieRecordWinVal = 0; GUICBHold &= 0xFFFFFF00; } void MovieStop() { if (MovieProcessing) { fclose(movfhandle); MovieProcessing = 0; } } extern unsigned int MovieCounter, statefileloc, Totalbyteloaded, curexecstate; extern unsigned int nmiprevaddrl, nmiprevaddrh, nmirept, nmiprevline, nmistatus; extern unsigned char GUIQuit, fnamest[512], CMovieExt, RecData[16], soundon; extern unsigned char NextLineCache, sramsavedis, UseRemoteSRAMData; extern unsigned char UnableMovie2[24], UnableMovie3[23]; void SRAMChdir(); void loadstate2(); void ChangetoLOADdir(); void MoviePlay() { unsigned char FileExt[4]; GUICBHold &= 0xFFFFFF00; if (CNetType != 20) { MovieCounter= 0; if (!MovieProcessing) { GUIQuit = 2; memcpy (FileExt, &fnamest[statefileloc-3], 4); memcpy (&fnamest[statefileloc-3], ".zmv", 4); fnamest[statefileloc] = CMovieExt; SRAMChdir(); loadstate2(); if ((movfhandle = fopen(fnamest+1,"rb")) != NULL) { memcpy(&fnamest[statefileloc-3], ".sub", 4); if (isdigit(CMovieExt)) { fnamest[statefileloc] = CMovieExt; } MovieSub_Open(fnamest+1); fseek(movfhandle, Totalbyteloaded, SEEK_SET); fread(RecData, 1, 16, movfhandle); printf("Movie made with version: %d\n", RecData[1]); if (RecData[2] == 1) { timer2upd = bytemerger(RecData[6], RecData[5], RecData[4], RecData[3]); curexecstate = bytemerger(RecData[10], RecData[9], RecData[9], RecData[7]); nmiprevaddrl = 0; nmiprevaddrh = 0; nmirept = 0; nmiprevline = 224; nmistatus = 0; spcnumread = 0; spchalted = 0xFFFFFFFF; NextLineCache = 0; } if (soundon == RecData[0]) { if (ramsize) { fread(sram, 1, ramsize, movfhandle); } MovieProcessing = 1; PJoyAOrig = 0; PJoyBOrig = 0; PJoyCOrig = 0; PJoyDOrig = 0; PJoyEOrig = 0; sramsavedis = 1; UseRemoteSRAMData = 0; DSPMem[0x08] = 0; DSPMem[0x18] = 0; DSPMem[0x28] = 0; DSPMem[0x38] = 0; DSPMem[0x48] = 0; DSPMem[0x58] = 0; DSPMem[0x68] = 0; DSPMem[0x78] = 0; } else { Msgptr = (!soundon) ? UnableMovie3 : UnableMovie2; MessageOn = MsgCount; fclose(movfhandle); } } memcpy (&fnamest[statefileloc-3], FileExt, 4); asm_call(ChangetoLOADdir); } } } extern unsigned char NoPictureSave; extern unsigned int versionNumber; void statesaver(); void MovieRecord() { unsigned char FileExt[4]; FILE *tempfhandle; GUICBHold &= 0xFFFFFF00; if (!MovieProcessing) { MovieCounter = 0; memcpy (FileExt, &fnamest[statefileloc-3], 4); memcpy (&fnamest[statefileloc-3], ".zmv", 4); fnamest[statefileloc] = CMovieExt; tempfhandle = fopen(fnamest+1,"rb"); // check if file exists if ((MovieRecordWinVal == 1) || (tempfhandle == NULL)) { if (!MovieProcessing) { CFWriteHead = 0; CReadHead = 0; ReadHead = 0; CFWriteStart = 64; } MovieRecordWinVal = 0; SRAMChdir(); NoPictureSave = 1; // saves the statedata as first chunk of movie if (!MovieProcessing) { statesaver(); } NoPictureSave = 0; // it shouldn't fail, but paranoids can add an 'if'... movfhandle = fopen(fnamest+1,"r+b"); fseek(movfhandle, 0, SEEK_END); if (!MovieProcessing) { RecData[0] = soundon; RecData[1] = (versionNumber & 0xFF); // valid for versions under 2.56 RecData[2] = 1; intsplitter (RecData, 3, timer2upd); intsplitter (RecData, 7, curexecstate); fwrite(RecData, 1, 16, movfhandle); if (ramsize) { fwrite(sram, 1, ramsize, movfhandle); } MovieBuffSize = 0; MovieBuffFrame = 0; if ((CNetType != 20) && (CNetType != 21)) { nmiprevaddrl = 0; nmiprevaddrh = 0; nmirept = 0; nmiprevline = 224; nmistatus = 0; spcnumread = 0; spchalted = 0xFFFFFFFF; NextLineCache = 0; PJoyAOrig = 0; PJoyBOrig = 0; PJoyCOrig = 0; PJoyDOrig = 0; PJoyEOrig = 0; DSPMem[0x08] = 0; DSPMem[0x18] = 0; DSPMem[0x28] = 0; DSPMem[0x38] = 0; DSPMem[0x48] = 0; DSPMem[0x58] = 0; DSPMem[0x68] = 0; DSPMem[0x78] = 0; } } MovieProcessing = 2; asm_call(ChangetoLOADdir); } else { fclose(tempfhandle); MovieRecordWinVal = 1; } memcpy (&fnamest[statefileloc-3], FileExt, 4); } }