tas editor rough draft

This commit is contained in:
zeromus 2008-05-28 07:41:49 +00:00
parent 886bae8b5a
commit d1cc70c7c4
14 changed files with 643 additions and 297 deletions

View File

@ -36,6 +36,7 @@
#include "../../fceu.h"
#include "../../state.h"
#include "../../debug.h"
#include "../../movie.h"
#include "input.h"
#include "netplay.h"
#include "memwatch.h"
@ -50,6 +51,7 @@
#include "tracer.h"
#include "cdlogger.h"
#include "throttle.h"
#include "tasedit.h"
#include "main.h"
#include "basicbot.h"
@ -297,6 +299,7 @@ int BlockingCheck()
{
//other accelerator capable dialogs could be added here
extern HWND hwndMemWatch;
extern HWND hwndTasEdit;
int handled = 0;
if(hwndMemWatch)
{
@ -305,6 +308,11 @@ int BlockingCheck()
if(!handled)
handled = IsDialogMessage(hwndMemWatch,&msg);
}
if(!handled && hwndTasEdit)
{
if(IsChild(hwndTasEdit,msg.hwnd))
handled = TranslateAccelerator(hwndTasEdit,fceu_hAccel,&msg);
}
if(!handled)
if(msg.hwnd == hAppWnd)
@ -689,6 +697,8 @@ doloopy:
FCEU_UpdateBot();
FCEUI_Emulate(&gfx, &sound, &ssize, 0); //emulate a single frame
currMovieData.TryDumpIncremental();
FCEUD_Update(gfx, sound, ssize); //update displays and debug tools
//mbg 6/30/06 - close game if we were commanded to by calls nested in FCEUI_Emulate()
@ -766,6 +776,7 @@ void _updateWindow()
UpdateMemWatch();
NTViewDoBlit(0);
UpdateCheatList();
UpdateTasEdit();
}
//void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count)

View File

@ -23,6 +23,7 @@
#include "memwatch.h"
#include "../../debug.h"
#include "debugger.h"
#include "../../utils/xstring.h"
const int NUMWATCHES = 24;
const int LABELLENGTH = 64;
@ -48,11 +49,6 @@ const unsigned int MEMW_MAX_NUMBER_OF_RECENT_FILES = sizeof(memw_recent_files)/s
static HMENU memwrecentmenu, memwrecentdmenu;
//mbg 5/12/08
//for the curious, I tested U16ToHexStr and it was 10x faster than printf.
//so the author of these dedicated functions is not insane, and I will leave them.
void UpdateMemw_RMenu(HMENU menu, char **strs, unsigned int mitem, unsigned int baseid)
{
MENUITEMINFO moo;
@ -178,87 +174,6 @@ void MemwAddRecentFile(const char *filename)
UpdateMemwRecentArray(filename, memw_recent_files, MEMW_MAX_NUMBER_OF_RECENT_FILES, memwrecentmenu, ID_FILE_RECENT, MEMW_MENU_FIRST_RECENT_FILE);
}
/**
* Add a directory to the recent directories list.
*
* @param dirname Name of the directory to add.
**/
static char *U8ToStr(uint8 a)
{
static char TempArray[8];
TempArray[0] = '0' + a/100;
TempArray[1] = '0' + (a%100)/10;
TempArray[2] = '0' + (a%10);
TempArray[3] = 0;
return TempArray;
}
//I don't trust scanf for speed
static uint16 FastStrToU16(char* s, bool& valid)
{
int i;
uint16 v=0;
for(i=0; i < 4; i++)
{
if(s[i] == 0) return v;
v<<=4;
if(s[i] >= '0' && s[i] <= '9')
{
v+=s[i]-'0';
}
else if(s[i] >= 'a' && s[i] <= 'f')
{
v+=s[i]-'a'+10;
}
else if(s[i] >= 'A' && s[i] <= 'F')
{
v+=s[i]-'A'+10;
}
else
{
valid = false;
return 0xFFFF;
}
}
valid = true;
return v;
}
static char *U16ToDecStr(uint16 a)
{
static char TempArray[8];
TempArray[0] = '0' + a/10000;
TempArray[1] = '0' + (a%10000)/1000;
TempArray[2] = '0' + (a%1000)/100;
TempArray[3] = '0' + (a%100)/10;
TempArray[4] = '0' + (a%10);
TempArray[5] = 0;
return TempArray;
}
static char *U16ToHexStr(uint16 a)
{
static char TempArray[8];
TempArray[0] = a/4096 > 9?'A'+a/4096-10:'0' + a/4096;
TempArray[1] = (a%4096)/256 > 9?'A'+(a%4096)/256 - 10:'0' + (a%4096)/256;
TempArray[2] = (a%256)/16 > 9?'A'+(a%256)/16 - 10:'0' + (a%256)/16;
TempArray[3] = a%16 > 9?'A'+(a%16) - 10:'0' + (a%16);
TempArray[4] = 0;
return TempArray;
}
static char *U8ToHexStr(uint8 a)
{
static char TempArray[8];
TempArray[0] = a/16 > 9?'A'+a/16 - 10:'0' + a/16;
TempArray[1] = a%16 > 9?'A'+(a%16) - 10:'0' + (a%16);
TempArray[2] = 0;
return TempArray;
}
static const int MW_ADDR_Lookup[] = {
MW_ADDR00,MW_ADDR01,MW_ADDR02,MW_ADDR03,
MW_ADDR04,MW_ADDR05,MW_ADDR06,MW_ADDR07,
@ -352,7 +267,7 @@ void UpdateMemWatch()
}
else
{
text = U8ToStr(GetMem(mwrec.addr));
text = U8ToDecStr(GetMem(mwrec.addr));
}
}
}

View File

@ -1264,12 +1264,14 @@ BEGIN
EDITTEXT IDC_EDIT_STOPFRAME,119,38,35,12,ES_AUTOHSCROLL | ES_NUMBER
END
TASEDIT DIALOGEX 0, 0, 374, 348
TASEDIT DIALOGEX 0, 0, 470, 349
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "TAS Editor"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIST1,"SysListView32",LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP,7,7,273,296
CONTROL "",IDC_LIST1,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,7,273,296
PUSHBUTTON "Hacky add 1000 frames",IDC_HACKY2,318,73,124,38
PUSHBUTTON "Hacky truncate after current frame",IDC_HACKY1,313,31,124,38
END
@ -1372,9 +1374,9 @@ BEGIN
"TASEDIT", DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 367
RIGHTMARGIN, 463
TOPMARGIN, 7
BOTTOMMARGIN, 341
BOTTOMMARGIN, 342
END
END
#endif // APSTUDIO_INVOKED
@ -1610,9 +1612,18 @@ END
IDR_ACCELERATOR1 ACCELERATORS
BEGIN
"E", ACCEL_CTRL_E, VIRTKEY, CONTROL, NOINVERT
"O", ACCEL_CTRL_O, VIRTKEY, CONTROL, NOINVERT
"W", ACCEL_CTRL_W, VIRTKEY, CONTROL, NOINVERT
END
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
IDB_TE_ARROW BITMAP "res\\te_arrow.bmp"
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -26,6 +26,8 @@
#define MENU_SAVE_STATE 110
#define CB_ASK_EXIT 110
#define COMBO_FAM 110
#define IDB_BITMAP1 110
#define IDB_TE_ARROW 110
#define MENU_LOAD_STATE 111
#define CB_DISABLE_SCREEN_SAVER 111
#define BTN_FAM 111
@ -240,6 +242,8 @@
#define BTN_PRESET_EXPORT2 1126
#define BTN_PRESET_EXPORT3 1127
#define IDC_LIST1 1130
#define IDC_HACKY2 1131
#define IDC_HACKY1 1132
#define MENU_NETWORK 40040
#define MENU_PALETTE 40041
#define MENU_SOUND 40042
@ -333,6 +337,8 @@
#define ID_CONFIG_PAUSEAFTERPLAYBACK 40125
#define MENU_PAUSEAFTERPLAYBACK 40126
#define ACCEL_CTRL_O 40130
#define ACCEL_CTRL_E 40131
#define ACCEL_CTRL_W 40132
#define MW_ValueLabel2 65423
#define MW_ValueLabel1 65426
#define GUI_BOT_DEBUG 65436
@ -343,9 +349,9 @@
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 110
#define _APS_NEXT_COMMAND_VALUE 40131
#define _APS_NEXT_CONTROL_VALUE 1131
#define _APS_NEXT_RESOURCE_VALUE 111
#define _APS_NEXT_COMMAND_VALUE 40134
#define _APS_NEXT_CONTROL_VALUE 1132
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -1,27 +1,265 @@
#include "common.h"
#include "tasedit.h"
#include "../../fceu.h"
#include "debugger.h"
#include "../../movie.h"
#include "../../utils/xstring.h"
//to change header font
//http://forums.devx.com/archive/index.php/t-37234.html
HWND hwndTasEdit = 0;
static int lastCursor;
static HWND hwndList;
//hacky.. we need to think about how to convey information from the driver to the movie code.
//add a new fceud_ function?? blehhh maybe
bool moviePleaseLogSavestates = false;
static void GetDispInfo(NMLVDISPINFO* nmlvDispInfo)
{
LVITEM& item = nmlvDispInfo->item;
if(item.mask & LVIF_TEXT)
{
switch(item.iSubItem)
{
case 0:
if(item.iImage == I_IMAGECALLBACK && item.iItem == currFrameCounter)
item.iImage = 0;
else
item.iImage = -1;
break;
case 1:
U32ToDecStr(item.pszText,item.iItem);
break;
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9: {
int bit = (item.iSubItem - 2);
uint8 data = currMovieData.records[item.iItem].joysticks[0];
if(data & (1<<bit))
{
item.pszText[0] = MovieRecord::mnemonics[bit];
item.pszText[1] = 0;
} else
item.pszText[1] = 0;
}
break;
}
}
}
#define CDDS_ITEMPREPAINT (CDDS_ITEM | CDDS_PREPAINT)
#define CDDS_ITEMPOSTPAINT (CDDS_ITEM | CDDS_POSTPAINT)
#define CDDS_ITEMPREERASE (CDDS_ITEM | CDDS_PREERASE)
#define CDDS_ITEMPOSTERASE (CDDS_ITEM | CDDS_POSTERASE)
#define CDDS_SUBITEMPREPAINT (CDDS_SUBITEM | CDDS_ITEMPREPAINT)
#define CDDS_SUBITEMPOSTPAINT (CDDS_SUBITEM | CDDS_ITEMPOSTPAINT)
#define CDDS_SUBITEMPREERASE (CDDS_SUBITEM | CDDS_ITEMPREERASE)
#define CDDS_SUBITEMPOSTERASE (CDDS_SUBITEM | CDDS_ITEMPOSTERASE)
static LONG CustomDraw(NMLVCUSTOMDRAW* msg)
{
switch(msg->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
return CDRF_NOTIFYITEMDRAW;
case CDDS_ITEMPREPAINT:
return CDRF_NOTIFYSUBITEMDRAW;
case CDDS_SUBITEMPREPAINT:
SelectObject(msg->nmcd.hdc,debugSystem->hFixedFont);
if((int)msg->nmcd.dwItemSpec < currMovieData.greenZoneCount)
msg->clrTextBk = RGB(192,255,192);
return CDRF_DODEFAULT;
default:
return CDRF_DODEFAULT;
}
}
// called from the rest of the emulator when things happen and the tasedit should change to reflect it
void UpdateTasEdit()
{
if(!hwndTasEdit) return;
//update the number of items
ListView_SetItemCountEx(hwndList,currMovieData.getNumRecords(),LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL);
//update the cursor
int newCursor = currFrameCounter;
if(newCursor != lastCursor)
{
//scroll to the row
ListView_EnsureVisible(hwndList,newCursor,FALSE);
//select the row
ListView_SetItemState(hwndList,newCursor,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
//update the old and new rows
ListView_Update(hwndList,lastCursor);
ListView_Update(hwndList,newCursor);
lastCursor = newCursor;
}
}
void RedrawList()
{
InvalidateRect(hwndList,0,FALSE);
}
void DoubleClick(LPNMITEMACTIVATE info)
{
int index = info->iItem;
//stray click
if(index == -1)
return;
//if the icon or frame columns were double clicked:
if(info->iSubItem == 0 || info->iSubItem == 1)
{
//if the row is in the green zone, then move to it
if(index < currMovieData.greenZoneCount)
{
MovieData::loadSavestateFrom(&currMovieData.records[index].savestate);
}
}
else //if an input column was clicked:
{
//toggle the bit
int bit = (info->iSubItem-2);
currMovieData.records[index].toggleBit(0,bit);
//update the row
ListView_Update(hwndList,index);
//reduce the green zone
currMovieData.greenZoneCount = std::min(index+1,currMovieData.greenZoneCount);
//redraw everything to show the reduced green zone
RedrawList();
}
}
void InitDialog()
{
ListView_SetExtendedListViewStyleEx(hwndList,
LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES ,
LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES );
HIMAGELIST himglist = ImageList_Create(12,12,ILC_COLOR32 | ILC_MASK,1,1);
HBITMAP bmp = LoadBitmap(fceu_hInstance,MAKEINTRESOURCE(IDB_TE_ARROW));
ImageList_AddMasked(himglist, bmp, RGB(255,0,255));
DeleteObject(bmp);
ListView_SetImageList(hwndList,himglist,LVSIL_SMALL);
//doesnt work well??
//HIMAGELIST himglist = ImageList_LoadImage(fceu_hInstance,MAKEINTRESOURCE(IDB_TE_ARROW),12,1,RGB(255,0,255),IMAGE_BITMAP,LR_DEFAULTCOLOR);
//setup columns
LVCOLUMN lvc;
int colidx=0;
lvc.mask = LVCF_WIDTH;
lvc.cx = 12;
ListView_InsertColumn(hwndList, colidx++, &lvc);
lvc.mask = LVCF_WIDTH | LVCF_TEXT;
lvc.cx = 95;
lvc.pszText = "Frame#";
ListView_InsertColumn(hwndList, colidx++, &lvc);
lvc.cx = 20;
lvc.pszText = "A";
ListView_InsertColumn(hwndList, colidx++, &lvc);
lvc.pszText = "B";
ListView_InsertColumn(hwndList, colidx++, &lvc);
lvc.pszText = "S";
ListView_InsertColumn(hwndList, colidx++, &lvc);
lvc.pszText = "T";
ListView_InsertColumn(hwndList, colidx++, &lvc);
lvc.pszText = "U";
ListView_InsertColumn(hwndList, colidx++, &lvc);
lvc.pszText = "D";
ListView_InsertColumn(hwndList, colidx++, &lvc);
lvc.pszText = "L";
ListView_InsertColumn(hwndList, colidx++, &lvc);
lvc.pszText = "R";
ListView_InsertColumn(hwndList, colidx++, &lvc);
//-----------------------------
//the initial update
UpdateTasEdit();
}
HWND hTasEdit = 0;
void KillTasEdit()
{
DestroyWindow(hTasEdit);
hTasEdit = 0;
DestroyWindow(hwndTasEdit);
hwndTasEdit = 0;
moviePleaseLogSavestates = false;
}
BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
hwndList = GetDlgItem(hwndDlg,IDC_LIST1);
InitDialog();
break;
case WM_NOTIFY:
switch(wParam)
{
case IDC_LIST1:
switch(((LPNMHDR)lParam)->code)
{
case NM_CUSTOMDRAW:
SetWindowLong(hwndDlg, DWL_MSGRESULT, CustomDraw((NMLVCUSTOMDRAW*)lParam));
return TRUE;
case LVN_GETDISPINFO:
GetDispInfo((NMLVDISPINFO*)lParam);
break;
case NM_CLICK:
break;
case NM_DBLCLK:
DoubleClick((LPNMITEMACTIVATE)lParam);
break;
}
break;
}
break;
case WM_CLOSE:
case WM_QUIT:
KillTasEdit();
break;
/* case WM_NOTIFY:
case LVN_GETDISPINFO:*/
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_HACKY1:
//hacky1: delete all items after the current selection
currMovieData.records.resize(currFrameCounter+1);
UpdateTasEdit();
break;
case IDC_HACKY2:
//hacky1: delete all items after the current selection
currMovieData.records.resize(currMovieData.records.size()+1000);
currMovieData.clearRecordRange(currMovieData.records.size()-1000,1000);
UpdateTasEdit();
break;
case ACCEL_CTRL_W:
KillTasEdit();
break;
}
break;
}
return FALSE;
@ -29,13 +267,13 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
void DoTasEdit()
{
if(!hTasEdit)
hTasEdit = CreateDialog(fceu_hInstance,"TASEDIT",NULL,WndprocTasEdit);
lastCursor = -1;
if(!hwndTasEdit)
hwndTasEdit = CreateDialog(fceu_hInstance,"TASEDIT",NULL,WndprocTasEdit);
if(hTasEdit)
if(hwndTasEdit)
{
SetWindowPos(hTasEdit,HWND_TOP,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
//update?
//FCEUD_UpdatePPUView(-1,1);
moviePleaseLogSavestates = true;
SetWindowPos(hwndTasEdit,HWND_TOP,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
}
}

View File

@ -1 +1,2 @@
void DoTasEdit();
void DoTasEdit();
void UpdateTasEdit();

View File

@ -857,6 +857,7 @@ LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
UpdateCheckedMenuItems();
break;
case ACCEL_CTRL_E:
case MENU_TASEDIT:
DoTasEdit();
break;

View File

@ -3,9 +3,6 @@
#include <string.h>
#include <assert.h>
#include <vector>
#include <map>
#ifdef WIN32
#include <windows.h>
#endif
@ -27,6 +24,7 @@
extern char FileBase[];
extern int EmulationPaused;
extern bool moviePleaseLogSavestates;
//TODO - remove the synchack stuff from the replay gui and require it to be put into the fm2 file
@ -66,167 +64,135 @@ SFORMAT FCEUMOV_STATEINFO[]={
};
char curMovieFilename[512] = {0};
MovieData currMovieData;
class MovieRecord
void MovieData::clearRecordRange(int start, int len)
{
public:
ValueArray<uint8,4> joysticks;
for(int i=0;i<len;i++)
records[i+start].clear();
}
void dump(FILE* fp, int index)
{
//todo: if we want frame numbers in the output (which we dont since we couldnt cut and paste in movies)
//but someone would need to change the parser to ignore it
//fputc('|',fp);
//fprintf(fp,"%08d",index);
fputc('|',fp);
//for each joystick
for(int i=0;i<4;i++)
{
//these are mnemonics for each joystick bit.
//since we usually use the regular joypad, these will be more helpful.
//but any character other than ' ' should count as a set bit
//maybe other input types will need to be encoded another way..
const char mnemonics[] = {'A','B','S','T','U','D','L','R'};
for(int bit=7;bit>=0;bit--)
{
uint8 &joystate = joysticks[i];
int bitmask = (1<<bit);
char mnemonic = mnemonics[bit];
//if the bit is set write the mnemonic
if(joystate & bitmask)
fputc(mnemonic,fp);
else //otherwise write a space
fputc(' ',fp);
}
//separate the joysticks
if(i != 3) fputc('|',fp);
}
//each frame is on a new line
fputc('\n',fp);
}
};
class MovieData
void MovieData::TryDumpIncremental()
{
public:
MovieData()
: version(MOVIE_VERSION)
, emuVersion(FCEU_VERSION_NUMERIC)
, palFlag(false)
, poweronFlag(false)
, resetFlag(false)
, recordCount(1)
if(moviePleaseLogSavestates)
{
memset(&romChecksum,0,sizeof(MD5DATA));
}
int emuVersion;
int version;
//todo - somehow force mutual exclusion for poweron and reset (with an error in the parser)
bool palFlag;
bool poweronFlag;
bool resetFlag;
MD5DATA romChecksum;
std::string romFilename;
std::vector<char> savestate;
std::vector<MovieRecord> records;
int recordCount;
FCEU_Guid guid;
//the entire contents of the disk file that was loaded
std::vector<char> serializedFile;
void truncateAt(int frame)
{
records.resize(frame);
}
class TDictionary : public std::map<std::string,std::string>
{
public:
bool containsKey(std::string key)
//only log the savestate if we are appending to the green zone
if(currFrameCounter == currMovieData.greenZoneCount)
{
return find(key) != end();
}
void tryInstallBool(std::string key, bool& val)
{
if(containsKey(key))
val = atoi(operator [](key).c_str())!=0;
}
void tryInstallString(std::string key, std::string& val)
{
if(containsKey(key))
val = operator [](key);
}
void tryInstallInt(std::string key, int& val)
{
if(containsKey(key))
val = atoi(operator [](key).c_str());
}
};
void installDictionary(TDictionary& dictionary)
{
dictionary.tryInstallInt("version",version);
dictionary.tryInstallInt("emuVersion",emuVersion);
dictionary.tryInstallInt("recordCount",recordCount);
dictionary.tryInstallBool("palFlag",palFlag);
dictionary.tryInstallBool("poweronFlag",poweronFlag);
dictionary.tryInstallBool("resetFlag",resetFlag);
dictionary.tryInstallString("romFilename",romFilename);
if(dictionary.containsKey("romChecksum"))
StringToBytes(dictionary["romChecksum"],&romChecksum,MD5DATA::size);
if(dictionary.containsKey("guid"))
guid = FCEU_Guid::fromString(dictionary["guid"]);
if(dictionary.containsKey("savestate"))
{
std::string& str = dictionary["savestate"];
int len = HexStringToBytesLength(str);
if(len >= 1)
if(currFrameCounter < (int)currMovieData.records.size())
{
savestate.resize(len);
StringToBytes(str,&savestate[0],len);
MovieData::dumpSavestateTo(&currMovieData.records[currFrameCounter].savestate);
currMovieData.greenZoneCount++;
}
}
}
}
int dumpLen()
const char MovieRecord::mnemonics[8] = {'A','B','S','T','U','D','L','R'};
void MovieRecord::dump(FILE* fp, int index)
{
//todo: if we want frame numbers in the output (which we dont since we couldnt cut and paste in movies)
//but someone would need to change the parser to ignore it
//fputc('|',fp);
//fprintf(fp,"%08d",index);
fputc('|',fp);
//for each joystick
for(int i=0;i<4;i++)
{
FILE* tmp = tmpfile();
dump(tmp);
int len = ftell(tmp);
fclose(tmp);
return len;
//these are mnemonics for each joystick bit.
//since we usually use the regular joypad, these will be more helpful.
//but any character other than ' ' should count as a set bit
//maybe other input types will need to be encoded another way..
for(int bit=7;bit>=0;bit--)
{
uint8 &joystate = joysticks[i];
int bitmask = (1<<bit);
char mnemonic = mnemonics[bit];
//if the bit is set write the mnemonic
if(joystate & bitmask)
fputc(mnemonic,fp);
else //otherwise write a space
fputc(' ',fp);
}
//separate the joysticks
if(i != 3) fputc('|',fp);
}
void dump(FILE *fp)
{
fprintf(fp,"version %d\n", version);
fprintf(fp,"emuVersion %d\n", emuVersion);
fprintf(fp,"recordCount %d\n", recordCount);
fprintf(fp,"palFlag %d\n", palFlag?1:0);
fprintf(fp,"poweronFlag %d\n", poweronFlag?1:0);
fprintf(fp,"resetFlag %d\n", resetFlag?1:0);
fprintf(fp,"romFilename %s\n", romFilename.c_str());
fprintf(fp,"romChecksum %s\n", BytesToString(romChecksum.data,MD5DATA::size).c_str());
fprintf(fp,"guid %s\n", guid.toString().c_str());
if(savestate.size() != 0)
fprintf(fp,"savestate %s\n", BytesToString(&savestate[0],savestate.size()).c_str());
for(int i=0;i<(int)records.size();i++)
records[i].dump(fp,i);
}
//each frame is on a new line
fputc('\n',fp);
}
MovieData::MovieData()
: version(MOVIE_VERSION)
, emuVersion(FCEU_VERSION_NUMERIC)
, palFlag(false)
, poweronFlag(false)
, resetFlag(false)
, recordCount(1)
, greenZoneCount(0)
{
memset(&romChecksum,0,sizeof(MD5DATA));
}
void MovieData::truncateAt(int frame)
{
records.resize(frame);
}
void MovieData::installDictionary(TDictionary& dictionary)
{
dictionary.tryInstallInt("version",version);
dictionary.tryInstallInt("emuVersion",emuVersion);
dictionary.tryInstallInt("recordCount",recordCount);
dictionary.tryInstallBool("palFlag",palFlag);
dictionary.tryInstallBool("poweronFlag",poweronFlag);
dictionary.tryInstallBool("resetFlag",resetFlag);
dictionary.tryInstallString("romFilename",romFilename);
if(dictionary.containsKey("romChecksum"))
StringToBytes(dictionary["romChecksum"],&romChecksum,MD5DATA::size);
if(dictionary.containsKey("guid"))
guid = FCEU_Guid::fromString(dictionary["guid"]);
if(dictionary.containsKey("savestate"))
{
std::string& str = dictionary["savestate"];
int len = HexStringToBytesLength(str);
if(len >= 1)
{
savestate.resize(len);
StringToBytes(str,&savestate[0],len);
}
}
}
void MovieData::dump(FILE *fp)
{
fprintf(fp,"version %d\n", version);
fprintf(fp,"emuVersion %d\n", emuVersion);
fprintf(fp,"recordCount %d\n", recordCount);
fprintf(fp,"palFlag %d\n", palFlag?1:0);
fprintf(fp,"poweronFlag %d\n", poweronFlag?1:0);
fprintf(fp,"resetFlag %d\n", resetFlag?1:0);
fprintf(fp,"romFilename %s\n", romFilename.c_str());
fprintf(fp,"romChecksum %s\n", BytesToString(romChecksum.data,MD5DATA::size).c_str());
fprintf(fp,"guid %s\n", guid.toString().c_str());
if(savestate.size() != 0)
fprintf(fp,"savestate %s\n", BytesToString(&savestate[0],savestate.size()).c_str());
for(int i=0;i<(int)records.size();i++)
records[i].dump(fp,i);
}
int MovieData::dumpLen()
{
FILE* tmp = tmpfile();
dump(tmp);
int len = ftell(tmp);
fclose(tmp);
return len;
}
} currMovieData;
//---------
int FCEUMOV_GetFrame(void)
{
@ -259,16 +225,6 @@ int FCEUMOV_IsRecording(void)
//yuck... another custom text parser.
void LoadFM2(MovieData& movieData, FILE *fp)
{
//read the entire file so we can keep it handy.
//we could parse from that instead of the disk again...
fseek(fp,0,SEEK_END);
int len = ftell(fp);
fseek(fp,0,SEEK_SET);
movieData.serializedFile.resize(len);
fread(&movieData.serializedFile[0],1,len,fp);
fseek(fp,0,SEEK_SET);
MovieData::TDictionary dictionary;
std::string key,value;
@ -406,6 +362,33 @@ static void ResetInputTypes()
#endif
}
bool MovieData::loadSavestateFrom(std::vector<uint8>* buf)
{
//dump the savestate to disk
FILE* fp = tmpfile();
fwrite(&(*buf)[0],1,buf->size(),fp);
fseek(fp,0,SEEK_SET);
//and load the state
bool success = FCEUSS_LoadFP(fp,SSLOADPARAM_BACKUP);
fclose(fp);
return success;
}
void MovieData::dumpSavestateTo(std::vector<uint8>* buf)
{
//dump a savestate to a tempfile..
FILE* tmp = tmpfile();
FCEUSS_SaveFP(tmp);
//reloading the savestate into the data structure
fseek(tmp,0,SEEK_END);
int len = (int)ftell(tmp);
fseek(tmp,0,SEEK_SET);
buf->resize(len);
fread(&(*buf)[0],1,len,tmp);
fclose(tmp);
}
//begin playing an existing movie
void FCEUI_LoadMovie(char *fname, bool _read_only, int _pauseframe)
@ -447,16 +430,8 @@ void FCEUI_LoadMovie(char *fname, bool _read_only, int _pauseframe)
//WE NEED TO LOAD A SAVESTATE
if(!currMovieData.poweronFlag)
{
//dump the savestate to disk
FILE* fp = tmpfile();
fwrite(&currMovieData.savestate[0],1,currMovieData.savestate.size(),fp);
fseek(fp,0,SEEK_SET);
//and load the state
bool success = FCEUSS_LoadFP(fp,SSLOADPARAM_BACKUP);
fclose(fp);
bool success = MovieData::loadSavestateFrom(&currMovieData.savestate);
if(!success) return;
}
@ -478,6 +453,8 @@ void FCEUI_LoadMovie(char *fname, bool _read_only, int _pauseframe)
movie_readonly = _read_only;
movieMode = MOVIEMODE_PLAY;
currMovieData.TryDumpIncremental();
if(movie_readonly)
FCEU_DispMessage("Replay started Read-Only.");
else
@ -498,7 +475,6 @@ static void openRecordingMovie(const char* fname)
strcpy(curMovieFilename, fname);
}
//begin recording a new movie
void FCEUI_SaveMovie(char *fname, uint8 flags)
{
@ -545,17 +521,7 @@ void FCEUI_SaveMovie(char *fname, uint8 flags)
}
else
{
//dump a savestate to a tempfile..
FILE* tmp = tmpfile();
FCEUSS_SaveFP(tmp);
//reloading the savestate into the data structure
fseek(tmp,0,SEEK_END);
int len = (int)ftell(tmp);
fseek(tmp,0,SEEK_SET);
currMovieData.savestate.resize(len);
fread(&currMovieData.savestate[0],1,len,tmp);
fclose(tmp);
MovieData::dumpSavestateTo(&currMovieData.savestate);
}
//we are going to go ahead and dump the header. from now on we will only be appending frames
@ -583,8 +549,6 @@ void FCEUMOV_AddJoy(uint8 *js, int SkipFlush)
{
if(movieMode == MOVIEMODE_PLAY)
{
//TODO - rock solid stability and error detection
//stop when we run out of frames
if(currFrameCounter == currMovieData.records.size())
{

View File

@ -1,6 +1,10 @@
#ifndef __MOVIE_H_
#define __MOVIE_H_
#include <vector>
#include <map>
#include <string>
void FCEUMOV_AddJoy(uint8 *, int SkipFlush);
void FCEUMOV_AddCommand(int cmd);
void FCEU_DrawMovies(uint8 *);
@ -13,6 +17,97 @@ int FCEUMOV_WriteState(FILE* st);
bool FCEUMOV_ReadState(FILE* st, uint32 size);
void FCEUMOV_PreLoad(void);
int FCEUMOV_PostLoad(void);
void MovieFlushHeader(void);
#endif /* __MOVIE_H_ */
class MovieRecord
{
public:
ValueArray<uint8,4> joysticks;
void toggleBit(int joy, int bit)
{
int mask = (1<<bit);
joysticks[joy] ^= mask;
}
void clear() {
*(uint32*)&joysticks = 0;
}
//a waste of memory in lots of cases.. maybe make it a pointer later?
std::vector<uint8> savestate;
void dump(FILE* fp, int index);
static const char mnemonics[8];
};
class MovieData
{
public:
MovieData();
int emuVersion;
int version;
//todo - somehow force mutual exclusion for poweron and reset (with an error in the parser)
bool palFlag;
bool poweronFlag;
bool resetFlag;
MD5DATA romChecksum;
std::string romFilename;
std::vector<uint8> savestate;
std::vector<MovieRecord> records;
int recordCount;
FCEU_Guid guid;
//----TasEdit stuff---
int greenZoneCount;
//----
int getNumRecords() { return records.size(); }
class TDictionary : public std::map<std::string,std::string>
{
public:
bool containsKey(std::string key)
{
return find(key) != end();
}
void tryInstallBool(std::string key, bool& val)
{
if(containsKey(key))
val = atoi(operator [](key).c_str())!=0;
}
void tryInstallString(std::string key, std::string& val)
{
if(containsKey(key))
val = operator [](key);
}
void tryInstallInt(std::string key, int& val)
{
if(containsKey(key))
val = atoi(operator [](key).c_str());
}
};
void truncateAt(int frame);
void MovieData::installDictionary(TDictionary& dictionary);
void dump(FILE *fp);
int dumpLen();
void clearRecordRange(int start, int len);
static bool loadSavestateFrom(std::vector<uint8>* buf);
static void dumpSavestateTo(std::vector<uint8>* buf);
void TryDumpIncremental();
};
extern MovieData currMovieData;
extern int currFrameCounter;
//---------
#endif //__MOVIE_H_

View File

@ -359,7 +359,8 @@ bool FCEUSS_SaveFP(FILE *st)
//compress it
uint8* cbuf = new uint8[len*2]; //worst case compression, lets say twice the input buffer size
uLongf comprlen = len*2;
int error = compress2(cbuf,&comprlen,&buf[0],len,Z_BEST_COMPRESSION);
//int error = compress2(cbuf,&comprlen,&buf[0],len,Z_BEST_COMPRESSION);
int error = compress2(cbuf,&comprlen,&buf[0],len,Z_BEST_SPEED);
//dump the header
uint8 header[16]="FCSX";

View File

@ -202,4 +202,4 @@ struct FCEU_Guid : public ValueArray<uint8,16>
}
};
#endif
#endif

View File

@ -363,4 +363,98 @@ void splitpath(const char* path, char* drv, char* dir, char* name, char* ext)
*dir = '\0';
}
}
//mbg 5/12/08
//for the curious, I tested U16ToHexStr and it was 10x faster than printf.
//so the author of these dedicated functions is not insane, and I will leave them.
static char TempArray[11];
uint16 FastStrToU16(char* s, bool& valid)
{
int i;
uint16 v=0;
for(i=0; i < 4; i++)
{
if(s[i] == 0) return v;
v<<=4;
if(s[i] >= '0' && s[i] <= '9')
{
v+=s[i]-'0';
}
else if(s[i] >= 'a' && s[i] <= 'f')
{
v+=s[i]-'a'+10;
}
else if(s[i] >= 'A' && s[i] <= 'F')
{
v+=s[i]-'A'+10;
}
else
{
valid = false;
return 0xFFFF;
}
}
valid = true;
return v;
}
char *U8ToDecStr(uint8 a)
{
TempArray[0] = '0' + a/100;
TempArray[1] = '0' + (a%100)/10;
TempArray[2] = '0' + (a%10);
TempArray[3] = 0;
return TempArray;
}
char *U16ToDecStr(uint16 a)
{
TempArray[0] = '0' + a/10000;
TempArray[1] = '0' + (a%10000)/1000;
TempArray[2] = '0' + (a%1000)/100;
TempArray[3] = '0' + (a%100)/10;
TempArray[4] = '0' + (a%10);
TempArray[5] = 0;
return TempArray;
}
char *U32ToDecStr(char* buf, uint32 a)
{
buf[0] = '0' + a/1000000000;
buf[1] = '0' + (a%1000000000)/100000000;
buf[2] = '0' + (a%100000000)/10000000;
buf[3] = '0' + (a%10000000)/1000000;
buf[4] = '0' + (a%1000000)/100000;
buf[5] = '0' + (a%100000)/10000;
buf[6] = '0' + (a%10000)/1000;
buf[7] = '0' + (a%1000)/100;
buf[8] = '0' + (a%100)/10;
buf[9] = '0' + (a%10);
buf[10] = 0;
return buf;
}
char *U32ToDecStr(uint32 a)
{
return U32ToDecStr(TempArray,a);
}
char *U16ToHexStr(uint16 a)
{
TempArray[0] = a/4096 > 9?'A'+a/4096-10:'0' + a/4096;
TempArray[1] = (a%4096)/256 > 9?'A'+(a%4096)/256 - 10:'0' + (a%4096)/256;
TempArray[2] = (a%256)/16 > 9?'A'+(a%256)/16 - 10:'0' + (a%256)/16;
TempArray[3] = a%16 > 9?'A'+(a%16) - 10:'0' + (a%16);
TempArray[4] = 0;
return TempArray;
}
char *U8ToHexStr(uint8 a)
{
TempArray[0] = a/16 > 9?'A'+a/16 - 10:'0' + a/16;
TempArray[1] = a%16 > 9?'A'+(a%16) - 10:'0' + (a%16);
TempArray[2] = 0;
return TempArray;
}

View File

@ -22,12 +22,13 @@
#include <stdlib.h>
#include <vector>
#include "../types.h"
//definitions for str_strip() flags
#define STRIP_SP 0x01 /* space */
#define STRIP_TAB 0x02 /* tab */
#define STRIP_CR 0x04 /* carriage return */
#define STRIP_LF 0x08 /* line feed */
#define STRIP_SP 0x01 // space
#define STRIP_TAB 0x02 // tab
#define STRIP_CR 0x04 // carriage return
#define STRIP_LF 0x08 // line feed
int str_ucase(char *str);
@ -43,4 +44,12 @@ std::string BytesToString(void* data, int len);
bool StringToBytes(std::string& str, void* data, int len);
std::vector<std::string> tokenize_str(const std::string & str,const std::string & delims);
void splitpath(const char* path, char* drv, char* dir, char* name, char* ext);
void splitpath(const char* path, char* drv, char* dir, char* name, char* ext);
uint16 FastStrToU16(char* s, bool& valid);
char *U16ToDecStr(uint16 a);
char *U32ToDecStr(uint32 a);
char *U32ToDecStr(char* buf, uint32 a);
char *U8ToDecStr(uint8 a);
char *U8ToHexStr(uint8 a);
char *U16ToHexStr(uint16 a);