diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index f3dc0254b..b20e89994 100755 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -781,6 +781,7 @@ int NDS_LoadROM(const char *filename, const char *physicalName, const char *logi cheats->init(buf); } + //UnloadMovieEmulationSettings(); called in NDS_Reset() NDS_Reset(); return ret; @@ -790,6 +791,7 @@ void NDS_FreeROM(void) { FCEUI_StopMovie(); gameInfo.closeROM(); + UnloadMovieEmulationSettings(); } void NDS_Sleep() { nds.sleeping = TRUE; } @@ -2567,6 +2569,8 @@ bool NDS_FakeBoot() bool _HACK_DONT_STOPMOVIE = false; void NDS_Reset() { + UnloadMovieEmulationSettings(); + //reload last paths if needed if(!gameInfo.reader) { diff --git a/desmume/src/commandline.cpp b/desmume/src/commandline.cpp index d3979e3c6..1c5612ed3 100644 --- a/desmume/src/commandline.cpp +++ b/desmume/src/commandline.cpp @@ -559,7 +559,7 @@ void CommandLine::process_movieCommands() } else if(record_movie_file != "") { - FCEUI_SaveMovie(record_movie_file.c_str(), L"", 0, NULL, FCEUI_MovieGetRTCDefault()); + FCEUI_SaveMovie(record_movie_file.c_str(), L"", START_BLANK, NULL, FCEUI_MovieGetRTCDefault()); } } diff --git a/desmume/src/frontend/windows/main.cpp b/desmume/src/frontend/windows/main.cpp index e96a9358e..f709e07c0 100755 --- a/desmume/src/frontend/windows/main.cpp +++ b/desmume/src/frontend/windows/main.cpp @@ -4817,6 +4817,11 @@ DOKEYDOWN: RunConfig(CONFIGSCREEN_HOTKEY); return 0; case IDM_FIRMSETTINGS: + if (AreMovieEmulationSettingsActive()) + { + MessageBox(hwnd, "The current settings have been set by a movie. Reset or unload the current game if you want to restore your saved settings.\n\n" + "If you make changes here, the new settings will overwrite your currently saved settings.", "Movie Settings Active", MB_OK); + } RunConfig(CONFIGSCREEN_FIRMWARE); return 0; case IDM_SOUNDSETTINGS: @@ -4826,6 +4831,11 @@ DOKEYDOWN: RunConfig(CONFIGSCREEN_WIFI); return 0; case IDM_EMULATIONSETTINGS: + if (AreMovieEmulationSettingsActive()) + { + MessageBox(hwnd, "The current settings have been set by a movie. Reset or unload the current game if you want to restore your saved settings.\n\n" + "If you make changes here (whether you reset now or not), the new settings will overwrite your currently saved settings.", "Movie Settings Active", MB_OK); + } RunConfig(CONFIGSCREEN_EMULATION); return 0; case IDM_MICROPHONESETTINGS: @@ -5874,7 +5884,7 @@ LRESULT CALLBACK EmulationSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, L if(romloaded) val = MessageBox(hDlg, "The current ROM needs to be reset to apply changes.\nReset now ?", "DeSmuME", (MB_YESNO | MB_ICONQUESTION)); - + UnloadMovieEmulationSettings(); CommonSettings.UseExtBIOS = IsDlgCheckboxChecked(hDlg, IDC_USEEXTBIOS); cur = GetDlgItem(hDlg, IDC_ARM9BIOS); diff --git a/desmume/src/frontend/windows/replay.cpp b/desmume/src/frontend/windows/replay.cpp index 332068cc8..a328aec1e 100644 --- a/desmume/src/frontend/windows/replay.cpp +++ b/desmume/src/frontend/windows/replay.cpp @@ -150,6 +150,7 @@ INT_PTR CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM OPENFILENAME ofn; char szChoice[MAX_PATH]={0}; char filename[MAX_PATH] = ""; + const char* error; switch(uMsg) { @@ -198,7 +199,10 @@ INT_PTR CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM return true; case IDOK: - FCEUI_LoadMovie(playfilename, replayreadonly, false, 80000); + error = FCEUI_LoadMovie(playfilename, replayreadonly, false, 80000); + if (error) + MessageBox(hwndDlg, error, "Failed to load movie", MB_OK); + ZeroMemory(&playfilename, sizeof(playfilename)); EndDialog(hwndDlg, 0); return true; @@ -243,8 +247,7 @@ INT_PTR CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM return false; } - -int flag=0; +START_FROM startFrom; //Record movie dialog static INT_PTR CALLBACK RecordDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) @@ -256,7 +259,11 @@ static INT_PTR CALLBACK RecordDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, switch(uMsg) { case WM_INITDIALOG: { - CheckDlgButton(hwndDlg, IDC_START_FROM_SRAM, ((flag == 1) ? BST_CHECKED : BST_UNCHECKED)); + CheckDlgButton(hwndDlg, IDC_START_FROM_SRAM, startFrom == START_SRAM ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_START_FROM_POWER_ON, startFrom == START_SAVESTATE ? BST_UNCHECKED : BST_CHECKED); + EnableWindow(GetDlgItem(hwndDlg, IDC_START_FROM_SRAM), startFrom != START_SAVESTATE); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_SRAMFILENAME), startFrom == START_SRAM); + EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_BROWSESRAM), startFrom == START_SRAM); SetFocus(GetDlgItem(hwndDlg, IDC_EDIT_FILENAME)); DateTime t = FCEUI_MovieGetRTCDefault(); @@ -313,7 +320,7 @@ static INT_PTR CALLBACK RecordDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, t.tm_sec = systime.wSecond; DateTime rtcstart(t.tm_year,t.tm_mon,t.tm_mday,t.tm_hour,t.tm_min,t.tm_sec); - FCEUI_SaveMovie(fname.c_str(), author, flag, sramfname, rtcstart); + FCEUI_SaveMovie(fname.c_str(), author, startFrom, sramfname, rtcstart); EndDialog(hwndDlg, 0); } return true; @@ -402,17 +409,34 @@ static INT_PTR CALLBACK RecordDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, break; } break; + + case IDC_START_FROM_SRAM: + { + bool checked = IsDlgButtonChecked(hwndDlg, IDC_START_FROM_SRAM); + startFrom = checked ? START_SRAM : START_BLANK; + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_SRAMFILENAME), checked); + EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_BROWSESRAM), checked); + + break; + } + + case IDC_START_FROM_POWER_ON: // TODO: test movies starting form savestate + { + bool powerOn = IsDlgButtonChecked(hwndDlg, IDC_START_FROM_POWER_ON); + startFrom = powerOn ? START_BLANK : START_SAVESTATE; + EnableWindow(GetDlgItem(hwndDlg, IDC_START_FROM_SRAM), powerOn); + if (!powerOn) + { + CheckDlgButton(hwndDlg, IDC_START_FROM_SRAM, false); + // CheckDlgButton does not send a WM_COMMAND message, so also disable SRAM stuff here + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_SRAMFILENAME), false); + EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_BROWSESRAM), false); + } + break; + } } } - HWND cur = GetDlgItem(hwndDlg, IDC_EDIT_SRAMFILENAME); - - IsDlgButtonChecked(hwndDlg, IDC_START_FROM_SRAM) ? flag=1 : flag=0; - IsDlgButtonChecked(hwndDlg, IDC_START_FROM_SRAM) ? EnableWindow(cur, TRUE) : EnableWindow(cur, FALSE); - - cur = GetDlgItem(hwndDlg, IDC_BUTTON_BROWSESRAM); - IsDlgButtonChecked(hwndDlg, IDC_START_FROM_SRAM) ? EnableWindow(cur, TRUE) : EnableWindow(cur, FALSE); - return false; } diff --git a/desmume/src/frontend/windows/replay.h b/desmume/src/frontend/windows/replay.h index 279d6a2e5..3f1b42ad0 100644 --- a/desmume/src/frontend/windows/replay.h +++ b/desmume/src/frontend/windows/replay.h @@ -18,6 +18,8 @@ #ifndef _WIN_REPLAY_H_ #define _WIN_REPLAY_H_ +enum START_FROM { START_BLANK, START_SRAM, START_SAVESTATE }; + void Replay_LoadMovie(); void MovieRecordTo(); diff --git a/desmume/src/frontend/windows/resource.h b/desmume/src/frontend/windows/resource.h index e7d0b1eec..593de50ef 100644 --- a/desmume/src/frontend/windows/resource.h +++ b/desmume/src/frontend/windows/resource.h @@ -273,6 +273,7 @@ #define IDC_BUTTON_BROWSESRAM 980 #define IDC_C_WATCH_UP 980 #define IDD_EDITWATCH 980 +#define IDC_START_FROM_POWER_ON 981 #define IDC_C_WATCH_DOWN 981 #define RAMMENU_FILE_AUTOLOAD 982 #define RAMMENU_FILE_SAVEWINDOW 983 diff --git a/desmume/src/frontend/windows/resources.rc b/desmume/src/frontend/windows/resources.rc index 69677ecc1..895440ed6 100644 --- a/desmume/src/frontend/windows/resources.rc +++ b/desmume/src/frontend/windows/resources.rc @@ -963,27 +963,28 @@ BEGIN PUSHBUTTON "Separator",IDC_C_WATCH_SEPARATE,225,136,36,14 END -IDD_RECORDMOVIE DIALOGEX 0, 0, 301, 124 +IDD_RECORDMOVIE DIALOGEX 0, 0, 301, 118 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Record movie" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - DEFPUSHBUTTON "OK",IDOK,177,106,50,14 - PUSHBUTTON "Cancel",IDCANCEL,231,106,50,14 - GROUPBOX "",65501,11,9,269,64 - LTEXT "File:",IDC_STATIC,31,22,14,8 - EDITTEXT IDC_EDIT_FILENAME,49,20,189,12,ES_AUTOHSCROLL - PUSHBUTTON "...",IDC_BUTTON_BROWSEFILE,249,20,18,14 - LTEXT "Author:",IDC_STATIC,21,38,25,8 - EDITTEXT IDC_EDIT_AUTHOR,49,36,189,14,ES_AUTOHSCROLL - LTEXT "Date:",IDC_STATIC,28,56,18,8 - CONTROL "",IDC_DTP_DATE,"SysDateTimePick32",DTS_RIGHTALIGN | DTS_LONGDATEFORMAT | WS_TABSTOP,49,54,123,12 - CONTROL "",IDC_DTP_TIME,"SysDateTimePick32",DTS_RIGHTALIGN | DTS_UPDOWN | WS_TABSTOP | 0x8,176,54,62,12 - GROUPBOX "",IDC_STATIC,11,73,269,29 - LTEXT "SRAM:",IDC_STATIC,19,85,25,8 - EDITTEXT IDC_EDIT_SRAMFILENAME,48,83,189,12,ES_AUTOHSCROLL - PUSHBUTTON "...",IDC_BUTTON_BROWSESRAM,248,83,18,14 - CONTROL "Start from SRAM",IDC_START_FROM_SRAM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,108,93,10 + DEFPUSHBUTTON "OK",IDOK,177,99,50,14 + PUSHBUTTON "Cancel",IDCANCEL,231,99,50,14 + GROUPBOX "",65501,11,2,269,64 + LTEXT "File:",IDC_STATIC,31,15,14,8 + EDITTEXT IDC_EDIT_FILENAME,49,13,189,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BUTTON_BROWSEFILE,249,13,18,14 + LTEXT "Author:",IDC_STATIC,21,31,25,8 + EDITTEXT IDC_EDIT_AUTHOR,49,29,189,14,ES_AUTOHSCROLL + LTEXT "Date:",IDC_STATIC,28,49,18,8 + CONTROL "",IDC_DTP_DATE,"SysDateTimePick32",DTS_RIGHTALIGN | DTS_LONGDATEFORMAT | WS_TABSTOP,49,47,123,12 + CONTROL "",IDC_DTP_TIME,"SysDateTimePick32",DTS_RIGHTALIGN | DTS_UPDOWN | WS_TABSTOP | 0x8,176,47,62,12 + GROUPBOX "",IDC_STATIC,11,66,269,29 + LTEXT "SRAM:",IDC_STATIC,19,78,25,8 + EDITTEXT IDC_EDIT_SRAMFILENAME,48,76,189,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BUTTON_BROWSESRAM,248,76,18,14 + CONTROL "Start from SRAM",IDC_START_FROM_SRAM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,101,73,10 + CONTROL "Start from power on",IDC_START_FROM_POWER_ON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,85,101,81,10 END IDD_REPLAY_METADATA DIALOGEX 0, 0, 325, 250 @@ -1904,7 +1905,7 @@ BEGIN IDD_RECORDMOVIE, DIALOG BEGIN - BOTTOMMARGIN, 113 + BOTTOMMARGIN, 107 END IDD_SOUNDSETTINGS, DIALOG @@ -2120,6 +2121,10 @@ BEGIN IDM_FILE_STOPAVI "Stop AVI" IDM_FILE_STOPWAV "Stop WAV" IDM_FILE_RECORDAVI "Record AVI" +END + +STRINGTABLE +BEGIN IDM_FILE_RECORDWAV "Record WAV" END @@ -2133,23 +2138,58 @@ BEGIN ID_LABEL_HK1 "Open ROM" ID_LABEL_HK2 "Reset" ID_LABEL_HK3 "Pause" - ID_LABEL_HK3b "CPU mode" - ID_LABEL_HK3c "Increase JIT block size" - ID_LABEL_HK3d "Decrease JIT block size" ID_LABEL_HK4 "Frame Advance" ID_LABEL_HK5 "Fast Forward" ID_LABEL_HK6 "Fast Forward Toggle" ID_LABEL_HK7 "Increase Speed" ID_LABEL_HK8 "Decrease Speed" - ID_LABEL_HK8b "Limit Framerate Toggle" ID_LABEL_HK9 "Microphone" ID_LABEL_HK10 "Auto-Hold" ID_LABEL_HK11 "Auto-Hold Clear" ID_LABEL_HK12 "Toggle Rasterizer" ID_LABEL_HK13 "Save Screenshot as" - ID_LABEL_HK13b "Quick Screenshot" ID_LABEL_HK14 "Record WAV" ID_LABEL_HK15 "Record AVI" +END + +STRINGTABLE +BEGIN + ID_LABEL_HK3b "CPU mode" + ID_LABEL_HK3c "Increase JIT block size" +END + +STRINGTABLE +BEGIN + ID_LABEL_HK3d "Decrease JIT block size" +END + +STRINGTABLE +BEGIN + ID_LABEL_HK8b "Limit Framerate Toggle" +END + +STRINGTABLE +BEGIN + ID_LABEL_HK48 "Turbo A" + ID_LABEL_HK49 "Turbo Y" + ID_LABEL_HK50 "Turbo X" + ID_LABEL_HK51 "Turbo R" + ID_LABEL_HK52 "Turbo L" + ID_LABEL_HK53 "Reload ROM" + ID_LABEL_HK13b "Quick Screenshot" + ID_LABEL_HK54 "Search Cheats" + ID_LABEL_HK55 "Increase Pressure" + ID_LABEL_HK56 "Decrease Pressure" + ID_LABEL_HK57 "Rotation 0" + ID_LABEL_HK58 "Rotation 90" + ID_LABEL_HK59 "Rotation 180" + ID_LABEL_HK60 "Rotation 270" + ID_LABEL_HK61 "Toggle Stylus Jitter" + ID_LABEL_HK62 "Toggle Cursor" +END + +STRINGTABLE +BEGIN ID_LABEL_HK16 "Toggle Frame Display" ID_LABEL_HK17 "Toggle FPS Display" ID_LABEL_HK18 "Toggle Input Display" @@ -2166,6 +2206,10 @@ BEGIN ID_LABEL_HK29 "Stylus Auto-Hold" ID_LABEL_HK30 "LCDs layout mode" ID_LABEL_HK31 "LCDs swap" +END + +STRINGTABLE +BEGIN ID_LABEL_HK32 "Increase Volume" ID_LABEL_HK33 "Decrease Volume" ID_LABEL_HK34 "Save to Slot " @@ -2182,21 +2226,10 @@ BEGIN ID_LABEL_HK45 "Turbo Select" ID_LABEL_HK46 "Turbo Start" ID_LABEL_HK47 "Turbo B" - ID_LABEL_HK48 "Turbo A" - ID_LABEL_HK49 "Turbo Y" - ID_LABEL_HK50 "Turbo X" - ID_LABEL_HK51 "Turbo R" - ID_LABEL_HK52 "Turbo L" - ID_LABEL_HK53 "Reload ROM" - ID_LABEL_HK54 "Search Cheats" - ID_LABEL_HK55 "Increase Pressure" - ID_LABEL_HK56 "Decrease Pressure" - ID_LABEL_HK57 "Rotation 0" - ID_LABEL_HK58 "Rotation 90" - ID_LABEL_HK59 "Rotation 180" - ID_LABEL_HK60 "Rotation 270" - ID_LABEL_HK61 "Toggle Stylus Jitter" - ID_LABEL_HK62 "Toggle Cursor" +END + +STRINGTABLE +BEGIN ID_LABEL_HK63 "List Cheats" ID_LABEL_HK64 "Toggle Cheats" ID_LABEL_HK65 "Toggle Main GPU" diff --git a/desmume/src/movie.cpp b/desmume/src/movie.cpp index 2fab4dd7b..8625df825 100644 --- a/desmume/src/movie.cpp +++ b/desmume/src/movie.cpp @@ -39,6 +39,8 @@ #include "version.h" #include "path.h" #include "emufile.h" +#include "replay.h" +#include "saves.h" using namespace std; bool freshMovie = false; //True when a movie loads, false when movie is altered. Used to determine if a movie has been altered since opening @@ -66,6 +68,10 @@ bool movie_readonly = true; char curMovieFilename[512] = {0}; MovieData currMovieData; +MovieData* oldSettings = NULL; +// Loading a movie calls NDS_Reset, which calls UnloadMovieEmulationSettings. Don't unload settings on that call. +bool firstReset = false; + int currRerecordCount; bool movie_reset_command = false; //-------------- @@ -195,7 +201,7 @@ DateTime FCEUI_MovieGetRTCDefault() return DateTime(2009,1,1,0,0,0); } -MovieData::MovieData() +MovieData::MovieData(bool fromCurrentSettings) : version(MOVIE_VERSION) , emuVersion(EMU_DESMUME_VERSION_NUMERIC()) , romChecksum(0) @@ -203,6 +209,31 @@ MovieData::MovieData() , binaryFlag(false) , rtcStart(FCEUI_MovieGetRTCDefault()) { + if (fromCurrentSettings) + { + useExtBios = CommonSettings.UseExtBIOS; + if (useExtBios) + swiFromBios = CommonSettings.SWIFromBIOS; + useExtFirmware = CommonSettings.UseExtFirmware; + if (useExtFirmware) + bootFromFirmware = CommonSettings.BootFromFirmware; + if (!CommonSettings.UseExtFirmware) + { + firmNickname.resize(CommonSettings.fw_config.nickname_len); + for (int i = 0; i < CommonSettings.fw_config.nickname_len; i++) + firmNickname[i] = CommonSettings.fw_config.nickname[i]; + firmMessage.resize(CommonSettings.fw_config.message_len); + for (int i = 0; i < CommonSettings.fw_config.message_len; i++) + firmMessage[i] = CommonSettings.fw_config.message[i]; + + firmFavColour = CommonSettings.fw_config.fav_colour; + firmBirthMonth = CommonSettings.fw_config.birth_month; + firmBirthDay = CommonSettings.fw_config.birth_day; + firmLanguage = CommonSettings.fw_config.language; + } + advancedTiming = CommonSettings.advanced_timing; + jitBlockSize = CommonSettings.use_jit ? CommonSettings.jit_max_block_size : 0; + } } void MovieData::truncateAt(int frame) @@ -211,68 +242,48 @@ void MovieData::truncateAt(int frame) records.resize(frame); } +void MovieData::installRomChecksum(std::string& val) +{ + // TODO: The current implementation of reading the checksum doesn't work correctly, and can + // cause crashes when the MovieData object is deallocated. (This is caused by StringToBytes() + // overrunning romChecksum into romSerial, making romSerial undefined.) Set romChecksum to + // some dummy value for now to prevent crashing. This is okay, since romChecksum isn't actually + // used in practice at this time. - rogerman, 2012/08/24 + //StringToBytes(val,&romChecksum,MD5DATA::size); + + romChecksum = 0; +} +void MovieData::installRtcStart(std::string& val) +{ + // sloppy format check and parse + const char *validFormatStr = "####-##-##T##:##:##Z"; + bool validFormat = true; + for (int i = 0; validFormatStr[i] != '\0'; i++) { + if (validFormatStr[i] != val[i] && + !(validFormatStr[i] == '#' && isdigit(val[i]))) { + validFormat = false; + break; + } + } + if (validFormat) { + const char *s = val.data(); + int year = atoi(&s[0]); + int mon = atoi(&s[5]); + int day = atoi(&s[8]); + int hour = atoi(&s[11]); + int min = atoi(&s[14]); + int sec = atoi(&s[17]); + rtcStart = DateTime(year, mon, day, hour, min, sec); + } +} +void MovieData::installComment(std::string& val) { comments.push_back(mbstowcs(val)); } +void MovieData::installSram(std::string& val) { BinaryDataFromString(val, &this->sram); } + void MovieData::installValue(std::string& key, std::string& val) { - //todo - use another config system, or drive this from a little data structure. because this is gross - if(key == "version") - installInt(val,version); - else if(key == "emuVersion") - installInt(val,emuVersion); - else if(key == "rerecordCount") - installInt(val,rerecordCount); - else if(key == "romFilename") - romFilename = val; - else if(key == "romChecksum") { - // TODO: The current implementation of reading the checksum doesn't work correctly, and can - // cause crashes when the MovieData object is deallocated. (This is caused by StringToBytes() - // overrunning romChecksum into romSerial, making romSerial undefined.) Set romChecksum to - // some dummy value for now to prevent crashing. This is okay, since romChecksum isn't actually - // used in practice at this time. - rogerman, 2012/08/24 - //StringToBytes(val,&romChecksum,MD5DATA::size); - - romChecksum = 0; - } - else if(key == "romSerial") - romSerial = val; - else if(key == "guid") - guid = Desmume_Guid::fromString(val); - else if(key == "rtcStart") { - // sloppy format check and parse - const char *validFormatStr = "####-##-##T##:##:##Z"; - bool validFormat = true; - for (int i = 0; validFormatStr[i] != '\0'; i++) { - if (validFormatStr[i] != val[i] && - !(validFormatStr[i] == '#' && isdigit(val[i]))) { - validFormat = false; - break; - } - } - if (validFormat) { - const char *s = val.data(); - int year = atoi(&s[0]); - int mon = atoi(&s[5]); - int day = atoi(&s[8]); - int hour = atoi(&s[11]); - int min = atoi(&s[14]); - int sec = atoi(&s[17]); - rtcStart = DateTime(year,mon,day,hour,min,sec); - } - } - else if(key == "rtcStartNew") { - DateTime::TryParse(val.c_str(),rtcStart); - } - else if(key == "comment") - comments.push_back(mbstowcs(val)); - else if(key == "binary") - installBool(val,binaryFlag); - else if(key == "savestate") - { - BinaryDataFromString(val, &this->savestate); - } - else if(key == "sram") - { - BinaryDataFromString(val, &this->sram); - } + ivm method = installValueMap[key]; + if (method != NULL) + (this->*method)(val); } @@ -287,13 +298,12 @@ int MovieData::dump(EMUFILE &fp, bool binary) fp.fprintf("romChecksum %s\n", u32ToHexString(gameInfo.crc).c_str()); fp.fprintf("romSerial %s\n", romSerial.c_str()); fp.fprintf("guid %s\n", guid.toString().c_str()); - fp.fprintf("useExtBios %d\n", CommonSettings.UseExtBIOS?1:0); - fp.fprintf("advancedTiming %d\n", CommonSettings.advanced_timing?1:0); + fp.fprintf("useExtBios %d\n", CommonSettings.UseExtBIOS?1:0); // TODO: include bios file data, not just a flag saying something was used if (CommonSettings.UseExtBIOS) fp.fprintf("swiFromBios %d\n", CommonSettings.SWIFromBIOS?1:0); - fp.fprintf("useExtFirmware %d\n", CommonSettings.UseExtFirmware?1:0); + fp.fprintf("useExtFirmware %d\n", CommonSettings.UseExtFirmware?1:0); // TODO: include firmware file data, not just a flag saying something was used if (CommonSettings.UseExtFirmware) { @@ -315,6 +325,9 @@ int MovieData::dump(EMUFILE &fp, bool binary) fp.fprintf("firmLanguage %d\n", CommonSettings.fw_config.language); } + fp.fprintf("advancedTiming %d\n", CommonSettings.advanced_timing?1:0); + fp.fprintf("jitBlockSize %d\n", CommonSettings.use_jit ? CommonSettings.jit_max_block_size : 0); + fp.fprintf("rtcStartNew %s\n", rtcStart.ToString().c_str()); for (u32 i = 0; i < comments.size(); i++) @@ -323,8 +336,7 @@ int MovieData::dump(EMUFILE &fp, bool binary) if (binary) fp.fprintf("binary 1\n"); - if (savestate.size() != 0) - fp.fprintf("savestate %s\n", BytesToString(&savestate[0],savestate.size()).c_str()); + fp.fprintf("savestate %d\n", savestate?1:0); if (sram.size() != 0) fp.fprintf("sram %s\n", BytesToString(&sram[0],sram.size()).c_str()); @@ -343,104 +355,116 @@ int MovieData::dump(EMUFILE &fp, bool binary) return end-start; } +std::string readUntilWhitespace(EMUFILE &fp) +{ + std::string ret = ""; + while (true) + { + int c = fp.fgetc(); + switch (c) + { + case -1: + case ' ': + case '\t': + case '\r': + case '\n': + return ret; + default: + ret += c; + break; + } + } +} +std::string readUntilNewline(EMUFILE &fp) +{ + std::string ret = ""; + while (true) + { + int c = fp.fgetc(); + switch (c) + { + case -1: + case '\r': + case '\n': + return ret; + default: + ret += c; + break; + } + } +} +void readUntilNotWhitespace(EMUFILE &fp) +{ + while (true) + { + int c = fp.fgetc(); + switch (c) + { + case -1: + return; + case ' ': + case '\t': + case '\r': + case '\n': + break; + default: + fp.unget(); + return; + } + } +} //yuck... another custom text parser. bool LoadFM2(MovieData &movieData, EMUFILE &fp, int size, bool stopAfterHeader) { - //TODO - start with something different. like 'desmume movie version 1" - int curr = fp.ftell(); + int endOfMovie; + if (size == INT_MAX) + endOfMovie = fp.size(); + else + endOfMovie = fp.ftell() + size; //movie must start with "version 1" + //TODO - start with something different. like 'desmume movie version 1" char buf[9]; - curr = fp.ftell(); - fp.fread(buf,9); - fp.fseek(curr, SEEK_SET); + fp.fread(buf, 9); + fp.fseek(-9, SEEK_CUR); // if(fp->fail()) return false; - if (memcmp(buf,"version 1",9)) + if (memcmp(buf, "version 1", 9)) return false; - std::string key,value; - enum { - NEWLINE, KEY, SEPARATOR, VALUE, RECORD, COMMENT - } state = NEWLINE; - bool bail = false; - for(;;) + while (fp.ftell() < endOfMovie) { - bool iswhitespace, isrecchar, isnewline; - int c; - if(size--<=0) goto bail; - c = fp.fgetc(); - if(c == -1) - goto bail; - iswhitespace = (c==' '||c=='\t'); - isrecchar = (c=='|'); - isnewline = (c==10||c==13); - if(isrecchar && movieData.binaryFlag && !stopAfterHeader) + readUntilNotWhitespace(fp); + int c = fp.fgetc(); + // This will be the case if there is a newline at the end of the file. + if (c == -1) break; + else if (c == '|') { - LoadFM2_binarychunk(movieData, fp, size); - return true; - } - switch(state) - { - case NEWLINE: - if(isnewline) goto done; - if(iswhitespace) goto done; - if(isrecchar) - goto dorecord; - //must be a key - key = ""; - value = ""; - goto dokey; - break; - case RECORD: + if (stopAfterHeader) break; + else if (movieData.binaryFlag) { - dorecord: - if (stopAfterHeader) return true; - int currcount = movieData.records.size(); - movieData.records.resize(currcount+1); - int preparse = fp.ftell(); - movieData.records[currcount].parse(fp); - int postparse = fp.ftell(); - size -= (postparse-preparse); - state = NEWLINE; + LoadFM2_binarychunk(movieData, fp, endOfMovie - fp.ftell()); break; } - - case KEY: - dokey: //dookie - state = KEY; - if(iswhitespace) goto doseparator; - if(isnewline) goto commit; - key += c; - break; - case SEPARATOR: - doseparator: - state = SEPARATOR; - if(isnewline) goto commit; - if(!iswhitespace) goto dovalue; - break; - case VALUE: - dovalue: - state = VALUE; - if(isnewline) goto commit; - value += c; - break; - case COMMENT: - default: - break; + else + { + int currcount = movieData.records.size(); + movieData.records.resize(currcount + 1); + movieData.records[currcount].parse(fp); + } + } + else // key value + { + fp.unget(); + std::string key = readUntilWhitespace(fp); + readUntilNotWhitespace(fp); + std::string value = readUntilNewline(fp); + movieData.installValue(key, value); } - goto done; - - bail: - bail = true; - if(state == VALUE) goto commit; - goto done; - commit: - movieData.installValue(key,value); - state = NEWLINE; - done: ; - if(bail) break; } + // just in case readUntilNotWhitespace read past the limit set by size parameter + fp.fseek(endOfMovie, SEEK_SET); + return true; } @@ -454,14 +478,14 @@ static void closeRecordingMovie() } } -/// Stop movie playback. +// Stop movie playback. static void StopPlayback() { driver->USR_InfoMessage("Movie playback stopped."); movieMode = MOVIEMODE_INACTIVE; } -/// Stop movie playback without closing the movie. +// Stop movie playback without closing the movie. static void FinishPlayback() { driver->USR_InfoMessage("Movie finished playing."); @@ -469,7 +493,7 @@ static void FinishPlayback() } -/// Stop movie recording +// Stop movie recording static void StopRecording() { driver->USR_InfoMessage("Movie recording stopped."); @@ -491,7 +515,66 @@ void FCEUI_StopMovie() freshMovie = false; } +static void LoadSettingsFromMovie(MovieData movieData) +{ + if (movieData.useExtBios != -1) + CommonSettings.UseExtBIOS = movieData.useExtBios; + if (movieData.swiFromBios != -1) + CommonSettings.SWIFromBIOS = movieData.swiFromBios; + if (movieData.useExtFirmware != -1) + CommonSettings.UseExtFirmware = movieData.useExtFirmware; + if (movieData.bootFromFirmware != -1) + CommonSettings.BootFromFirmware = movieData.bootFromFirmware; + if (!CommonSettings.UseExtFirmware) + { + if (movieData.firmNickname != "") + { + CommonSettings.fw_config.nickname_len = movieData.firmNickname.length() > MAX_FW_NICKNAME_LENGTH ? MAX_FW_NICKNAME_LENGTH : movieData.firmNickname.length(); + for (int i = 0; i < CommonSettings.fw_config.nickname_len; i++) + CommonSettings.fw_config.nickname[i] = movieData.firmNickname[i]; + } + if (movieData.firmMessage != "") + { + CommonSettings.fw_config.message_len = movieData.firmMessage.length() > MAX_FW_MESSAGE_LENGTH ? MAX_FW_MESSAGE_LENGTH : movieData.firmMessage.length(); + for (int i = 0; i < CommonSettings.fw_config.message_len; i++) + CommonSettings.fw_config.message[i] = movieData.firmMessage[i]; + } + if (movieData.firmFavColour != -1) + CommonSettings.fw_config.fav_colour = movieData.firmFavColour; + if (movieData.firmBirthMonth != -1) + CommonSettings.fw_config.birth_month = movieData.firmBirthMonth; + if (movieData.firmBirthDay != -1) + CommonSettings.fw_config.birth_day = movieData.firmBirthDay; + if (movieData.firmLanguage != -1) + CommonSettings.fw_config.language = movieData.firmLanguage; + + // reset firmware (some games can write to it) + NDS_CreateDummyFirmware(&CommonSettings.fw_config); + } + if (movieData.advancedTiming != -1) + CommonSettings.advanced_timing = movieData.advancedTiming; + if (movieData.jitBlockSize > 0 && movieData.jitBlockSize <= 100) + { + CommonSettings.use_jit = true; + CommonSettings.jit_max_block_size = movieData.jitBlockSize; + } + else + CommonSettings.use_jit = false; +} +void UnloadMovieEmulationSettings() +{ + if (oldSettings && !firstReset) + { + LoadSettingsFromMovie(*oldSettings); + delete oldSettings; + oldSettings = NULL; + } +} +bool AreMovieEmulationSettingsActive() +{ + return (bool)oldSettings; +} //begin playing an existing movie const char* _CDECL_ FCEUI_LoadMovie(const char *fname, bool _read_only, bool tasedit, int _pauseframe) { @@ -514,36 +597,11 @@ const char* _CDECL_ FCEUI_LoadMovie(const char *fname, bool _read_only, bool tas currMovieData = MovieData(); strcpy(curMovieFilename, fname); - //FCEUFILE *fp = FCEU_fopen(fname,0,"rb",0); - //if (!fp) return; - //if(fp->isArchive() && !_read_only) { - // FCEU_PrintError("Cannot open a movie in read+write from an archive."); - // return; - //} - - //LoadFM2(currMovieData, fp->stream, INT_MAX, false); - bool loadedfm2 = false; - bool opened = false; -// { - EMUFILE *fp = new EMUFILE_FILE(fname, "rb"); -// if(fs.is_open()) -// { - loadedfm2 = LoadFM2(currMovieData, *fp, INT_MAX, false); - opened = true; -// } -// fs.close(); - delete fp; -// } - if(!opened) - { - // for some reason fs.open doesn't work, it has to be a whole new fstream object -// fstream fs (fname, std::ios_base::in); - loadedfm2 = LoadFM2(currMovieData, *fp, INT_MAX, false); -// fs.close(); - delete fp; - } + EMUFILE *fp = new EMUFILE_FILE(fname, "rb"); + loadedfm2 = LoadFM2(currMovieData, *fp, INT_MAX, false); + delete fp; if(!loadedfm2) return "failed to load movie"; @@ -552,20 +610,26 @@ const char* _CDECL_ FCEUI_LoadMovie(const char *fname, bool _read_only, bool tas //fully reload the game to reinitialize everything before playing any movie //poweron(true); - // reset firmware (some games can write to it) - if (!CommonSettings.UseExtFirmware) + // set emulation/firmware settings + oldSettings = new MovieData(true); + LoadSettingsFromMovie(currMovieData); + + if (currMovieData.savestate) { - NDS_CreateDummyFirmware(&CommonSettings.fw_config); + // SS file name should be the same as the movie file name, except for extension + std::string ssName = fname; + ssName.erase(ssName.length() - 3, 3); + ssName.append("dst"); + if (!savestate_load(ssName.c_str())) + return "Could not load movie's savestate. There should be a .dst file with the same name as the movie, in the same folder."; + } + else + { + firstReset = true; + NDS_Reset(); + firstReset = false; } - NDS_Reset(); - - ////WE NEED TO LOAD A SAVESTATE - //if(currMovieData.savestate.size() != 0) - //{ - // bool success = MovieData::loadSavestateFrom(&currMovieData.savestate); - // if(!success) return; - //} lagframecounter=0; LagFrameFlag=0; lastLag=0; @@ -646,7 +710,7 @@ bool MovieData::loadSramFrom(std::vector* buf) //begin recording a new movie //TODO - BUG - the record-from-another-savestate doesnt work. -void FCEUI_SaveMovie(const char *fname, std::wstring author, int flag, std::string sramfname, const DateTime &rtcstart) +void FCEUI_SaveMovie(const char *fname, std::wstring author, START_FROM startFrom, std::string sramfname, const DateTime &rtcstart) { //if(!FCEU_IsValidUI(FCEUI_RECORDMOVIE)) // return; @@ -675,15 +739,26 @@ void FCEUI_SaveMovie(const char *fname, std::wstring author, int flag, std::stri NDS_CreateDummyFirmware(&CommonSettings.fw_config); } - NDS_Reset(); - //todo ? - //poweron(true); - //else - // MovieData::dumpSavestateTo(&currMovieData.savestate,Z_BEST_COMPRESSION); + if (startFrom == START_SAVESTATE) + { + // SS file name should be the same as the movie file name, except for extension + std::string ssName = fname; + ssName.erase(ssName.length() - 3, 3); + ssName.append("dst"); + savestate_save(ssName.c_str()); + currMovieData.savestate = true; + } + else + { + NDS_Reset(); - if(flag == 1) - EMUFILE::readAllBytes(&currMovieData.sram, sramfname); + //todo ? + //poweron(true); + + if (startFrom == START_SRAM) + EMUFILE::readAllBytes(&currMovieData.sram, sramfname); + } //we are going to go ahead and dump the header. from now on we will only be appending frames currMovieData.dump(*osRecordingMovie, false); diff --git a/desmume/src/movie.h b/desmume/src/movie.h index 67ede7ba3..14e805684 100644 --- a/desmume/src/movie.h +++ b/desmume/src/movie.h @@ -28,6 +28,8 @@ #include "utils/guid.h" #include "utils/md5.h" +#include "replay.h" + struct UserInput; class EMUFILE; @@ -132,8 +134,7 @@ private: class MovieData { public: - MovieData(); - + MovieData(bool fromCurrentSettings = false); int version; int emuVersion; @@ -142,7 +143,7 @@ public: u32 romChecksum; std::string romSerial; std::string romFilename; - std::vector savestate; + bool savestate = false; std::vector sram; std::vector records; std::vector comments; @@ -155,6 +156,21 @@ public: //was the frame data stored in binary? bool binaryFlag; + int useExtBios = -1; + int swiFromBios = -1; + int useExtFirmware = -1; + int bootFromFirmware = -1; + + std::string firmNickname = ""; + std::string firmMessage = ""; + int firmFavColour = -1; + int firmBirthMonth = -1; + int firmBirthDay = -1; + int firmLanguage = -1; + + int advancedTiming = -1; + int jitBlockSize = -1; + int getNumRecords() { return records.size(); } class TDictionary : public std::map @@ -191,22 +207,65 @@ public: void clearRecordRange(int start, int len); void insertEmpty(int at, int frames); - static bool loadSavestateFrom(std::vector* buf); - static void dumpSavestateTo(std::vector* buf, int compressionLevel); - static bool loadSramFrom(std::vector* buf); //void TryDumpIncremental(); private: - void installInt(std::string& val, int& var) - { - var = atoi(val.c_str()); - } + void installVersion(std::string& val) { version = atoi(val.c_str()); } + void installEmuVersion(std::string& val) { emuVersion = atoi(val.c_str()); } + void installRerecordCount(std::string& val) { rerecordCount = atoi(val.c_str()); } + void installRomFilename(std::string& val) { romFilename = val; } + void installRomSerial(std::string& val) { romSerial = val; } + void installGuid(std::string& val) { guid = Desmume_Guid::fromString(val); } + void installRtcStartNew(std::string& val) { DateTime::TryParse(val.c_str(), rtcStart); } + void installBinary(std::string& val) { binaryFlag = atoi(val.c_str()) != 0; } + void installUseExtBios(std::string& val) { useExtBios = atoi(val.c_str()) != 0; } + void installSwiFromBios(std::string& val) { swiFromBios = atoi(val.c_str()) != 0; } + void installUseExtFirmware(std::string& val) { useExtFirmware = atoi(val.c_str()) != 0; } + void installBootFromFirmware(std::string& val) { bootFromFirmware = atoi(val.c_str()) != 0; } + void installFirmNickname(std::string& val) { firmNickname = val; } + void installFirmMessage(std::string& val) { firmMessage = val; } + void installFirmFavColour(std::string& val) { firmFavColour = atoi(val.c_str()); } + void installFirmBirthMonth(std::string& val) { firmBirthMonth = atoi(val.c_str()); } + void installFirmBirthDay(std::string& val) { firmBirthDay = atoi(val.c_str()); } + void installFirmLanguage(std::string& val) { firmLanguage = atoi(val.c_str()); } + void installAdvancedTiming(std::string& val) { advancedTiming = atoi(val.c_str()) != 0; } + void installJitBlockSize(std::string& val) { jitBlockSize = atoi(val.c_str()); } + void installSavestate(std::string& val) { savestate = atoi(val.c_str()) != 0; } - void installBool(std::string& val, bool& var) - { - var = atoi(val.c_str())!=0; - } + void installRomChecksum(std::string& val); + void installRtcStart(std::string& val); + void installComment(std::string& val); + void installSram(std::string& val); + + typedef void(MovieData::* ivm)(std::string&); + std::map installValueMap = { + {"version", &MovieData::installVersion}, + {"emuVersion", &MovieData::installEmuVersion}, + {"rerecordCount", &MovieData::installRerecordCount}, + {"romFilename", &MovieData::installRomFilename}, + {"romChecksum", &MovieData::installRomChecksum}, + {"romSerial", &MovieData::installRomSerial}, + {"guid", &MovieData::installGuid}, + {"rtcStart", &MovieData::installRtcStart}, + {"rtcStartNew", &MovieData::installRtcStartNew}, + {"comment", &MovieData::installComment}, + {"binary", &MovieData::installBinary}, + {"useExtBios", &MovieData::installUseExtBios}, + {"swiFromBios", &MovieData::installSwiFromBios}, + {"useExtFirmware", &MovieData::installUseExtFirmware}, + {"bootFromFirmware", &MovieData::installBootFromFirmware}, + {"firmNickname", &MovieData::installFirmNickname}, + {"firmMessage", &MovieData::installFirmMessage}, + {"firmFavColour", &MovieData::installFirmFavColour}, + {"firmBirthMonth", &MovieData::installFirmBirthMonth}, + {"firmBirthDay", &MovieData::installFirmBirthDay}, + {"firmLanguage", &MovieData::installFirmLanguage}, + {"advancedTiming", &MovieData::installAdvancedTiming}, + {"jitBlockSize", &MovieData::installJitBlockSize}, + {"savestate", &MovieData::installSavestate}, + {"sram", &MovieData::installSram} + }; }; extern int currFrameCounter; @@ -216,8 +275,10 @@ extern MovieData currMovieData; //adelikat: main needs this for frame counter d extern bool movie_reset_command; bool FCEUI_MovieGetInfo(EMUFILE &fp, MOVIE_INFO &info, bool skipFrameCount); -void FCEUI_SaveMovie(const char *fname, std::wstring author, int flag, std::string sramfname, const DateTime &rtcstart); +void FCEUI_SaveMovie(const char *fname, std::wstring author, START_FROM startFrom, std::string sramfname, const DateTime &rtcstart); const char* _CDECL_ FCEUI_LoadMovie(const char *fname, bool _read_only, bool tasedit, int _pauseframe); // returns NULL on success, errmsg on failure +void UnloadMovieEmulationSettings(); +bool AreMovieEmulationSettingsActive(); void FCEUI_StopMovie(); void FCEUMOV_AddInputState(); void FCEUMOV_HandlePlayback(); diff --git a/desmume/src/saves.cpp b/desmume/src/saves.cpp index 5d4b6bc4d..ac78da24a 100644 --- a/desmume/src/saves.cpp +++ b/desmume/src/saves.cpp @@ -1042,12 +1042,9 @@ bool savestate_save (const char *file_name) { EMUFILE_MEMORY ms; size_t elems_written; -#ifdef HAVE_LIBZ - if (!savestate_save(ms, Z_DEFAULT_COMPRESSION)) -#else - if (!savestate_save(ms, 0)) -#endif + if (!savestate_save(ms)) return false; + FILE* file = fopen(file_name,"wb"); if(file) { diff --git a/desmume/src/saves.h b/desmume/src/saves.h index b3db4723e..e7059cf6d 100644 --- a/desmume/src/saves.h +++ b/desmume/src/saves.h @@ -21,6 +21,7 @@ #define _SRAM_H #include "types.h" +#include "zlib.h" #define NB_STATES 10 @@ -64,6 +65,6 @@ void savestate_slot(int num); void loadstate_slot(int num); bool savestate_load(class EMUFILE &is); -bool savestate_save(class EMUFILE &outstream, int compressionLevel); +bool savestate_save(class EMUFILE &outstream, int compressionLevel = Z_DEFAULT_COMPRESSION); #endif