From 3a1d872a6d285daf5a71ce498413a031977799cc Mon Sep 17 00:00:00 2001 From: zeromus Date: Wed, 17 Jan 2018 03:42:59 +0000 Subject: [PATCH] use pseudo-deterministic PRNG for random ram initialization (reseed PRNG from system clock every time poweron happens -- except when dealing with movies) --- trunk/src/fceu.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++++- trunk/src/movie.cpp | 12 +++++++++- trunk/src/movie.h | 4 ++-- 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/trunk/src/fceu.cpp b/trunk/src/fceu.cpp index a6dd4f2e..bafe8d18 100644 --- a/trunk/src/fceu.cpp +++ b/trunk/src/fceu.cpp @@ -796,12 +796,63 @@ void ResetNES(void) { } +int RAMInitSeed = 0; int RAMInitOption = 0; // Note: this option does not currently apply to WRAM. // Would it be appropriate to call FCEU_MemoryRand inside FCEU_gmalloc to initialize them? +u64 splitmix64(u32 input) { + u64 z = (input + 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); +} + +static inline u64 xoroshiro128plus_rotl(const u64 x, int k) { + return (x << k) | (x >> (64 - k)); +} + +u64 xoroshiro128plus_s[2]; +void xoroshiro128plus_seed(u32 input) +{ +//http://xoroshiro.di.unimi.it/splitmix64.c + u64 x = input; + + u64 z = (x += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + xoroshiro128plus_s[0] = z ^ (z >> 31); + + z = (x += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + xoroshiro128plus_s[1] = z ^ (z >> 31); +} + +//http://vigna.di.unimi.it/xorshift/xoroshiro128plus.c +u64 xoroshiro128plus_next() { + const u64 s0 = xoroshiro128plus_s[0]; + u64 s1 = xoroshiro128plus_s[1]; + const u64 result = s0 + s1; + + s1 ^= s0; + xoroshiro128plus_s[0] = xoroshiro128plus_rotl(s0, 55) ^ s1 ^ (s1 << 14); // a, b + xoroshiro128plus_s[1] = xoroshiro128plus_rotl(s1, 36); // c + + return result; +} + void FCEU_MemoryRand(uint8 *ptr, uint32 size) { int x = 0; + + //reseed random, unless we're in a movie + extern int disableBatteryLoading; + if(FCEUMOV_Mode(MOVIEMODE_INACTIVE) && !disableBatteryLoading) + RAMInitSeed = rand()&0x7FFF; + + //always reseed the PRNG with the current seed, for deterministic results (for that seed) + xoroshiro128plus_seed(RAMInitSeed); + while (size) { uint8 v = 0; switch (RAMInitOption) @@ -810,7 +861,7 @@ void FCEU_MemoryRand(uint8 *ptr, uint32 size) { case 0: v = (x & 4) ? 0xFF : 0x00; break; case 1: v = 0xFF; break; case 2: v = 0x00; break; - case 3: v = uint8(rand()); break; + case 3: v = (u8)(xoroshiro128plus_next()); break; // the default is this 8 byte pattern: 00 00 00 00 FF FF FF FF // it has been used in FCEUX since time immemorial diff --git a/trunk/src/movie.cpp b/trunk/src/movie.cpp index e58dcf98..76be97b7 100644 --- a/trunk/src/movie.cpp +++ b/trunk/src/movie.cpp @@ -35,6 +35,7 @@ extern bool mustEngageTaseditor; #endif extern int RAMInitOption; +extern int RAMInitSeed; #include #include @@ -395,6 +396,7 @@ MovieData::MovieData() , palFlag(false) , PPUflag(false) , RAMInitOption(0) + , RAMInitSeed(0) , rerecordCount(0) , binaryFlag(false) , loadFrameCount(-1) @@ -417,6 +419,8 @@ void MovieData::installValue(std::string& key, std::string& val) installBool(val,PPUflag); else if(key == "RAMInitOption") installInt(val,RAMInitOption); + else if(key == "RAMInitSeed") + installInt(val,RAMInitSeed); else if(key == "version") installInt(val,version); else if(key == "emuVersion") @@ -481,6 +485,7 @@ int MovieData::dump(EMUFILE *os, bool binary) os->fprintf("FDS %d\n" , fds?1:0 ); os->fprintf("NewPPU %d\n" , PPUflag?1:0 ); os->fprintf("RAMInitOption %d\n", RAMInitOption); + os->fprintf("RAMInitSeed %d\n", RAMInitSeed); for(uint32 i=0;ifprintf("comment %s\n" , wcstombs(comments[i]).c_str() ); @@ -817,6 +822,7 @@ void FCEUMOV_CreateCleanMovie() currMovieData.fds = isFDS; currMovieData.PPUflag = (newppu != 0); currMovieData.RAMInitOption = RAMInitOption; + currMovieData.RAMInitSeed = RAMInitSeed; } void FCEUMOV_ClearCommands() { @@ -881,6 +887,9 @@ bool FCEUI_LoadMovie(const char *fname, bool _read_only, int _pauseframe) LoadSubtitles(currMovieData); delete fp; + RAMInitOption = currMovieData.RAMInitOption; + RAMInitSeed = currMovieData.RAMInitSeed; + freshMovie = true; //Movie has been loaded, so it must be unaltered if (bindSavestate) AutoSS = false; //If bind savestate to movie is true, then their isn't a valid auto-save to load, so flag it //fully reload the game to reinitialize everything before playing any movie @@ -903,7 +912,7 @@ bool FCEUI_LoadMovie(const char *fname, bool _read_only, int _pauseframe) else FCEUI_SetVidSystem(0); - RAMInitOption = currMovieData.RAMInitOption; + //force the input configuration stored in the movie to apply FCEUD_SetInput(currMovieData.fourscore, currMovieData.microphone, (ESI)currMovieData.ports[0], (ESI)currMovieData.ports[1], (ESIFC)currMovieData.ports[2]); @@ -1573,6 +1582,7 @@ bool FCEUI_MovieGetInfo(FCEUFILE* fp, MOVIE_INFO& info, bool skipFrameCount) info.pal = md.palFlag; info.ppuflag = md.PPUflag; info.RAMInitOption = md.RAMInitOption; + info.RAMInitSeed = md.RAMInitSeed; info.nosynchack = true; info.num_frames = md.records.size(); info.md5_of_rom_used = md.romChecksum; diff --git a/trunk/src/movie.h b/trunk/src/movie.h index f37a17a2..cab767bd 100644 --- a/trunk/src/movie.h +++ b/trunk/src/movie.h @@ -42,7 +42,7 @@ typedef struct uint32 emu_version_used; // 9813 = 0.98.13 MD5DATA md5_of_rom_used; std::string name_of_rom_used; - int RAMInitOption; + int RAMInitOption, RAMInitSeed; std::vector comments; std::vector subtitles; @@ -199,7 +199,7 @@ public: int getNumRecords() { return records.size(); } - int RAMInitOption; + int RAMInitOption, RAMInitSeed; class TDictionary : public std::map {