553 lines
12 KiB
C
553 lines
12 KiB
C
/*
|
|
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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#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);
|
|
}
|
|
}
|