diff --git a/desmume/src/GPU.cpp b/desmume/src/GPU.cpp index aeaf82584..26f109861 100644 --- a/desmume/src/GPU.cpp +++ b/desmume/src/GPU.cpp @@ -3230,7 +3230,7 @@ void gpu_savestate(std::ostream* os) //version write32le(0,os); - os->write((char*)GPU_tempScreen,sizeof(GPU_tempScreen)); + os->write((char*)GPU_screen,sizeof(GPU_screen)); write32le(MainScreen.gpu->affineInfo[0].x,os); write32le(MainScreen.gpu->affineInfo[0].y,os); @@ -3242,14 +3242,14 @@ void gpu_savestate(std::ostream* os) write32le(SubScreen.gpu->affineInfo[1].y,os); } -bool gpu_loadstate(std::istream* is) +bool gpu_loadstate(std::istream* is, int size) { //read version int version; if(read32le(&version,is) != 1) return false; if(version != 0) return false; - is->read((char*)GPU_tempScreen,sizeof(GPU_tempScreen)); + is->read((char*)GPU_screen,sizeof(GPU_screen)); read32le(&MainScreen.gpu->affineInfo[0].x,is); read32le(&MainScreen.gpu->affineInfo[0].y,is); read32le(&MainScreen.gpu->affineInfo[1].x,is); diff --git a/desmume/src/GPU.h b/desmume/src/GPU.h index b226b80f9..f2f5893b4 100644 --- a/desmume/src/GPU.h +++ b/desmume/src/GPU.h @@ -36,7 +36,7 @@ #include void gpu_savestate(std::ostream* os); -bool gpu_loadstate(std::istream* is); +bool gpu_loadstate(std::istream* is, int size); /******************************************************************************* this structure is for display control, diff --git a/desmume/src/GPU_osd.cpp b/desmume/src/GPU_osd.cpp index a3787d3e1..8d8a98ce3 100644 --- a/desmume/src/GPU_osd.cpp +++ b/desmume/src/GPU_osd.cpp @@ -180,7 +180,11 @@ void OSDCLASS::addFixed(u16 x, u16 y, const char *fmt, ...) if (strcmp(msg, old_msg) == 0) return; - render51.PrintString(1,x,y,render51.MakeColor(128,0,0),msg,&screenshell); + render51.PrintString(1,x-1,y-1,render51.MakeColor(0,0,0),msg,&screenshell); + render51.PrintString(1,x+1,y-1,render51.MakeColor(0,0,0),msg,&screenshell); + render51.PrintString(1,x-1,y+1,render51.MakeColor(0,0,0),msg,&screenshell); + render51.PrintString(1,x+1,y+1,render51.MakeColor(0,0,0),msg,&screenshell); + render51.PrintString(1,x,y,render51.MakeColor(255,255,255),msg,&screenshell); needUpdate = true; } diff --git a/desmume/src/MMU.cpp b/desmume/src/MMU.cpp index a3f0b1cce..044733d50 100644 --- a/desmume/src/MMU.cpp +++ b/desmume/src/MMU.cpp @@ -46,6 +46,7 @@ #include "mc.h" #include "addons.h" #include "mic.h" +#include "movie.h" #ifdef DO_ASSERT_UNALIGNED #define ASSERT_UNALIGNED(x) assert(x) diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index e874bc47a..a9c5126a2 100644 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -40,6 +40,7 @@ #include "bios.h" #include "debug.h" #include "cheatSystem.h" +#include "movie.h" #ifdef _WIN32 #include "./windows/disView.h" @@ -844,6 +845,23 @@ int NDS_LoadROM( const char *filename, int bmtype, u32 bmsize, return ret; } +void MovieSRAM(int bmtype, u32 bmsize) { + + char buf[MAX_PATH]; + + memset(buf, 0, MAX_PATH); + strcpy(buf, pathFilenameToROMwithoutExt); + strcat(buf, ".sav"); // DeSmuME memory card :) + + if(movieMode != MOVIEMODE_INACTIVE) { + strcat(buf, "movie"); + } + + mc_realloc(&MMU.bupmem, bmtype, bmsize); + mc_load_file(&MMU.bupmem, buf); + +} + void NDS_FreeROM(void) { if (MMU.CART_ROM != MMU.UNUSED_RAM) @@ -869,6 +887,8 @@ void NDS_Reset(BOOL resetBySavestate) lastLag=0; TotalLagFrames=0; + currFrameCounter=0; + MMU_clearMem(); //ARM7 BIOS IRQ HANDLER @@ -1565,10 +1585,9 @@ u32 NDS_exec(s32 nb) { int i, j; - //TODO - since NDS_exec is not necessarily called one frame at a time, this could be wrong. - LagFrameFlag=1; + nb = 560190<<1; - nb += nds.cycles;//(nds.cycles>>26)<<26; + LagFrameFlag=1; //increase this to execute more instructions in each batch (reducing overhead) //the value of 4 seems to optimize speed.. do lower values increase precision? @@ -2393,23 +2412,30 @@ static std::string MakeInputDisplayString(u16 pad, u16 padExt) { return s; } +void ClearAutoHold(void) { + + for (int i=0; i < 10; i++) { + AutoHold.hold(i)=false; + } +} + void NDS_setPadFromMovie(u16 pad) { #define FIX(b,n) (((pad>>n)&1)!=0) NDS_setPad( - FIX(pad,0), - FIX(pad,1), + FIX(pad,12), //R + FIX(pad,11), //L + FIX(pad,10), //D + FIX(pad,9), //U + FIX(pad,7), //Select + FIX(pad,8), //Start + FIX(pad,6), //B + FIX(pad,5), //A + FIX(pad,4), //Y + FIX(pad,3), //X FIX(pad,2), - FIX(pad,3), - FIX(pad,4), - FIX(pad,5), - FIX(pad,6), - FIX(pad,7), - FIX(pad,8), - FIX(pad,9), - FIX(pad,10), - FIX(pad,11), - FIX(pad,12), + FIX(pad,1), + FIX(pad,0), FIX(pad,13) ); #undef FIX @@ -2418,12 +2444,12 @@ void NDS_setPadFromMovie(u16 pad) turbo Turbo; turbotime TurboTime; -static void SetTurbo(bool (&pad) [10]) { +static void SetTurbo(bool (&pad) [12]) { bool turbo[4] = {true, false, true, false}; bool currentbutton; - - for (int i=0; i < 10; i++) { + + for (int i=0; i < 12; i++) { currentbutton=Turbo.button(i); if(currentbutton) { @@ -2435,7 +2461,7 @@ static void SetTurbo(bool (&pad) [10]) { else TurboTime.time(i)=0; //reset timer if the button isn't pressed } - for (int i=0; i<10; i++) + for (int i=0; i<12; i++) TurboTime.time(i)++; } @@ -2444,7 +2470,7 @@ autohold AutoHold; void NDS_setPad(bool R,bool L,bool D,bool U,bool T,bool S,bool B,bool A,bool Y,bool X,bool W,bool E,bool G, bool F) { - bool padarray[10] = {R, L, D, U, T, S, B, A, Y, X}; + bool padarray[12] = {R, L, D, U, T, S, B, A, Y, X, W, E}; SetTurbo(padarray); @@ -2458,6 +2484,8 @@ void NDS_setPad(bool R,bool L,bool D,bool U,bool T,bool S,bool B,bool A,bool Y,b A=padarray[7]; Y=padarray[8]; X=padarray[9]; + W=padarray[10]; + E=padarray[11]; if(AutoHold.Right) R=!padarray[0]; if(AutoHold.Left) L=!padarray[1]; @@ -2556,20 +2584,18 @@ void NDS_setPad(bool R,bool L,bool D,bool U,bool T,bool S,bool B,bool A,bool Y,b nds.pad = - (FIX(r)<<0)| - (FIX(l)<<1)| - (FIX(d)<<2)| - (FIX(u)<<3)| - (FIX(t)<<4)| - (FIX(s)<<5)| + (FIX(r)<<12)| + (FIX(l)<<11)| + (FIX(d)<<10)| + (FIX(u)<<9)| + (FIX(s)<<8)| + (FIX(t)<<7)| (FIX(b)<<6)| - (FIX(a)<<7)| - (FIX(y)<<8)| - (FIX(x)<<9)| - (FIX(w)<<10)| - (FIX(e)<<11)| - (FIX(g)<<12)| - (FIX(f)<<13); + (FIX(a)<<5)| + (FIX(y)<<4)| + (FIX(x)<<3)| + (FIX(w)<<2)| + (FIX(e)<<1); // TODO: low power IRQ } diff --git a/desmume/src/NDSSystem.h b/desmume/src/NDSSystem.h index 0092d79b9..c4804a2b6 100644 --- a/desmume/src/NDSSystem.h +++ b/desmume/src/NDSSystem.h @@ -45,6 +45,8 @@ struct turbo { bool A; bool Y; bool X; + bool L; + bool R; bool &button(int i) { return ((bool*)this)[i]; } }; @@ -62,6 +64,8 @@ struct turbotime { int A; int Y; int X; + int L; + int R; int &time(int i) { return ((int*)this)[i]; } }; @@ -380,6 +384,7 @@ class Driver { public: virtual BOOL WIFI_Host_InitSystem() { return FALSE; } virtual void WIFI_Host_ShutdownSystem() {} + virtual BOOL AVI_IsRecording() { return FALSE; } }; extern Driver* driver; @@ -387,6 +392,10 @@ extern std::string InputDisplayString; extern int LagFrameFlag; extern int lastLag, TotalLagFrames; +void MovieSRAM(int bmtype, u32 bmsize); + +void ClearAutoHold(void); + #endif diff --git a/desmume/src/OGLRender.cpp b/desmume/src/OGLRender.cpp index 4cd4cae75..8d77d4685 100644 --- a/desmume/src/OGLRender.cpp +++ b/desmume/src/OGLRender.cpp @@ -81,9 +81,6 @@ static ALIGN(16) u8 GPU_screen3D [256*192*4]; static const unsigned short map3d_cull[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0}; static const int texEnv[4] = { GL_MODULATE, GL_DECAL, GL_MODULATE, GL_MODULATE }; static const int depthFunc[2] = { GL_LESS, GL_EQUAL }; -static bool needRefreshFramebuffer = false; - -static bool validFramebuffer = false; //derived values extracted from polyattr etc static bool wireframe=false, alpha31=false; @@ -342,7 +339,6 @@ static void OGLReset() // memset(GPU_screenStencil,0,sizeof(GPU_screenStencil)); memset(GPU_screen3D,0,sizeof(GPU_screen3D)); - needRefreshFramebuffer = false; } @@ -678,6 +674,49 @@ static void Control() } } + +static void GL_ReadFramebuffer() +{ + if(!BEGINGL()) return; + glFinish(); +// glReadPixels(0,0,256,192,GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, GPU_screenStencil); + glReadPixels(0,0,256,192,GL_BGRA_EXT, GL_UNSIGNED_BYTE, GPU_screen3D); + ENDGL(); + + //convert the pixels to a different format which is more convenient + //is it safe to modify the screen buffer? if not, we could make a temp copy + for(int i=0,y=191;y>=0;y--) + { + u16* dst = gfx3d_convertedScreen + (y<<8); + u8* dstAlpha = gfx3d_convertedAlpha + (y<<8); + + #ifndef NOSSE2 + //I dont know much about this kind of stuff, but this seems to help + //for some reason I couldnt make the intrinsics work + u8* wanx = (u8*)&((u32*)GPU_screen3D)[i]; + #define ASS(X,Y) __asm { prefetchnta [wanx+32*0x##X##Y] } + #define PUNK(X) ASS(X,0) ASS(X,1) ASS(X,2) ASS(X,3) ASS(X,4) ASS(X,5) ASS(X,6) ASS(X,7) ASS(X,8) ASS(X,9) ASS(X,A) ASS(X,B) ASS(X,C) ASS(X,D) ASS(X,E) ASS(X,F) + PUNK(0); PUNK(1); + #endif + + for(int x=0;x<256;x++,i++) + { + u32 &u32screen3D = ((u32*)GPU_screen3D)[i]; + u32screen3D>>=3; + u32screen3D &= 0x1F1F1F1F; + + const int t = i<<2; + const u8 a = GPU_screen3D[t+3]; + const u8 r = GPU_screen3D[t+2]; + const u8 g = GPU_screen3D[t+1]; + const u8 b = GPU_screen3D[t+0]; + dst[x] = R5G5B5TORGB15(r,g,b) | alpha_lookup[a]; + dstAlpha[x] = alpha_5bit_to_4bit[a]; + } + } +} + + static void OGLRender() { if(!BEGINGL()) return; @@ -819,10 +858,9 @@ static void OGLRender() } } - //since we just redrew, we need to refresh the framebuffers - needRefreshFramebuffer = true; - ENDGL(); + + GL_ReadFramebuffer(); } static void OGLVramReconfigureSignal() @@ -830,58 +868,6 @@ static void OGLVramReconfigureSignal() TexCache_Invalidate(); } - -static void GL_ReadFramebuffer() -{ - if(!BEGINGL()) return; - glFinish(); -// glReadPixels(0,0,256,192,GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, GPU_screenStencil); - glReadPixels(0,0,256,192,GL_BGRA_EXT, GL_UNSIGNED_BYTE, GPU_screen3D); - ENDGL(); - - //convert the pixels to a different format which is more convenient - //is it safe to modify the screen buffer? if not, we could make a temp copy - for(int i=0,y=191;y>=0;y--) - { - u16* dst = gfx3d_convertedScreen + (y<<8); - u8* dstAlpha = gfx3d_convertedAlpha + (y<<8); - - #ifndef NOSSE2 - //I dont know much about this kind of stuff, but this seems to help - //for some reason I couldnt make the intrinsics work - u8* wanx = (u8*)&((u32*)GPU_screen3D)[i]; - #define ASS(X,Y) __asm { prefetchnta [wanx+32*0x##X##Y] } - #define PUNK(X) ASS(X,0) ASS(X,1) ASS(X,2) ASS(X,3) ASS(X,4) ASS(X,5) ASS(X,6) ASS(X,7) ASS(X,8) ASS(X,9) ASS(X,A) ASS(X,B) ASS(X,C) ASS(X,D) ASS(X,E) ASS(X,F) - PUNK(0); PUNK(1); - #endif - - for(int x=0;x<256;x++,i++) - { - u32 &u32screen3D = ((u32*)GPU_screen3D)[i]; - u32screen3D>>=3; - u32screen3D &= 0x1F1F1F1F; - - const int t = i<<2; - const u8 a = GPU_screen3D[t+3]; - const u8 r = GPU_screen3D[t+2]; - const u8 g = GPU_screen3D[t+1]; - const u8 b = GPU_screen3D[t+0]; - dst[x] = R5G5B5TORGB15(r,g,b) | alpha_lookup[a]; - dstAlpha[x] = alpha_5bit_to_4bit[a]; - } - } -} - -static void OGLCheckFresh() -{ - if(needRefreshFramebuffer) - { - needRefreshFramebuffer = false; - GL_ReadFramebuffer(); - } -} - - GPU3DInterface gpu3Dgl = { "OpenGL", OGLInit, @@ -889,5 +875,4 @@ GPU3DInterface gpu3Dgl = { OGLClose, OGLRender, OGLVramReconfigureSignal, - OGLCheckFresh, }; diff --git a/desmume/src/SPU.cpp b/desmume/src/SPU.cpp index e0d4e9ee5..bcb4d5d2d 100644 --- a/desmume/src/SPU.cpp +++ b/desmume/src/SPU.cpp @@ -32,6 +32,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "mem.h" #include "readwrite.h" #include "armcpu.h" +#include "NDSSystem.h" SPU_struct *SPU_core = 0; SPU_struct *SPU_user = 0; @@ -1034,7 +1035,10 @@ void SPU_Emulate_core() spu_core_samples = (int)(samples); samples -= spu_core_samples; - SPU_MixAudio(SPU_core,spu_core_samples); + if(driver->AVI_IsRecording()) + SPU_MixAudio(SPU_core,spu_core_samples); + else + SPU_MixAudio(SPU_core,spu_core_samples); } void SPU_Emulate_user() @@ -1300,7 +1304,7 @@ void spu_savestate(std::ostream* os) } } -bool spu_loadstate(std::istream* is) +bool spu_loadstate(std::istream* is, int size) { //read version int version; diff --git a/desmume/src/SPU.h b/desmume/src/SPU.h index 3279ce6db..6fc34b217 100644 --- a/desmume/src/SPU.h +++ b/desmume/src/SPU.h @@ -112,6 +112,6 @@ extern SPU_struct *SPU_core, *SPU_user; extern int spu_core_samples; void spu_savestate(std::ostream* os); -bool spu_loadstate(std::istream* is); +bool spu_loadstate(std::istream* is, int size); #endif diff --git a/desmume/src/gfx3d.cpp b/desmume/src/gfx3d.cpp index e61cffb14..a924df224 100644 --- a/desmume/src/gfx3d.cpp +++ b/desmume/src/gfx3d.cpp @@ -183,7 +183,7 @@ static u32 lightColor[4] = {0,0,0,0}; static u32 lightDirection[4] = {0,0,0,0}; //material state: static u16 dsDiffuse, dsAmbient, dsSpecular, dsEmission; -/* Shininess */ +// Shininess static float shininessTable[128] = {0}; static int shininessInd = 0; @@ -315,6 +315,9 @@ void gfx3d_reset() last_t = 0; last_s = 0; viewport = 0xBFFF0000; + + memset(gfx3d_convertedScreen,0,sizeof(gfx3d_convertedScreen)); + memset(gfx3d_convertedAlpha,0,sizeof(gfx3d_convertedAlpha)); GFX_FIFOclear(); } @@ -1470,6 +1473,13 @@ void gfx3d_VBlankEndSignal(bool skipFrame) drawPending = FALSE; gpu3D->NDS_3D_Render(); + + //if the null 3d core is chosen, then we need to clear out the 3d buffers to keep old data from being rendered + if(gpu3D == &gpu3DNull) + { + memset(gfx3d_convertedScreen,0,sizeof(gfx3d_convertedScreen)); + memset(gfx3d_convertedScreen,0,sizeof(gfx3d_convertedAlpha)); + } } #ifdef USE_GEOMETRY_FIFO_EMULATION @@ -2221,7 +2231,6 @@ void gfx3d_glGetLightColor(unsigned int index, unsigned int* dest) void gfx3d_GetLineData(int line, u16** dst, u8** dstAlpha) { - gpu3D->NDS_3D_CheckFresh(); *dst = gfx3d_convertedScreen+((line)<<8); if(dstAlpha != NULL) { @@ -2286,11 +2295,6 @@ SFORMAT SF_GFX3D[]={ { "GMAM", 2, 1, &dsAmbient}, { "GMSP", 2, 1, &dsSpecular}, { "GMEM", 2, 1, &dsEmission}, - { "GTST", 4, 1, &triStripToggle}, - { "GTVC", 4, 1, &tempVertInfo.count}, - { "GTVM", 4, 4, &tempVertInfo.map[0]}, - { "GTVF", 4, 1, &tempVertInfo.first}, - { "GLTW", 4, 1, &listTwiddle}, { "GFLP", 4, 1, &flushPending}, { "GDRP", 4, 1, &drawPending}, { "GSET", 4, 1, &gfx3d.enableTexturing}, @@ -2310,6 +2314,13 @@ SFORMAT SF_GFX3D[]={ { "GSTT", 4, 32, gfx3d.rgbToonTable}, { "GSST", 4, 128, shininessTable}, { "GSSI", 4, 1, &shininessInd}, + //------------------------ + { "GTST", 4, 1, &triStripToggle}, + { "GTVC", 4, 1, &tempVertInfo.count}, + { "GTVM", 4, 4, tempVertInfo.map}, + { "GTVF", 4, 1, &tempVertInfo.first}, + { "G3CS", 2, 256*192, gfx3d_convertedScreen}, + { "G3CA", 2, 256*192, gfx3d_convertedAlpha}, { 0 } }; @@ -2317,10 +2328,15 @@ SFORMAT SF_GFX3D[]={ void gfx3d_savestate(std::ostream* os) { //dump the render lists - //TODO!!!! + OSWRITE(vertlist->count); + for(int i=0;icount;i++) + vertlist->list[i].save(os); + OSWRITE(polylist->count); + for(int i=0;icount;i++) + polylist->list[i].save(os); } -bool gfx3d_loadstate(std::istream* is) +bool gfx3d_loadstate(std::istream* is, int size) { gfx3d_glPolygonAttrib_cache(); gfx3d_glTexImage_cache(); @@ -2331,14 +2347,21 @@ bool gfx3d_loadstate(std::istream* is) gfx3d_glLightDirection_cache(3); //jiggle the lists. and also wipe them. this is clearly not the best thing to be doing. + listTwiddle = 0; polylist = &polylists[listTwiddle]; vertlist = &vertlists[listTwiddle]; - polylist->count = 0; - vertlist->count = 0; + + OSREAD(vertlist->count); + for(int i=0;icount;i++) + vertlist->list[i].load(is); + OSREAD(polylist->count); + for(int i=0;icount;i++) + polylist->list[i].load(is); + gfx3d.polylist = &polylists[listTwiddle^1]; gfx3d.vertlist = &vertlists[listTwiddle^1]; gfx3d.polylist->count=0; gfx3d.vertlist->count=0; return true; -} +} \ No newline at end of file diff --git a/desmume/src/gfx3d.h b/desmume/src/gfx3d.h index eb65084c6..b612c7980 100644 --- a/desmume/src/gfx3d.h +++ b/desmume/src/gfx3d.h @@ -26,6 +26,8 @@ #include "types.h" #include +#include +#include //produce a 32bpp color from a DS RGB16 #define RGB16TO32(col,alpha) (((alpha)<<24) | ((((col) & 0x7C00)>>7)<<16) | ((((col) & 0x3E0)>>2)<<8) | (((col) & 0x1F)<<3)) @@ -68,8 +70,6 @@ struct POLY { int type; //tri or quad u16 vertIndexes[4]; //up to four verts can be referenced by this poly u32 polyAttr, texParam, texPalette; //the hardware rendering params -// int projIndex; //the index into the projlist that this poly uses - u32 pad; u32 viewport; float miny, maxy; @@ -88,6 +88,26 @@ struct POLY { } int getAlpha() { return (polyAttr>>16)&0x1F; } + + void save(std::ostream* os) + { + OSWRITE(type); + OSWRITE(vertIndexes[0]); OSWRITE(vertIndexes[1]); OSWRITE(vertIndexes[2]); OSWRITE(vertIndexes[3]); + OSWRITE(polyAttr); OSWRITE(texParam); OSWRITE(texPalette); + OSWRITE(viewport); + OSWRITE(miny); + OSWRITE(maxy); + } + + void load(std::istream* is) + { + OSREAD(type); + OSREAD(vertIndexes[0]); OSREAD(vertIndexes[1]); OSREAD(vertIndexes[2]); OSREAD(vertIndexes[3]); + OSREAD(polyAttr); OSREAD(texParam); OSREAD(texPalette); + OSREAD(viewport); + OSREAD(miny); + OSREAD(maxy); + } }; #define POLYLIST_SIZE 100000 @@ -117,6 +137,20 @@ struct VERT { fcolor[1] = color[1]; fcolor[2] = color[2]; } + void save(std::ostream* os) + { + OSWRITE(x); OSWRITE(y); OSWRITE(z); OSWRITE(w); + OSWRITE(u); OSWRITE(v); + OSWRITE(color[0]); OSWRITE(color[1]); OSWRITE(color[2]); + OSWRITE(fcolor[0]); OSWRITE(fcolor[1]); OSWRITE(fcolor[2]); + } + void load(std::istream* is) + { + OSREAD(x); OSREAD(y); OSREAD(z); OSREAD(w); + OSREAD(u); OSREAD(v); + OSREAD(color[0]); OSREAD(color[1]); OSREAD(color[2]); + OSREAD(fcolor[0]); OSREAD(fcolor[1]); OSREAD(fcolor[2]); + } }; #define VERTLIST_SIZE 400000 @@ -267,6 +301,6 @@ void gfx3d_GetLineData(int line, u16** dst, u8** dstAlpha); struct SFORMAT; extern SFORMAT SF_GFX3D[]; void gfx3d_savestate(std::ostream* os); -bool gfx3d_loadstate(std::istream* is); +bool gfx3d_loadstate(std::istream* is, int size); #endif diff --git a/desmume/src/mc.cpp b/desmume/src/mc.cpp index a439b3768..69af93a48 100644 --- a/desmume/src/mc.cpp +++ b/desmume/src/mc.cpp @@ -23,6 +23,7 @@ #include "debug.h" #include "types.h" #include "mc.h" +#include "movie.h" #define FW_CMD_READ 0x3 #define FW_CMD_WRITEDISABLE 0x4 @@ -207,9 +208,16 @@ void mc_load_file(memory_chip_t *mc, const char* filename) { long size; int type = -1; - FILE* file = fopen(filename, "rb+"); + FILE* file; size_t elems_read; + if(movieMode != MOVIEMODE_INACTIVE) { + mc->filename = strdup(filename); + return; + } + else + file = fopen(filename, "rb+"); + if(file == NULL) { mc->filename = strdup(filename); diff --git a/desmume/src/movie.cpp b/desmume/src/movie.cpp index d507eeeb7..471301658 100644 --- a/desmume/src/movie.cpp +++ b/desmume/src/movie.cpp @@ -1,6 +1,6 @@ /* movie.cpp * - * Copyright (C) 2006-2008 Zeromus + * Copyright (C) 2006-2009 DeSmuME team * * This file is part of DeSmuME * @@ -22,14 +22,18 @@ #include #include #include - +#include "main.h" #include "utils/guid.h" #include "utils/xstring.h" #include "movie.h" #include "NDSSystem.h" +#include "readwrite.h" #include "debug.h" +#include "rtc.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 +bool autoMovieBackup = true; #define FCEU_PrintError LOG @@ -189,6 +193,8 @@ void MovieData::installValue(std::string& key, std::string& val) guid = Desmume_Guid::fromString(val); else if(key == "comment") comments.push_back(mbstowcs(val)); + else if(key == "binary") + installBool(val,binaryFlag); else if(key == "savestate") { int len = Base64StringToBytesLength(val); @@ -222,10 +228,10 @@ int MovieData::dump(std::ostream *os, bool binary) *os << "savestate " << BytesToString(&savestate[0],savestate.size()) << endl; if(binary) { - ////put one | to start the binary dump - //os->put('|'); - //for(int i=0;i<(int)records.size();i++) - // records[i].dumpBinary(this,os,i); + //put one | to start the binary dump + os->put('|'); + for(int i=0;i<(int)records.size();i++) + records[i].dumpBinary(this,os,i); } else for(int i=0;i<(int)records.size();i++) @@ -268,8 +274,8 @@ static bool LoadFM2(MovieData& movieData, std::istream* fp, int size, bool stopA isnewline = (c==10||c==13); if(isrecchar && movieData.binaryFlag && !stopAfterHeader) { - //LoadFM2_binarychunk(movieData, fp, size); - return false; + LoadFM2_binarychunk(movieData, fp, size); + return true; } switch(state) { @@ -349,7 +355,7 @@ static void closeRecordingMovie() /// Stop movie playback. static void StopPlayback() { - //FCEU_DispMessageOnMovie("Movie playback stopped."); + SetMessageToDisplay("Movie playback stopped."); movieMode = MOVIEMODE_INACTIVE; } @@ -357,8 +363,7 @@ static void StopPlayback() /// Stop movie recording static void StopRecording() { - //FCEU_DispMessage("Movie recording stopped."); - + SetMessageToDisplay("Movie recording stopped."); movieMode = MOVIEMODE_INACTIVE; closeRecordingMovie(); @@ -366,7 +371,7 @@ static void StopRecording() -static void FCEUI_StopMovie() +void FCEUI_StopMovie() { //if(suppressMovieStop) // return; @@ -377,11 +382,12 @@ static void FCEUI_StopMovie() StopRecording(); curMovieFilename[0] = 0; + freshMovie = false; } //begin playing an existing movie -static void FCEUI_LoadMovie(const char *fname, bool _read_only, bool tasedit, int _pauseframe) +void FCEUI_LoadMovie(const char *fname, bool _read_only, bool tasedit, int _pauseframe) { //if(!tasedit && !FCEU_IsValidUI(FCEUI_PLAYMOVIE)) // return; @@ -416,6 +422,7 @@ static void FCEUI_LoadMovie(const char *fname, bool _read_only, bool tasedit, in //fully reload the game to reinitialize everything before playing any movie //poweron(true); + NDS_Reset(); ////WE NEED TO LOAD A SAVESTATE //if(currMovieData.savestate.size() != 0) //{ @@ -428,11 +435,15 @@ static void FCEUI_LoadMovie(const char *fname, bool _read_only, bool tasedit, in movie_readonly = _read_only; movieMode = MOVIEMODE_PLAY; currRerecordCount = currMovieData.rerecordCount; + InitMovieTime(); + MovieSRAM(backupmemorytype, backupmemorysize); + freshMovie = true; + ClearAutoHold(); - //if(movie_readonly) - // FCEU_DispMessage("Replay started Read-Only."); - //else - // FCEU_DispMessage("Replay started Read+Write."); + if(movie_readonly) + SetMessageToDisplay("Replay started Read-Only."); + else + SetMessageToDisplay("Replay started Read+Write."); } static void openRecordingMovie(const char* fname) @@ -447,7 +458,7 @@ static void openRecordingMovie(const char* fname) //begin recording a new movie //TODO - BUG - the record-from-another-savestate doesnt work. -static void FCEUI_SaveMovie(const char *fname, std::wstring author) + void FCEUI_SaveMovie(const char *fname, std::wstring author) { //if(!FCEU_IsValidUI(FCEUI_RECORDMOVIE)) // return; @@ -468,6 +479,7 @@ static void FCEUI_SaveMovie(const char *fname, std::wstring author) //currMovieData.romChecksum = GameInfo->MD5; //currMovieData.romFilename = FileBase; + NDS_Reset(); //todo ? //poweron(true); //else @@ -479,81 +491,109 @@ static void FCEUI_SaveMovie(const char *fname, std::wstring author) movieMode = MOVIEMODE_RECORD; movie_readonly = false; currRerecordCount = 0; + InitMovieTime(); + MovieSRAM(backupmemorytype, backupmemorysize); - //FCEU_DispMessage("Movie recording started."); + SetMessageToDisplay("Movie recording started."); } + void NDS_setTouchFromMovie(void) { -//the main interaction point between the emulator and the movie system. -//either dumps the current joystick state or loads one state from the movie -static void FCEUMOV_AddInputState() -{ - //todo - for tasedit, either dump or load depending on whether input recording is enabled - //or something like that - //(input recording is just like standard read+write movie recording with input taken from gamepad) - //otherwise, it will come from the tasedit data. + if(movieMode == MOVIEMODE_PLAY) + { - if(movieMode == MOVIEMODE_PLAY) - { - //stop when we run out of frames - if(currFrameCounter == (int)currMovieData.records.size()) - { - StopPlayback(); - } - else - { - MovieRecord* mr = &currMovieData.records[currFrameCounter]; - - //reset if necessary - if(mr->command_reset()) - {} - //ResetNES(); + MovieRecord* mr = &currMovieData.records[currFrameCounter]; + nds.touchX=mr->touch.x << 4; + nds.touchY=mr->touch.y << 4; - NDS_setPadFromMovie(mr->pad); - } + if(mr->touch.touch) { + nds.isTouch=mr->touch.touch; + MMU.ARM7_REG[0x136] &= 0xBF; + } + else { + nds.touchX=0; + nds.touchY=0; + nds.isTouch=0; - //if we are on the last frame, then pause the emulator if the player requested it - if(currFrameCounter == (int)currMovieData.records.size()-1) - { - /*if(FCEUD_PauseAfterPlayback()) - { - FCEUI_ToggleEmulationPause(); - }*/ - } + MMU.ARM7_REG[0x136] |= 0x40; + } + osd->addFixed(mr->touch.x, mr->touch.y, "%s", "X"); + } + } - //pause the movie at a specified frame - //if(FCEUMOV_ShouldPause() && FCEUI_EmulationPaused()==0) - //{ - // FCEUI_ToggleEmulationPause(); - // FCEU_DispMessage("Paused at specified movie frame"); - //} - - } - else if(movieMode == MOVIEMODE_RECORD) - { - MovieRecord mr; + //the main interaction point between the emulator and the movie system. + //either dumps the current joystick state or loads one state from the movie + void FCEUMOV_AddInputState() + { + //todo - for tasedit, either dump or load depending on whether input recording is enabled + //or something like that + //(input recording is just like standard read+write movie recording with input taken from gamepad) + //otherwise, it will come from the tasedit data. - mr.commands = 0; - mr.pad = nds.pad; - if(nds.isTouch) { - mr.touch.x = nds.touchX; - mr.touch.y = nds.touchY; - mr.touch.touch = 1; - } else { - mr.touch.x = 0; - mr.touch.y = 0; - mr.touch.touch = 0; - } + if(movieMode == MOVIEMODE_PLAY) + { + //stop when we run out of frames + if(currFrameCounter == (int)currMovieData.records.size()) + { + StopPlayback(); + } + else + { + MovieRecord* mr = &currMovieData.records[currFrameCounter]; - mr.dump(&currMovieData, osRecordingMovie,currMovieData.records.size()); - currMovieData.records.push_back(mr); - } + //reset if necessary + if(mr->command_reset()) + {} + //ResetNES(); - currFrameCounter++; + NDS_setPadFromMovie(mr->pad); + NDS_setTouchFromMovie(); + } - /*extern uint8 joy[4]; - memcpy(&cur_input_display,joy,4);*/ -} + //if we are on the last frame, then pause the emulator if the player requested it + if(currFrameCounter == (int)currMovieData.records.size()-1) + { + /*if(FCEUD_PauseAfterPlayback()) + { + FCEUI_ToggleEmulationPause(); + }*/ + } + + //pause the movie at a specified frame + //if(FCEUMOV_ShouldPause() && FCEUI_EmulationPaused()==0) + //{ + // FCEUI_ToggleEmulationPause(); + // FCEU_DispMessage("Paused at specified movie frame"); + //} + osd->addFixed(180, 176, "%s", "Playback"); + + } + else if(movieMode == MOVIEMODE_RECORD) + { + MovieRecord mr; + + mr.commands = 0; + mr.pad = nds.pad; + if(nds.isTouch) { + mr.touch.x = nds.touchX >> 4; + mr.touch.y = nds.touchY >> 4; + mr.touch.touch = 1; + } else { + mr.touch.x = 0; + mr.touch.y = 0; + mr.touch.touch = 0; + } + + mr.dump(&currMovieData, osRecordingMovie,currMovieData.records.size()); + currMovieData.records.push_back(mr); + osd->addFixed(180, 176, "%s", "Recording"); + } + + currFrameCounter++; + + /*extern uint8 joy[4]; + memcpy(&cur_input_display,joy,4);*/ + } //TODO @@ -570,38 +610,60 @@ static void FCEUMOV_AddCommand(int cmd) } -static int FCEUMOV_WriteState(std::ostream* os) +void mov_savestate(std::ostream* os) { //we are supposed to dump the movie data into the savestate + //if(movieMode == MOVIEMODE_RECORD || movieMode == MOVIEMODE_PLAY) + // return currMovieData.dump(os, true); + //else return 0; if(movieMode == MOVIEMODE_RECORD || movieMode == MOVIEMODE_PLAY) - return currMovieData.dump(os, true); - else return 0; + { + write32le('MOVI',os); + currMovieData.dump(os, true); + } + else + { + write32le('NOMO',os); + } } -//TODO EVERYTHING BELOW static bool load_successful; -static bool FCEUMOV_ReadState(std::istream* is, uint32 size) +bool mov_loadstate(std::istream* is, int size) { load_successful = false; + int cookie; + if(read32le(&cookie,is) != 1) return false; + if(cookie == 'NOMO') + return true; + else if(cookie != 'MOVI') + return false; + + size -= 4; + + if (!movie_readonly && autoMovieBackup && freshMovie) //If auto-backup is on, movie has not been altered this session and the movie is in read+write mode + { + FCEUI_MakeBackupMovie(false); //Backup the movie before the contents get altered, but do not display messages + } + //a little rule: cant load states in read+write mode with a movie from an archive. //so we are going to switch it to readonly mode in that case - if(!movie_readonly - //*&& FCEU_isFileInArchive(curMovieFilename)*/ - ) { - FCEU_PrintError("Cannot loadstate in Read+Write with movie from archive. Movie is now Read-Only."); - movie_readonly = true; - } +// if(!movie_readonly +// //*&& FCEU_isFileInArchive(curMovieFilename)*/ +// ) { +// FCEU_PrintError("Cannot loadstate in Read+Write with movie from archive. Movie is now Read-Only."); +// movie_readonly = true; +// } MovieData tempMovieData = MovieData(); std::ios::pos_type curr = is->tellg(); if(!LoadFM2(tempMovieData, is, size, false)) { - /*is->seekg((uint32)curr+size); - extern bool FCEU_state_loading_old_format; + // is->seekg((uint32)curr+size); + /* extern bool FCEU_state_loading_old_format; if(FCEU_state_loading_old_format) { if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_RECORD) { FCEUI_StopMovie(); @@ -652,12 +714,29 @@ static bool FCEUMOV_ReadState(std::istream* is, uint32 size) if(movie_readonly) { + //------------------------------------------------------------- + //this code would reload the movie from disk. allegedly it is helpful to hexers, but + //it is way too slow with dsm format. so it is only here as a reminder, and in case someone + //wants to play with it + //------------------------------------------------------------- + //{ + // fstream fs (curMovieFilename); + // if(!LoadFM2(tempMovieData, &fs, INT_MAX, false)) + // { + // FCEU_PrintError("Failed to reload DSM after loading savestate"); + // } + // fs.close(); + // currMovieData = tempMovieData; + //} + //------------------------------------------------------------- + //if the frame counter is longer than our current movie, then error if(currFrameCounter > (int)currMovieData.records.size()) { FCEU_PrintError("Savestate is from a frame (%d) after the final frame in the movie (%d). This is not permitted.", currFrameCounter, currMovieData.records.size()-1); return false; } + movieMode = MOVIEMODE_PLAY; } else @@ -666,10 +745,10 @@ static bool FCEUMOV_ReadState(std::istream* is, uint32 size) tempMovieData.truncateAt(currFrameCounter); currMovieData = tempMovieData; - #ifdef _S9XLUA_H - if(!FCEU_LuaRerecordCountSkip()) + // #ifdef _S9XLUA_H + // if(!FCEU_LuaRerecordCountSkip()) currRerecordCount++; - #endif + // #endif currMovieData.rerecordCount = currRerecordCount; @@ -680,6 +759,7 @@ static bool FCEUMOV_ReadState(std::istream* is, uint32 size) } load_successful = true; + freshMovie = false; //// Maximus: Show the last input combination entered from the //// movie within the state @@ -722,3 +802,137 @@ bool FCEUI_MovieGetInfo(std::istream* fp, MOVIE_INFO& info, bool skipFrameCount) return true; } + +bool MovieRecord::parseBinary(MovieData* md, std::istream* is) +{ + commands=is->get(); + is->read((char *) &pad, sizeof pad); + is->read((char *) &touch.x, sizeof touch.x); + is->read((char *) &touch.y, sizeof touch.y); + is->read((char *) &touch.touch, sizeof touch.touch); + return true; +} + + +void MovieRecord::dumpBinary(MovieData* md, std::ostream* os, int index) +{ + os->put(md->records[index].commands); + os->write((char *) &md->records[index].pad, sizeof md->records[index].pad); + os->write((char *) &md->records[index].touch.x, sizeof md->records[index].touch.x); + os->write((char *) &md->records[index].touch.y, sizeof md->records[index].touch.y); + os->write((char *) &md->records[index].touch.touch, sizeof md->records[index].touch.touch); +} + +void LoadFM2_binarychunk(MovieData& movieData, std::istream* fp, int size) +{ + int recordsize = 1; //1 for the command + + recordsize = 6; + + assert(size%6==0); + + //find out how much remains in the file + int curr = fp->tellg(); + fp->seekg(0,std::ios::end); + int end = fp->tellg(); + int flen = end-curr; + fp->seekg(curr,std::ios::beg); + + //the amount todo is the min of the limiting size we received and the remaining contents of the file + int todo = std::min(size, flen); + + int numRecords = todo/recordsize; + movieData.records.resize(numRecords); + for(int i=0;i + +bool CheckFileExists(const char* filename) +{ + //This function simply checks to see if the given filename exists + string checkFilename; + + if (filename) + checkFilename = filename; + + //Check if this filename exists + fstream test; + test.open(checkFilename.c_str(),fstream::in); + + if (test.fail()) + { + test.close(); + return false; + } + else + { + test.close(); + return true; + } +} + +void FCEUI_MakeBackupMovie(bool dispMessage) +{ + //This function generates backup movie files + string currentFn; //Current movie fillename + string backupFn; //Target backup filename + string tempFn; //temp used in back filename creation + stringstream stream; + int x; //Temp variable for string manip + bool exist = false; //Used to test if filename exists + bool overflow = false; //Used for special situation when backup numbering exceeds limit + + currentFn = curMovieFilename; //Get current moviefilename + backupFn = curMovieFilename; //Make backup filename the same as current moviefilename + x = backupFn.find_last_of("."); //Find file extension + backupFn = backupFn.substr(0,x); //Remove extension + tempFn = backupFn; //Store the filename at this point + for (unsigned int backNum=0;backNum<999;backNum++) //999 = arbituary limit to backup files + { + stream.str(""); //Clear stream + if (backNum > 99) + stream << "-" << backNum; //assign backNum to stream + else if (backNum <=99 && backNum >= 10) + stream << "-0"; //Make it 010, etc if two digits + else + stream << "-00" << backNum; //Make it 001, etc if single digit + backupFn.append(stream.str()); //add number to bak filename + backupFn.append(".bak"); //add extension + + exist = CheckFileExists(backupFn.c_str()); //Check if file exists + + if (!exist) + break; //Yeah yeah, I should use a do loop or something + else + { + backupFn = tempFn; //Before we loop again, reset the filename + + if (backNum == 999) //If 999 exists, we have overflowed, let's handle that + { + backupFn.append("-001.bak"); //We are going to simply overwrite 001.bak + overflow = true; //Flag that we have exceeded limit + break; //Just in case + } + } + } + + MovieData md = currMovieData; //Get current movie data + std::fstream* outf = new fstream(backupFn.c_str(),std::ios_base::out); //FCEUD_UTF8_fstream(backupFn, "wb"); //open/create file + md.dump(outf,false); //dump movie data + delete outf; //clean up, delete file object + + //TODO, decide if fstream successfully opened the file and print error message if it doesn't + + if (dispMessage) //If we should inform the user + { +// if (overflow) +// FCEUI_DispMessage("Backup overflow, overwriting %s",backupFn.c_str()); //Inform user of overflow +// else +// FCEUI_DispMessage("%s created",backupFn.c_str()); //Inform user of backup filename + } +} + diff --git a/desmume/src/movie.h b/desmume/src/movie.h index 3935971eb..0f637c031 100644 --- a/desmume/src/movie.h +++ b/desmume/src/movie.h @@ -21,6 +21,7 @@ typedef struct std::string name_of_rom_used; std::vector comments; + std::vector subtitles; } MOVIE_INFO; enum EMOVIEMODE @@ -88,12 +89,12 @@ public: void clear(); //a waste of memory in lots of cases.. maybe make it a pointer later? - std::vector savestate; + //std::vector savestate; void parse(MovieData* md, std::istream* is); - //bool parseBinary(MovieData* md, std::istream* is); + bool parseBinary(MovieData* md, std::istream* is); void dump(MovieData* md, std::ostream* os, int index); - //void dumpBinary(MovieData* md, std::ostream* os, int index); + void dumpBinary(MovieData* md, std::ostream* os, int index); void parsePad(std::istream* is, u16& pad); void dumpPad(std::ostream* os, u16 pad); @@ -178,6 +179,19 @@ private: } }; -bool FCEUI_MovieGetInfo(std::istream* fp, MOVIE_INFO& info, bool skipFrameCount); +extern int currFrameCounter; +extern EMOVIEMODE movieMode; //adelikat: main needs this for frame counter display +extern MovieData currMovieData; //adelikat: main needs this for frame counter display +bool FCEUI_MovieGetInfo(std::istream* fp, MOVIE_INFO& info, bool skipFrameCount); +void FCEUI_SaveMovie(const char *fname, std::wstring author); +void FCEUI_LoadMovie(const char *fname, bool _read_only, bool tasedit, int _pauseframe); +void FCEUI_StopMovie(); +void FCEUMOV_AddInputState(); +void NDS_setTouchFromMovie(void); +void mov_savestate(std::ostream* os); +bool mov_loadstate(std::istream* is, int size); +void LoadFM2_binarychunk(MovieData& movieData, std::istream* fp, int size); +extern bool movie_readonly; +void FCEUI_MakeBackupMovie(bool dispMessage); #endif diff --git a/desmume/src/rasterize.cpp b/desmume/src/rasterize.cpp index 1401e7ec8..fcf03693c 100644 --- a/desmume/src/rasterize.cpp +++ b/desmume/src/rasterize.cpp @@ -930,7 +930,6 @@ static char SoftRastInit(void) } static void SoftRastReset() { - validFramebuffer = false; } static void SoftRastClose() @@ -968,15 +967,6 @@ static void SoftRastConvertFramebuffer() dstAlpha[i] = alpha_5bit_to_4bit[a]; } } - validFramebuffer = true; -} - -static void SoftRastCheckFresh() -{ - if(!validFramebuffer) - { - SoftRastConvertFramebuffer(); - } } static struct TClippedPoly @@ -1295,6 +1285,7 @@ static void SoftRastRender() validFramebuffer = false; // printf("rendered %d of %d polys after backface culling\n",gfx3d.polylist->count-culled,gfx3d.polylist->count); + SoftRastConvertFramebuffer(); } GPU3DInterface gpu3DRasterize = { @@ -1304,5 +1295,4 @@ GPU3DInterface gpu3DRasterize = { SoftRastClose, SoftRastRender, SoftRastVramReconfigureSignal, - SoftRastCheckFresh, }; diff --git a/desmume/src/render3D.cpp b/desmume/src/render3D.cpp index 9c2bb7a17..03650a2f0 100644 --- a/desmume/src/render3D.cpp +++ b/desmume/src/render3D.cpp @@ -32,7 +32,6 @@ GPU3DInterface gpu3DNull = { NDS_nullFunc1, //NDS_3D_Close NDS_nullFunc1, //NDS_3D_Render NDS_nullFunc1, //NDS_3D_VramReconfigureSignal - NDS_nullFunc1, //NDS_3D_CheckFresh }; GPU3DInterface *gpu3D = &gpu3DNull; diff --git a/desmume/src/render3D.h b/desmume/src/render3D.h index a6d32fbdc..877afa4d8 100644 --- a/desmume/src/render3D.h +++ b/desmume/src/render3D.h @@ -46,9 +46,6 @@ typedef struct Render3DInterface //called when the emulator reconfigures its vram. you may need to invalidate your texture cache. void (CALL_CONVENTION* NDS_3D_VramReconfigureSignal) (); - //ensures that the plugin's framebuffer generation is fresh - void (CALL_CONVENTION* NDS_3D_CheckFresh) (); - } GPU3DInterface; extern int cur3DCore; diff --git a/desmume/src/rtc.cpp b/desmume/src/rtc.cpp index 39e95f480..ddac69bcb 100644 --- a/desmume/src/rtc.cpp +++ b/desmume/src/rtc.cpp @@ -33,6 +33,7 @@ #ifdef WIN32 #include "windows/main.h" #endif +#include "movie.h" typedef struct { @@ -81,24 +82,26 @@ struct movietime movie; int oldframeCounter; u64 totalcycles=2904024960000ULL;//noon int totalseconds; -bool init=false; bool moviemode=false; -#ifdef WIN32 -static void MovieTime(void) { - if(!init) { + +void InitMovieTime(void) { + movie.year=9; movie.month=1; movie.monthday=1; movie.weekday=4; oldframeCounter=0; - init=true; - } - if(frameCounter > oldframeCounter) { +} + +#ifdef WIN32 +static void MovieTime(void) { + + if(currFrameCounter > oldframeCounter) { int difference; - difference=frameCounter-oldframeCounter; - oldframeCounter=frameCounter; + difference=currFrameCounter-oldframeCounter; + oldframeCounter=currFrameCounter; totalcycles+=((560190<<1)*difference); } @@ -143,7 +146,7 @@ static void rtcRecv() tm_local->tm_year %= 100; tm_local->tm_mon++; - if(moviemode) { + if(movieMode != MOVIEMODE_INACTIVE) { MovieTime(); @@ -177,7 +180,7 @@ static void rtcRecv() time(&tm); struct tm *tm_local= localtime(&tm); - if(moviemode) { + if(movieMode != MOVIEMODE_INACTIVE) { MovieTime(); diff --git a/desmume/src/rtc.h b/desmume/src/rtc.h index 193c7f1a1..f1b6765ae 100644 --- a/desmume/src/rtc.h +++ b/desmume/src/rtc.h @@ -30,4 +30,6 @@ extern void rtcInit(); extern u16 rtcRead(); extern void rtcWrite(u16 val); + +void InitMovieTime(void); #endif diff --git a/desmume/src/saves.cpp b/desmume/src/saves.cpp index 5bd5da886..e20040e7a 100644 --- a/desmume/src/saves.cpp +++ b/desmume/src/saves.cpp @@ -36,6 +36,8 @@ #include "memorystream.h" #include "readwrite.h" #include "gfx3d.h" +#include "movie.h" +#include "windows/main.h" //void*v is actually a void** which will be indirected before reading @@ -129,8 +131,8 @@ SFORMAT SF_ARM9[]={ }; SFORMAT SF_MEM[]={ - { "ITCM", 1, 0x8000, ARM9Mem.ARM9_ITCM}, - { "DTCM", 1, 0x4000, ARM9Mem.ARM9_DTCM}, + { "ITCM", 1, sizeof(ARM9Mem.ARM9_ITCM), ARM9Mem.ARM9_ITCM}, + { "DTCM", 1, sizeof(ARM9Mem.ARM9_DTCM), ARM9Mem.ARM9_DTCM}, //for legacy purposes, WRAX is a separate variable. shouldnt be a problem. { "WRAM", 1, 0x400000, ARM9Mem.MAIN_MEM}, @@ -141,9 +143,9 @@ SFORMAT SF_MEM[]={ //but there are actually no more registers than this { "9REG", 1, 0x2000, ARM9Mem.ARM9_REG}, - { "VMEM", 1, 0x800, ARM9Mem.ARM9_VMEM}, - { "OAMS", 1, 0x800, ARM9Mem.ARM9_OAM}, - { "LCDM", 1, 0xA4000, ARM9Mem.ARM9_LCD}, + { "VMEM", 1, sizeof(ARM9Mem.ARM9_VMEM), ARM9Mem.ARM9_VMEM}, + { "OAMS", 1, sizeof(ARM9Mem.ARM9_OAM), ARM9Mem.ARM9_OAM}, + { "LCDM", 1, sizeof(ARM9Mem.ARM9_LCD), ARM9Mem.ARM9_LCD}, { 0 } }; @@ -169,15 +171,12 @@ extern u32 DMASrc[2][4]; extern u32 DMADst[2][4]; SFORMAT SF_MMU[]={ - { "M7BI", 1, 0x4000, MMU.ARM7_BIOS}, - { "M7ER", 1, 0x10000, MMU.ARM7_ERAM}, - { "M7RG", 1, 0x10000, MMU.ARM7_REG}, - { "M7WI", 1, 0x10000, MMU.ARM7_WIRAM}, - { "MVRM", 1, 4, MMU.VRAM_MAP}, - { "MVRM", 4, 9, MMU.LCD_VRAM_ADDR}, - { "MVRM", 1, 9, MMU.LCDCenable}, - { "MSWI", 1, 0x8000, MMU.SWIRAM}, - { "MCRA", 1, 0x10000, MMU.CART_RAM}, + { "M7BI", 1, sizeof(MMU.ARM7_BIOS), MMU.ARM7_BIOS}, + { "M7ER", 1, sizeof(MMU.ARM7_ERAM), MMU.ARM7_ERAM}, + { "M7RG", 1, sizeof(MMU.ARM7_REG), MMU.ARM7_REG}, + { "M7WI", 1, sizeof(MMU.ARM7_WIRAM), MMU.ARM7_WIRAM}, + { "MSWI", 1, sizeof(MMU.SWIRAM), MMU.SWIRAM}, + { "MCRA", 1, sizeof(MMU.CART_RAM), MMU.CART_RAM}, { "M9RW", 1, 1, &MMU.ARM9_RW_MODE}, { "MDTC", 4, 1, &MMU.DTCMRegion}, { "MITC", 4, 1, &MMU.ITCMRegion}, @@ -229,37 +228,46 @@ SFORMAT SF_MMU[]={ //fifos { "F0TL", 1, 1, &ipc_fifo[0].tail}, - { "F0BF", 4, 16, &ipc_fifo[0].buf[0]}, + { "F0BF", 4, 16, ipc_fifo[0].buf}, { "F1TL", 1, 1, &ipc_fifo[1].tail}, - { "F1BF", 4, 16, &ipc_fifo[1].buf[0]}, + { "F1BF", 4, 16, ipc_fifo[1].buf}, { "FDHD", 4, 1, &disp_fifo.head}, { "FDTL", 4, 1, &disp_fifo.tail}, - { "FDBF", 4, 0x6000, &disp_fifo.buf[0]}, + { "FDBF", 4, 0x6000, disp_fifo.buf}, { 0 } }; +SFORMAT SF_MOVIE[]={ + { "FRAC", 4, 1, &currFrameCounter}, + { "LAGC", 4, 1, &TotalLagFrames}, + { 0 } +}; static void mmu_savestate(std::ostream* os) { //version write32le(0,os); + write32le(MMU.bupmem.type,os); write32le(MMU.bupmem.size,os); os->write((char*)MMU.bupmem.data,MMU.bupmem.size); } -static bool mmu_loadstate(std::istream* is) +static bool mmu_loadstate(std::istream* is, int size) { //read version int version; if(read32le(&version,is) != 1) return false; if(version != 0) return false; + int bupmem_type; u32 bupmem_size; + if(read32le(&bupmem_type,is) != 1) return false; if(read32le(&bupmem_size,is) != 1) return false; - if(bupmem_size != MMU.bupmem.size) return false; //mismatch between current initialized and saved size + + mc_realloc(&MMU.bupmem,bupmem_type,bupmem_size); is->read((char*)MMU.bupmem.data,bupmem_size); if(is->fail()) return false; @@ -364,7 +372,7 @@ static bool cp15_loadone(armcp15_t *cp15, std::istream* is) return true; } -static bool cp15_loadstate(std::istream* is) +static bool cp15_loadstate(std::istream* is, int size) { //read version int version; @@ -633,6 +641,9 @@ static int savestate_WriteChunk(std::ostream* os, int type, SFORMAT *sf) return (bsize+8); } +//TODO TODO TODO TODO TODO TODO TODO +// - this is retarded. why not write placeholders for size and then write directly to the stream +//and then go back and fill them in static void savestate_WriteChunk(std::ostream* os, int type, void (*saveproc)(std::ostream* os)) { //get the size @@ -657,6 +668,16 @@ static bool savestate_save(std::ostream* outstream, int compressionLevel) writechunks(os); ms.flush(); + for(int i=0x2000;i<0x1000000;i++) + if(ARM9Mem.ARM9_REG[i] != 0) { + MessageBox(0,"Debug check fail: ARM9Mem.ARM9_REG",0,0); + } + + for(int i=0;i<0x20000;i++) + if(ARM9Mem.blank_memory[i] != 0) { + MessageBox(0,"Debug check fail: ARM9Mem.blank_memory",0,0); + } + //save the length of the file u32 len = ms.size(); @@ -730,6 +751,8 @@ static void writechunks(std::ostream* os) { savestate_WriteChunk(os,8,spu_savestate); savestate_WriteChunk(os,90,SF_GFX3D); savestate_WriteChunk(os,91,gfx3d_savestate); + savestate_WriteChunk(os,100,SF_MOVIE); + savestate_WriteChunk(os,101,mov_savestate); savestate_WriteChunk(os,0xFFFFFFFF,(SFORMAT*)0); } @@ -747,15 +770,17 @@ static bool ReadStateChunks(std::istream* is, s32 totalsize) { case 1: if(!ReadStateChunk(is,SF_ARM9,size)) ret=false; break; case 2: if(!ReadStateChunk(is,SF_ARM7,size)) ret=false; break; - case 3: if(!cp15_loadstate(is)) ret=false; break; + case 3: if(!cp15_loadstate(is,size)) ret=false; break; case 4: if(!ReadStateChunk(is,SF_MEM,size)) ret=false; break; case 5: if(!ReadStateChunk(is,SF_NDS,size)) ret=false; break; case 60: if(!ReadStateChunk(is,SF_MMU,size)) ret=false; break; - case 61: if(!mmu_loadstate(is)) ret=false; break; - case 7: if(!gpu_loadstate(is)) ret=false; break; - case 8: if(!spu_loadstate(is)) ret=false; break; + case 61: if(!mmu_loadstate(is,size)) ret=false; break; + case 7: if(!gpu_loadstate(is,size)) ret=false; break; + case 8: if(!spu_loadstate(is,size)) ret=false; break; case 90: if(!ReadStateChunk(is,SF_GFX3D,size)) ret=false; break; - case 91: if(!gfx3d_loadstate(is)) ret=false; break; + case 91: if(!gfx3d_loadstate(is,size)) ret=false; break; + case 100: if(!ReadStateChunk(is,SF_MOVIE, size)) ret=false; break; + case 101: if(!mov_loadstate(is, size)) ret=false; break; default: ret=false; break; @@ -842,13 +867,22 @@ static bool savestate_load(std::istream* is) memorystream mstemp(&buf); bool x = ReadStateChunks(&mstemp,(s32)len); + if(!x) + { + printf("Error loading savestate. It failed halfway through;\nSince there is no savestate backup system, your current game session is wrecked"); +#ifdef _MSC_VER + MessageBox(0,"Error loading savestate. It failed halfway through;\nSince there is no savestate backup system, your current game session is wrecked",0,0); +#endif + return false; + } + loadstate(); if((nds.debugConsole!=0) != CommonSettings.DebugConsole) { printf("WARNING: forcing console debug mode to: debugmode=%s\n",nds.debugConsole?"TRUE":"FALSE"); } - return x; + return true; } bool savestate_load(const char *file_name) diff --git a/desmume/src/wifi.cpp b/desmume/src/wifi.cpp index 10278cfa7..de672db4a 100644 --- a/desmume/src/wifi.cpp +++ b/desmume/src/wifi.cpp @@ -931,7 +931,8 @@ u16 WIFI_read16(wifimac_t *wifi,u32 address) //printf("wifi: read reg 0x0214\n"); return 0x0009; case 0x19C: - return 0; //luigi, please pick something to return from here + //assert(false); //luigi, please pick something to return from here + return 0; default: // printf("wifi: read unhandled reg %03X\n", address); return wifi->ioMem[address >> 1]; diff --git a/desmume/src/windows/DeSmuME_2005.vcproj b/desmume/src/windows/DeSmuME_2005.vcproj index f8e22d599..574f786ce 100644 --- a/desmume/src/windows/DeSmuME_2005.vcproj +++ b/desmume/src/windows/DeSmuME_2005.vcproj @@ -393,6 +393,22 @@ RelativePath=".\palView.h" > + + + + + + + + diff --git a/desmume/src/windows/DeSmuME_2008.vcproj b/desmume/src/windows/DeSmuME_2008.vcproj index 69577db3c..872b4c758 100644 --- a/desmume/src/windows/DeSmuME_2008.vcproj +++ b/desmume/src/windows/DeSmuME_2008.vcproj @@ -279,6 +279,10 @@ RelativePath="..\MMU.cpp" > + + @@ -636,6 +640,18 @@ RelativePath=".\palView.cpp" > + + + + + + @@ -746,6 +762,18 @@ RelativePath=".\palView.h" > + + + + + + @@ -882,6 +910,10 @@ RelativePath="..\MMU.h" > + + @@ -957,6 +989,10 @@ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" > + + @@ -968,5 +1004,9 @@ + diff --git a/desmume/src/windows/aviout.cpp b/desmume/src/windows/aviout.cpp index fe396d8dd..c663822d8 100644 --- a/desmume/src/windows/aviout.cpp +++ b/desmume/src/windows/aviout.cpp @@ -414,11 +414,15 @@ void DRV_AviVideoUpdate(const u16* buffer) AviNextSegment(); } +BOOL AVI_IsRecording() +{ + return avi_file && avi_file->valid; +} void DRV_AviSoundUpdate(void* soundData, int soundLen) { int nBytes; - if(!avi_file || !avi_file->valid || !avi_file->sound_added) + if(!AVI_IsRecording() || !avi_file->sound_added) return; nBytes = soundLen * avi_file->wave_format.nBlockAlign; diff --git a/desmume/src/windows/hotkey.cpp b/desmume/src/windows/hotkey.cpp index 4d823deb6..9bbf56e38 100644 --- a/desmume/src/windows/hotkey.cpp +++ b/desmume/src/windows/hotkey.cpp @@ -27,6 +27,7 @@ #include "render3d.h" #include "throttle.h" #include "../mic.h" +#include "../movie.h" extern LRESULT OpenFile(); //adelikat: Made this an extern here instead of main.h Seemed icky not to limit the scope of this function @@ -154,9 +155,7 @@ void HK_MicrophoneKeyUp(int) {MicButtonPressed =0;} void HK_AutoHoldClearKeyDown(int) { - for (int i=0; i < 10; i++) { - AutoHold.hold(i)=false; - } + ClearAutoHold(); } void HK_Reset(int) {ResetGame();} @@ -168,6 +167,13 @@ void HK_ToggleFrame(int) {frameCounterDisplay ^= true;} void HK_ToggleFPS(int) {FpsDisplay ^= true;} void HK_ToggleInput(int) {ShowInputDisplay ^= true;} void HK_ToggleLag(int) {ShowLagFrameCounter ^= true;} +void HK_ToggleReadOnly(int) { + movie_readonly ^= true; + if(movie_readonly) + SetMessageToDisplay("Read Only"); + else + SetMessageToDisplay("Read+Write"); +} void HK_AutoHoldKeyDown(int) {AutoHoldPressed = true;} void HK_AutoHoldKeyUp(int) {AutoHoldPressed = false;} @@ -178,6 +184,12 @@ void HK_TurboRightKeyUp(int) { Turbo.Right = false; } void HK_TurboLeftKeyDown(int) { Turbo.Left = true; } void HK_TurboLeftKeyUp(int) { Turbo.Left = false; } +void HK_TurboRKeyDown(int) { Turbo.R = true; } +void HK_TurboRKeyUp(int) { Turbo.R = false; } + +void HK_TurboLKeyDown(int) { Turbo.L = true; } +void HK_TurboLKeyUp(int) { Turbo.L = false; } + void HK_TurboDownKeyDown(int) { Turbo.Down = true; } void HK_TurboDownKeyUp(int) { Turbo.Down = false; } @@ -208,7 +220,9 @@ void HK_FastForwardKeyDown(int) { FastForward = 1; } void HK_FastForwardKeyUp(int) { FastForward = 0; } void HK_IncreaseSpeed(int) { IncreaseSpeed(); } void HK_DecreaseSpeed(int) { DecreaseSpeed(); } -void HK_FrameAdvance(int) { FrameAdvance(); } +void HK_FrameAdvanceKeyDown(int) { FrameAdvance(true); } +void HK_FrameAdvanceKeyUp(int) { FrameAdvance(false); } + void HK_ToggleRasterizer(int) { if(cur3DCore == GPU3D_OPENGL) cur3DCore = GPU3D_SWRAST; @@ -261,7 +275,8 @@ void InitCustomKeys (SCustomKeys *keys) keys->Pause.page = HOTKEY_PAGE_MAIN; keys->Pause.key = VK_SPACE; - keys->FrameAdvance.handleKeyDown = HK_FrameAdvance; + keys->FrameAdvance.handleKeyDown = HK_FrameAdvanceKeyDown; + keys->FrameAdvance.handleKeyUp = HK_FrameAdvanceKeyUp; keys->FrameAdvance.code = "FrameAdvance"; keys->FrameAdvance.name = L"Frame Advance"; keys->FrameAdvance.page = HOTKEY_PAGE_MAIN; @@ -348,6 +363,12 @@ void InitCustomKeys (SCustomKeys *keys) keys->ToggleLag.page = HOTKEY_PAGE_MAIN; keys->ToggleLag.key = NULL; + keys->ToggleReadOnly.handleKeyDown = HK_ToggleReadOnly; + keys->ToggleReadOnly.code = "ToggleReadOnly"; + keys->ToggleReadOnly.name = L"Toggle Read Only"; + keys->ToggleReadOnly.page = HOTKEY_PAGE_MAIN; + keys->ToggleReadOnly.key = NULL; + keys->RecordAVI.handleKeyDown = HK_RecordAVI; keys->RecordAVI.code = "RecordAVI"; keys->RecordAVI.name = L"Record AVI"; @@ -375,6 +396,20 @@ void InitCustomKeys (SCustomKeys *keys) keys->TurboLeft.page = HOTKEY_PAGE_TURBO; keys->TurboLeft.key = NULL; + keys->TurboR.handleKeyDown = HK_TurboRKeyDown; + keys->TurboR.handleKeyUp = HK_TurboRKeyUp; + keys->TurboR.code = "TurboR"; + keys->TurboR.name = L"Turbo R"; + keys->TurboR.page = HOTKEY_PAGE_TURBO; + keys->TurboR.key = NULL; + + keys->TurboL.handleKeyDown = HK_TurboLKeyDown; + keys->TurboL.handleKeyUp = HK_TurboLKeyUp; + keys->TurboL.code = "TurboL"; + keys->TurboL.name = L"Turbo L"; + keys->TurboL.page = HOTKEY_PAGE_TURBO; + keys->TurboL.key = NULL; + keys->TurboDown.handleKeyDown = HK_TurboDownKeyDown; keys->TurboDown.handleKeyUp = HK_TurboDownKeyUp; keys->TurboDown.code = "TurboDown"; diff --git a/desmume/src/windows/hotkey.h b/desmume/src/windows/hotkey.h index 397cef441..c40625d7f 100644 --- a/desmume/src/windows/hotkey.h +++ b/desmume/src/windows/hotkey.h @@ -67,7 +67,7 @@ struct SCustomKeys SCustomKey OpenROM, Reset, Pause, FrameAdvance, FastForward, FastForwardToggle, IncreaseSpeed, DecreaseSpeed, Microphone; - SCustomKey TurboRight, TurboLeft, TurboDown, TurboUp, TurboSelect, TurboStart, TurboB, TurboA, TurboY, TurboX; + SCustomKey TurboRight, TurboLeft, TurboDown, TurboUp, TurboSelect, TurboStart, TurboB, TurboA, TurboY, TurboX, TurboR, TurboL; SCustomKey AutoHold, AutoHoldClear; @@ -80,6 +80,7 @@ struct SCustomKeys SCustomKey ToggleFPS; SCustomKey ToggleInput; SCustomKey ToggleLag; + SCustomKey ToggleReadOnly; SCustomKey LastItem; // dummy, must be last //--methods-- diff --git a/desmume/src/windows/main.cpp b/desmume/src/windows/main.cpp index e79d9e7e3..455b7abf9 100644 --- a/desmume/src/windows/main.cpp +++ b/desmume/src/windows/main.cpp @@ -69,13 +69,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "throttle.h" #include "gbaslot_config.h" #include "cheatsWin.h" - +#include "Mmsystem.h" #include "../mic.h" #include "../common.h" #include "main.h" #include "hotkey.h" - +#include "../movie.h" +#include "../replay.h" #include "snddx.h" +#include "ramwatch.h" +#include "ram_search.h" + #include "directx/ddraw.h" @@ -91,8 +95,8 @@ using namespace std; //----Recent ROMs menu globals---------- vector RecentRoms; //The list of recent ROM filenames const unsigned int MAX_RECENT_ROMS = 10; //To change the recent rom max, simply change this number -const unsigned int clearid = 600; // ID for the Clear recent ROMs item -const unsigned int baseid = 601; //Base identifier for the recent ROMs items +const unsigned int clearid = IDM_RECENT_RESERVED0; // ID for the Clear recent ROMs item +const unsigned int baseid = IDM_RECENT_RESERVED1; //Base identifier for the recent ROMs items static HMENU recentromsmenu; //Handle to the recent ROMs submenu //-------------------------------------- @@ -107,6 +111,10 @@ void DRV_AviSoundUpdate(void* soundData, int soundLen); bool DRV_AviIsRecording(); void DRV_AviVideoUpdate(const u16* buffer); //------ +DWORD hKeyInputTimer; + +extern LRESULT CALLBACK RamSearchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + CRITICAL_SECTION win_sync; volatile int win_sound_samplecounter = 0; @@ -157,6 +165,7 @@ int WndX = 0; int WndY = 0; int ScreenGap = 0; +extern HWND RamSearchHWnd; static int FrameLimit = 1; @@ -215,9 +224,8 @@ GPU3DInterface *core3DList[] = { int autoframeskipenab=0; int frameskiprate=0; int emu_paused = 0; -static int backupmemorytype=MC_TYPE_AUTODETECT; -static u32 backupmemorysize=1; -unsigned int frameCounter=0; +int backupmemorytype=MC_TYPE_AUTODETECT; +u32 backupmemorysize=1; bool frameAdvance = false; bool frameCounterDisplay = false; bool FpsDisplay = false; @@ -343,10 +351,10 @@ void ResetHud(HudStruct *hudstruct) { SetHudDummy(&hudstruct->Dummy); } -unsigned int lastSaveState = 0; //Keeps track of last savestate used for quick save/load functions -stringstream MessageToDisplay; //temp variable to store message that will be displayed via DisplayMessage function -int displayMessageCounter = 0; //Counter to keep track with how long to display messages on screen - +unsigned int lastSaveState = 0; //Keeps track of last savestate used for quick save/load functions +stringstream MessageToDisplay; //temp variable to store message that will be displayed via DisplayMessage function +int displayMessageCounter = 0; //Counter to keep track with how long to display messages on screen +bool NewMessageToDisplay = false; //Flag to indicate a new message is in queue /* the firmware settings */ struct NDS_fw_config_data win_fw_config; @@ -382,6 +390,79 @@ struct configured_features { const char *cflash_disk_image_file; }; +int KeyInDelayInCount=10; + +static int lastTime = timeGetTime(); +int repeattime; + +VOID CALLBACK KeyInputTimer( UINT idEvent, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) +{ + // +// if(timeGetTime() - lastTime > 5) +// { + bool S9xGetState (WORD KeyIdent); + + /* if(GUI.JoystickHotkeys) + { + static uint32 joyState [256]; + + for(int i = 0 ; i < 255 ; i++) + { + bool active = !S9xGetState(0x8000|i); + + if(active) + { + if(joyState[i] < ULONG_MAX) // 0xffffffffUL + joyState[i]++; + if(joyState[i] == 1 || joyState[i] >= (unsigned) KeyInDelayInCount) + PostMessage(GUI.hWnd, WM_CUSTKEYDOWN, (WPARAM)(0x8000|i),(LPARAM)(NULL)); + } + else + { + if(joyState[i]) + { + joyState[i] = 0; + PostMessage(GUI.hWnd, WM_CUSTKEYUP, (WPARAM)(0x8000|i),(LPARAM)(NULL)); + } + } + } + }*/ + // if((!GUI.InactivePause || !Settings.ForcedPause) + // || (GUI.BackgroundInput || !(Settings.ForcedPause & (PAUSE_INACTIVE_WINDOW | PAUSE_WINDOW_ICONISED)))) + // { + static uint32 joyState [256]; + for(int i = 0 ; i < 255 ; i++) + { + bool active = !S9xGetState(i); + if(active) + { + if(joyState[i] < ULONG_MAX) // 0xffffffffUL + joyState[i]++; + if(joyState[i] == 1 || joyState[i] >= (unsigned) KeyInDelayInCount) { + //sort of fix the auto repeating + //TODO find something better + // repeattime++; + // if(repeattime % 10 == 0) { + + PostMessage(MainWindow->getHWnd(), WM_CUSTKEYDOWN, (WPARAM)(i),(LPARAM)(NULL)); + repeattime=0; + // } + } + } + else + { + if(joyState[i]) + { + joyState[i] = 0; + PostMessage(MainWindow->getHWnd(), WM_CUSTKEYUP, (WPARAM)(i),(LPARAM)(NULL)); + } + } + } + // } + // lastTime = timeGetTime(); +// } +} + static void init_configured_features( struct configured_features *config) { config->arm9_gdb_port = 0; @@ -920,7 +1001,8 @@ void DisplayMessage() { //adelikat - By using stringstream, it leaves open the possibility to keep a series of message in queue displayMessageCounter--; - osd->addFixed(0, 40, "%s",MessageToDisplay.str().c_str()); + osd->addFixed(0, 120, "%s",MessageToDisplay.str().c_str()); + NewMessageToDisplay = false; } } @@ -932,12 +1014,15 @@ void SaveStateMessages(int slotnum, int whichMessage) { case 0: //State saved MessageToDisplay << "State " << slotnum << " saved."; + NewMessageToDisplay = true; //adelikat: TODO: make these call SetMessageToDisplay instead break; case 1: //State loaded MessageToDisplay << "State " << slotnum << " loaded."; + NewMessageToDisplay = true; break; case 2: //Save slot selected MessageToDisplay << "State " << slotnum << " selected."; + NewMessageToDisplay = true; default: break; } @@ -999,12 +1084,13 @@ DWORD WINAPI run() while(execute) { input_process(); + FCEUMOV_AddInputState(); if (ShowInputDisplay) osd->addFixed(Hud.InputDisplay.x, Hud.InputDisplay.y, "%s",InputDisplayString.c_str()); { Lock lock; - cycles = NDS_exec((560190<<1)-cycles); + NDS_exec(0); win_sound_samplecounter = 735; } @@ -1025,7 +1111,17 @@ DWORD WINAPI run() static int fps3d = 0; if (FpsDisplay) osd->addFixed(Hud.FpsDisplay.x, Hud.FpsDisplay.y, "%02d Fps / %02d 3d", fps, fps3d); - osd->update(); + if (frameCounterDisplay) + { + if (movieMode == MOVIEMODE_PLAY) + osd->addFixed(Hud.FrameCounter.x, Hud.FrameCounter.y, "%d/%d",currFrameCounter,currMovieData.records.size()); + else if(movieMode == MOVIEMODE_RECORD) + osd->addFixed(Hud.FrameCounter.x, Hud.FrameCounter.y, "%d",currFrameCounter); + else + osd->addFixed(Hud.FrameCounter.x, Hud.FrameCounter.y, "%d (no movie)",currFrameCounter); + } + + if(!DRV_AviIsRecording()) osd->update(); Display(); osd->clear(); @@ -1047,6 +1143,8 @@ DWORD WINAPI run() } + Update_RAM_Watch(); + Update_RAM_Search(); fpsframecount++; QueryPerformanceCounter((LARGE_INTEGER *)&curticks); @@ -1141,14 +1239,19 @@ DWORD WINAPI run() emu_halt(); SPU_Pause(1); } - frameCounter++; - if (frameCounterDisplay) osd->addFixed(Hud.FrameCounter.x, Hud.FrameCounter.y, "%d",frameCounter); if (ShowLagFrameCounter) osd->addFixed(Hud.LagFrameCounter.x, Hud.LagFrameCounter.y, "%d",TotalLagFrames); if (ShowMicrophone) osd->addFixed(Hud.Microphone.x, Hud.Microphone.y, "%d",MicDisplay); - DisplayMessage(); +// DisplayMessage(); CheckMessages(); } + if (NewMessageToDisplay) + { + DisplayMessage(); + osd->update(); + Display(); + osd->clear(); + } paused = TRUE; CheckMessages(); Sleep(100); @@ -1261,7 +1364,6 @@ BOOL LoadROM(char * filename, const char *cflash_disk_image) { INFO("Loading %s was successful\n",filename); LoadSaveStateInfo(); - frameCounter=0; lagframecounter=0; UpdateRecentRoms(filename); osd->setRotate(GPU_rotation); @@ -1390,6 +1492,7 @@ static void ExitRunLoop() emu_halt(); } +BOOL AVI_IsRecording(); class WinDriver : public Driver { virtual BOOL WIFI_Host_InitSystem() { @@ -1419,6 +1522,11 @@ class WinDriver : public Driver WSACleanup() ; #endif } + + virtual BOOL AVI_IsRecording() + { + return ::AVI_IsRecording(); + } }; int WINAPI WinMain (HINSTANCE hThisInstance, @@ -1657,6 +1765,10 @@ int WINAPI WinMain (HINSTANCE hThisInstance, MainWindow->checkMenu(frameskiprate + IDC_FRAMESKIP0, MF_BYCOMMAND | MF_CHECKED); } + int KeyInRepeatMSec=20; + + hKeyInputTimer = timeSetEvent (KeyInRepeatMSec, 0, KeyInputTimer, 0, TIME_PERIODIC); + cur3DCore = GetPrivateProfileInt("3D", "Renderer", GPU3D_OPENGL, IniName); CommonSettings.HighResolutionInterpolateColor = GetPrivateProfileInt("3D", "HighResolutionInterpolateColor", 1, IniName); NDS_3D_ChangeCore(cur3DCore); @@ -2246,11 +2358,24 @@ void Pause() emu_paused ^= 1; } -void FrameAdvance() +bool first; + +void FrameAdvance(bool state) { - frameAdvance = true; - execute = TRUE; - emu_paused = 1; + if(state) { + if(first) { + frameAdvance=true; + } + else { + execute = TRUE; + first=false; + } + } + else { + emu_halt(); + SPU_Pause(1); + emu_paused = 1; + } } enum CONFIGSCREEN @@ -2345,6 +2470,10 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM EnableMenuItem(mainMenu, IDM_CHEATS_SEARCH, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(mainMenu, IDM_WIFISETTINGS, MF_BYCOMMAND | (romloaded) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(mainMenu, IDM_RECORD_MOVIE, MF_BYCOMMAND | (romloaded && movieMode == MOVIEMODE_INACTIVE) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(mainMenu, IDM_PLAY_MOVIE, MF_BYCOMMAND | (romloaded && movieMode == MOVIEMODE_INACTIVE) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(mainMenu, IDM_STOPMOVIE, MF_BYCOMMAND | (romloaded && movieMode != MOVIEMODE_INACTIVE) ? MF_ENABLED : MF_GRAYED); + //Update savestate slot items based on ROM loaded for (int x = 0; x < 10; x++) { @@ -2476,6 +2605,7 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM //break; case WM_KEYDOWN: + break; case WM_SYSKEYDOWN: case WM_CUSTKEYDOWN: { @@ -2953,7 +3083,15 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM case IDM_CHEATS_SEARCH: CheatsSearchDialog(hwnd); return 0; - + case IDM_RECORD_MOVIE: + MovieRecordTo(); + return 0; + case IDM_PLAY_MOVIE: + Replay_LoadMovie(); + return 0; + case IDM_STOPMOVIE: + FCEUI_StopMovie(); + return 0; case ID_VIEW_FRAMECOUNTER: frameCounterDisplay ^= 1; MainWindow->checkMenu(ID_VIEW_FRAMECOUNTER, frameCounterDisplay ? MF_CHECKED : MF_UNCHECKED); @@ -2993,6 +3131,25 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM osd->clear(); return 0; + case ID_RAM_SEARCH: + if(!RamSearchHWnd) + { + RamSearchHWnd = CreateDialog(hAppInst, MAKEINTRESOURCE(IDD_RAMSEARCH), hwnd, (DLGPROC) RamSearchProc); + } + else + SetForegroundWindow(RamSearchHWnd); + break; + + case ID_RAM_WATCH: + if(!RamWatchHWnd) + { + RamWatchHWnd = CreateDialog(hAppInst, MAKEINTRESOURCE(IDD_RAMWATCH), hwnd, (DLGPROC) RamWatchProc); + // DialogsOpen++; + } + else + SetForegroundWindow(RamWatchHWnd); + return 0; + #define clearsaver() \ MainWindow->checkMenu(IDC_SAVETYPE1, MF_BYCOMMAND | MF_UNCHECKED); \ MainWindow->checkMenu(IDC_SAVETYPE2, MF_BYCOMMAND | MF_UNCHECKED); \ @@ -3231,7 +3388,6 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM } } - return DefWindowProc (hwnd, message, wParam, lParam); } @@ -3373,7 +3529,6 @@ LRESULT CALLBACK EmulationSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, L { CheatsSearchReset(); NDS_Reset(); - frameCounter = 0; } } } @@ -3607,7 +3762,6 @@ LRESULT CALLBACK WifiSettingsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM { CheatsSearchReset(); NDS_Reset(); - frameCounter = 0; } } } @@ -3729,7 +3883,6 @@ void ResetGame() { CheatsSearchReset(); NDS_Reset(); - frameCounter=0; } //adelikat: This function allows another file to add a message to the screen @@ -3739,6 +3892,7 @@ void SetMessageToDisplay(const char *message) //adelikat: TODO: set up a function that does the clearing and setting of the counter, so this code doesn't have to be done all over the place // make the function receive an int for the counter, but overload so that if no int, 120 is used MessageToDisplay << message; + NewMessageToDisplay = true; displayMessageCounter = 120;// } @@ -3775,4 +3929,9 @@ void UpdateHotkeyAssignments() text = text.substr(0,truncate); text.append("\tCtrl+O"); //TODO: Append actual hotkey assignment ChangeMenuItemText(IDM_OPEN, text); //Set Menu item text +} + +HWND GetMainHWND() +{ + return MainWindow->getHWnd(); } \ No newline at end of file diff --git a/desmume/src/windows/main.h b/desmume/src/windows/main.h index bcb29d147..40e250c5b 100644 --- a/desmume/src/windows/main.h +++ b/desmume/src/windows/main.h @@ -11,14 +11,13 @@ extern unsigned int lastSaveState; void SaveStateMessages(int slotnum, int whichMessage); void Display(); void Pause(); -void FrameAdvance(); +void FrameAdvance(bool state); void ResetGame(); //Resets game (for the menu item & hotkey void AviRecordTo(); void AviEnd(); void SetMessageToDisplay(const char *message); //For sending output to the main screen extern bool ShowInputDisplay; -extern unsigned int frameCounter; extern bool frameCounterDisplay; extern bool FpsDisplay; extern bool ShowInputDisplay; @@ -28,5 +27,9 @@ extern bool ShowLagFrameCounter; #define GPU3D_OPENGL 1 #define GPU3D_SWRAST 2 +extern int backupmemorytype; +extern u32 backupmemorysize; + +HWND GetMainHWND(); //adelikat: returns the main window handle #endif diff --git a/desmume/src/windows/ram_search.cpp b/desmume/src/windows/ram_search.cpp new file mode 100644 index 000000000..16daf0c25 --- /dev/null +++ b/desmume/src/windows/ram_search.cpp @@ -0,0 +1,1932 @@ +// A few notes about this implementation of a RAM search window: +// +// Speed of update was one of the highest priories. +// This is because I wanted the RAM search window to be able to +// update every single value in RAM every single frame, and +// keep track of the exact number of frames across which each value has changed, +// without causing the emulation to run noticeably slower than normal. +// +// The data representation was changed from one entry per valid address +// to one entry per contiguous range of uneliminated addresses +// which references uniform pools of per-address properties. +// - This saves time when there are many items because +// it minimizes the amount of data that needs to be stored and processed per address. +// - It also saves time when there are few items because +// it ensures that no time is wasted in iterating through +// addresses that have already been eliminated from the search. +// +// The worst-case scenario is when every other item has been +// eliminated from the search, maximizing the number of regions. +// This implementation manages to handle even that pathological case +// acceptably well. In fact, it still updates faster than the previous implementation. +// The time spent setting up or clearing such a large number of regions +// is somewhat horrendous, but it seems reasonable to have poor worst-case speed +// during these sporadic "setup" steps to achieve an all-around faster per-update speed. +// (You can test this case by performing the search: Modulo 2 Is Specific Address 0) + +#include "resource.h" + +#include "common.h" +#include "NDSSystem.h" +#include "ramwatch.h" + +#include "ram_search.h" +#include +#include +#include "ramwatch.h" +#include +#include +#ifdef _WIN32 + #include "BaseTsd.h" + typedef INT_PTR intptr_t; +#else + #include "stdint.h" +#endif + +HWND RamSearchHWnd; + +extern HWND RamWatchHWnd; + +extern char Str_Tmp[1024]; + +int Rom_Size; //TODO +unsigned char* Rom_Data; //TODO + +struct MemoryRegion +{ + unsigned int hardwareAddress; // hardware address of the start of this region + unsigned int size; // number of bytes to the end of this region + unsigned char* softwareAddress; // pointer to the start of the live emulator source values for this region + BOOL byteSwapped; // true if this is a byte-swapped region of memory + + unsigned int virtualIndex; // index into s_prevValues, s_curValues, and s_numChanges, valid after being initialized in ResetMemoryRegions() + unsigned int itemIndex; // index into listbox items, valid when s_itemIndicesInvalid is false +}; + +static unsigned char s_prevValues [MAX_RAM_SIZE+4] = {0}; // values at last search or reset +static unsigned char s_curValues [MAX_RAM_SIZE+4] = {0}; // values at last frame update +static unsigned short s_numChanges [MAX_RAM_SIZE+4] = {0}; // number of changes of the item starting at this virtual index address +static MemoryRegion* s_itemIndexToRegionPointer [MAX_RAM_SIZE+4] = {0}; // used for random access into the memory list (trading memory size to get speed here, too bad it's so much memory), only valid when s_itemIndicesInvalid is false +static BOOL s_itemIndicesInvalid = true; // if true, the link from listbox items to memory regions (s_itemIndexToRegionPointer) and the link from memory regions to list box items (MemoryRegion::itemIndex) both need to be recalculated +static BOOL s_prevValuesNeedUpdate = true; // if true, the "prev" values should be updated using the "cur" values on the next frame update signaled +static unsigned int s_maxItemIndex = 0; // max currently valid item index, the listbox sometimes tries to update things past the end of the list so we need to know this to ignore those attempts + +static const MemoryRegion s_prgRegion = { 0x02000000, 0x400000, (unsigned char*)ARM9Mem.MAIN_MEM, true}; + +/* +static const MemoryRegion s_prgRegion = { 0x020000, SEGACD_RAM_PRG_SIZE, (unsigned char*)Ram_Prg, true}; +static const MemoryRegion s_word1MRegion = { 0x200000, SEGACD_1M_RAM_SIZE, (unsigned char*)Ram_Word_1M, true}; +static const MemoryRegion s_word2MRegion = { 0x200000, SEGACD_2M_RAM_SIZE, (unsigned char*)Ram_Word_2M, true}; +static const MemoryRegion s_z80Region = { 0xA00000, Z80_RAM_SIZE, (unsigned char*)Ram_Z80, true}; +static const MemoryRegion s_68kRegion = { 0xFF0000, _68K_RAM_SIZE, (unsigned char*)Ram_68k, true}; +static const MemoryRegion s_32xRegion = {0x06000000, _32X_RAM_SIZE, (unsigned char*)_32X_Ram, false}; +*/ +// list of contiguous uneliminated memory regions +typedef std::list MemoryList; +static MemoryList s_activeMemoryRegions; + +// for undo support (could be better, but this way was really easy) +static MemoryList s_activeMemoryRegionsBackup; +static int s_undoType = 0; // 0 means can't undo, 1 means can undo, 2 means can redo + +void RamSearchSaveUndoStateIfNotTooBig(HWND hDlg); +static const int tooManyRegionsForUndo = 10000; + +void ResetMemoryRegions() +{ +// Clear_Sound_Buffer(); + + s_activeMemoryRegions.clear(); + + s_activeMemoryRegions.push_back(s_prgRegion); + + /*if(Genesis_Started || _32X_Started || SegaCD_Started) + { + s_activeMemoryRegions.push_back(s_68kRegion); + s_activeMemoryRegions.push_back(s_z80Region); + if(SegaCD_Started) + { + s_activeMemoryRegions.push_back(s_prgRegion); + s_activeMemoryRegions.push_back((Ram_Word_State & 0x2) ? s_word1MRegion : s_word2MRegion); + } + if(_32X_Started) + { + s_activeMemoryRegions.push_back(s_32xRegion); + } + }*/ + + int nextVirtualIndex = 0; + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ++iter) + { + MemoryRegion& region = *iter; + region.virtualIndex = nextVirtualIndex; + assert(((intptr_t)region.softwareAddress & 1) == 0 && "somebody need to reimplement ReadValueAtSoftwareAddress()"); + nextVirtualIndex = region.virtualIndex + region.size; + } +// assert(nextVirtualIndex <= MAX_RAM_SIZE); TODO +} + +// eliminates a range of hardware addresses from the search results +// returns 2 if it changed the region and moved the iterator to another region +// returns 1 if it changed the region but didn't move the iterator +// returns 0 if it had no effect +// warning: don't call anything that takes an itemIndex in a loop that calls DeactivateRegion... +// doing so would be tremendously slow because DeactivateRegion invalidates the index cache +int DeactivateRegion(MemoryRegion& region, MemoryList::iterator& iter, unsigned int hardwareAddress, unsigned int size) +{ + if(hardwareAddress + size <= region.hardwareAddress || hardwareAddress >= region.hardwareAddress + region.size) + { + // region is unaffected + return 0; + } + else if(hardwareAddress > region.hardwareAddress && hardwareAddress + size >= region.hardwareAddress + region.size) + { + // erase end of region + region.size = hardwareAddress - region.hardwareAddress; + return 1; + } + else if(hardwareAddress <= region.hardwareAddress && hardwareAddress + size < region.hardwareAddress + region.size) + { + // erase start of region + int eraseSize = (hardwareAddress + size) - region.hardwareAddress; + region.hardwareAddress += eraseSize; + region.size -= eraseSize; + region.softwareAddress += eraseSize; + region.virtualIndex += eraseSize; + return 1; + } + else if(hardwareAddress <= region.hardwareAddress && hardwareAddress + size >= region.hardwareAddress + region.size) + { + // erase entire region + iter = s_activeMemoryRegions.erase(iter); + s_itemIndicesInvalid = TRUE; + return 2; + } + else //if(hardwareAddress > region.hardwareAddress && hardwareAddress + size < region.hardwareAddress + region.size) + { + // split region + int eraseSize = (hardwareAddress + size) - region.hardwareAddress; + MemoryRegion region2 = {region.hardwareAddress + eraseSize, region.size - eraseSize, region.softwareAddress + eraseSize, region.byteSwapped, region.virtualIndex + eraseSize}; + region.size = hardwareAddress - region.hardwareAddress; + iter = s_activeMemoryRegions.insert(++iter, region2); + s_itemIndicesInvalid = TRUE; + return 2; + } +} + +/* +// eliminates a range of hardware addresses from the search results +// this is a simpler but usually slower interface for the above function +void DeactivateRegion(unsigned int hardwareAddress, unsigned int size) +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ) + { + MemoryRegion& region = *iter; + if(2 != DeactivateRegion(region, iter, hardwareAddress, size)) + ++iter; + } +} +*/ + +// warning: can be slow +void CalculateItemIndices(int itemSize) +{ + unsigned int itemIndex = 0; + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ++iter) + { + MemoryRegion& region = *iter; + region.itemIndex = itemIndex; + int startSkipSize = ((unsigned int)(itemSize - region.hardwareAddress)) % itemSize; + unsigned int start = startSkipSize; + unsigned int end = region.size; + for(unsigned int i = start; i < end; i += itemSize) + s_itemIndexToRegionPointer[itemIndex++] = ®ion; + } + s_maxItemIndex = itemIndex; + s_itemIndicesInvalid = FALSE; +} + +template +void UpdateRegionT(const MemoryRegion& region, const MemoryRegion* nextRegionPtr) +{ + if(s_prevValuesNeedUpdate) + memcpy(s_prevValues + region.virtualIndex, s_curValues + region.virtualIndex, region.size + sizeof(compareType) - sizeof(stepType)); + + unsigned int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + + unsigned char* sourceAddr = region.softwareAddress - region.virtualIndex; + unsigned int indexStart = region.virtualIndex + startSkipSize; + unsigned int indexEnd = region.virtualIndex + region.size; + + if(sizeof(compareType) == 1) + { + for(unsigned int i = indexStart; i < indexEnd; i++) + { + if(s_curValues[i] != sourceAddr[i^swapXOR]) // if value changed + { + s_curValues[i] = sourceAddr[i^swapXOR]; // update value + //if(s_numChanges[i] != 0xFFFF) + s_numChanges[i]++; // increase change count + } + } + } + else // it's more complicated for non-byte sizes because: + { // - more than one byte can affect a given change count entry + // - when more than one of those bytes changes simultaneously the entry's change count should only increase by 1 + // - a few of those bytes can be outside the region + + unsigned int endSkipSize = ((unsigned int)(startSkipSize - region.size)) % sizeof(stepType); + unsigned int lastIndexToRead = indexEnd + endSkipSize + sizeof(compareType) - sizeof(stepType); + unsigned int lastIndexToCopy = lastIndexToRead; + if(nextRegionPtr) + { + const MemoryRegion& nextRegion = *nextRegionPtr; + int nextStartSkipSize = ((unsigned int)(sizeof(stepType) - nextRegion.hardwareAddress)) % sizeof(stepType); + unsigned int nextIndexStart = nextRegion.virtualIndex + nextStartSkipSize; + if(lastIndexToCopy > nextIndexStart) + lastIndexToCopy = nextIndexStart; + } + + unsigned int nextValidChange [sizeof(compareType)]; + for(unsigned int i = 0; i < sizeof(compareType); i++) + nextValidChange[i] = indexStart + i; + + for(unsigned int i = indexStart, j = 0; i < lastIndexToRead; i++, j++) + { + if(s_curValues[i] != sourceAddr[i^swapXOR]) // if value of this byte changed + { + if(i < lastIndexToCopy) + s_curValues[i] = sourceAddr[i^swapXOR]; // update value + for(int k = 0; k < sizeof(compareType); k++) // loop through the previous entries that contain this byte + { + if(i >= indexEnd+k) + continue; + int m = (j-k+sizeof(compareType)) & (sizeof(compareType)-1); + if(nextValidChange[m]+sizeof(compareType) <= i+sizeof(compareType)) // if we didn't already increase the change count for this entry + { + //if(s_numChanges[i-k] != 0xFFFF) + s_numChanges[i-k]++; // increase the change count for this entry + nextValidChange[m] = i+sizeof(compareType); // and remember not to increase it again + } + } + } + } + } +} + +template +void UpdateRegionsT() +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end();) + { + const MemoryRegion& region = *iter; + ++iter; + const MemoryRegion* nextRegion = (iter == s_activeMemoryRegions.end()) ? NULL : &*iter; + + if(region.byteSwapped) + UpdateRegionT(region, nextRegion); + else + UpdateRegionT(region, nextRegion); + } + + s_prevValuesNeedUpdate = false; +} + +template +int CountRegionItemsT() +{ + if(sizeof(stepType) == 1) + { + if(s_activeMemoryRegions.empty()) + return 0; + + if(s_itemIndicesInvalid) + CalculateItemIndices(sizeof(stepType)); + + MemoryRegion& lastRegion = s_activeMemoryRegions.back(); + return lastRegion.itemIndex + lastRegion.size; + } + else // the branch above is faster but won't work if the step size isn't 1 + { + int total = 0; + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ++iter) + { + MemoryRegion& region = *iter; + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + total += (region.size - startSkipSize + (sizeof(stepType)-1)) / sizeof(stepType); + } + return total; + } +} + +// returns information about the item in the form of a "fake" region +// that has the item in it and nothing else +template +void ItemIndexToVirtualRegion(unsigned int itemIndex, MemoryRegion& virtualRegion) +{ + if(s_itemIndicesInvalid) + CalculateItemIndices(sizeof(stepType)); + + if(itemIndex >= s_maxItemIndex) + { + memset(&virtualRegion, 0, sizeof(MemoryRegion)); + return; + } + + const MemoryRegion* regionPtr = s_itemIndexToRegionPointer[itemIndex]; + const MemoryRegion& region = *regionPtr; + + int bytesWithinRegion = (itemIndex - region.itemIndex) * sizeof(stepType); + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + bytesWithinRegion += startSkipSize; + + virtualRegion.size = sizeof(compareType); + virtualRegion.hardwareAddress = region.hardwareAddress + bytesWithinRegion; + virtualRegion.softwareAddress = region.softwareAddress + bytesWithinRegion; + virtualRegion.virtualIndex = region.virtualIndex + bytesWithinRegion; + virtualRegion.byteSwapped = region.byteSwapped; + virtualRegion.itemIndex = itemIndex; + return; +} + +template +unsigned int ItemIndexToVirtualIndex(unsigned int itemIndex) +{ + MemoryRegion virtualRegion; + ItemIndexToVirtualRegion(itemIndex, virtualRegion); + return virtualRegion.virtualIndex; +} + +template +T ReadBigEndian(const unsigned char* data) +{ + T rv = 0; + for(int i = 0; i < sizeof(T); i++) + { + rv <<= 8; + rv |= *data++; + } + return rv; +} +template<> signed char ReadBigEndian(const unsigned char* data) { return *data; } +template<> unsigned char ReadBigEndian(const unsigned char* data) { return *data; } + + +template +compareType GetPrevValueFromVirtualIndex(unsigned int virtualIndex) +{ + return ReadBigEndian(s_prevValues + virtualIndex); + //return *(compareType*)(s_prevValues+virtualIndex); +} +template +compareType GetCurValueFromVirtualIndex(unsigned int virtualIndex) +{ + return ReadBigEndian(s_curValues + virtualIndex); +// return *(compareType*)(s_curValues+virtualIndex); +} +template +unsigned short GetNumChangesFromVirtualIndex(unsigned int virtualIndex) +{ + unsigned short num = s_numChanges[virtualIndex]; + //for(unsigned int i = 1; i < sizeof(stepType); i++) + // if(num < s_numChanges[virtualIndex+i]) + // num = s_numChanges[virtualIndex+i]; + return num; +} + +template +compareType GetPrevValueFromItemIndex(unsigned int itemIndex) +{ + int virtualIndex = ItemIndexToVirtualIndex(itemIndex); + return GetPrevValueFromVirtualIndex(virtualIndex); +} +template +compareType GetCurValueFromItemIndex(unsigned int itemIndex) +{ + int virtualIndex = ItemIndexToVirtualIndex(itemIndex); + return GetCurValueFromVirtualIndex(virtualIndex); +} +template +unsigned short GetNumChangesFromItemIndex(unsigned int itemIndex) +{ + int virtualIndex = ItemIndexToVirtualIndex(itemIndex); + return GetNumChangesFromVirtualIndex(virtualIndex); +} +template +unsigned int GetHardwareAddressFromItemIndex(unsigned int itemIndex) +{ + MemoryRegion virtualRegion; + ItemIndexToVirtualRegion(itemIndex, virtualRegion); + return virtualRegion.hardwareAddress; +} + +// this one might be unreliable, haven't used it much +template +unsigned int HardwareAddressToItemIndex(unsigned int hardwareAddress) +{ + if(s_itemIndicesInvalid) + CalculateItemIndices(sizeof(stepType)); + + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ++iter) + { + MemoryRegion& region = *iter; + if(hardwareAddress >= region.hardwareAddress && hardwareAddress < region.hardwareAddress + region.size) + { + int indexWithinRegion = (hardwareAddress - region.hardwareAddress) / sizeof(stepType); + return region.itemIndex + indexWithinRegion; + } + } + + return -1; +} + + + + +// workaround for a parser error in MSVC that sometimes deletes a comma preceeding a macro +// this macro takes a type and a signed/unsigned modifier, and returns the same type with that modifier whether or not the compiler decides to delete the comma between them +template +struct DummyType { typedef T t; }; +#define COMMAHACK(sign, type) DummyType::t +#ifdef _MSC_VER +#pragma warning(disable : 4114) // disable "same modifier used twice" warning that otherwise would get issued when the compiler bug happens +#endif + +// it's ugly but I can't think of a better way to call these functions that isn't also slower, since +// I need the current values of these arguments to determine which primitive types are used within the function +#define CALL_WITH_T_SIZE_TYPES(functionName, sizeTypeID, isSigned, requireAligned, ...) \ + (sizeTypeID == 'b' \ + ? (isSigned \ + ? functionName(__VA_ARGS__) \ + : functionName(__VA_ARGS__)) \ + : sizeTypeID == 'w' \ + ? (isSigned \ + ? (requireAligned \ + ? functionName(__VA_ARGS__) \ + : functionName(__VA_ARGS__)) \ + : (requireAligned \ + ? functionName(__VA_ARGS__) \ + : functionName(__VA_ARGS__))) \ + : sizeTypeID == 'd' \ + ? (isSigned \ + ? (requireAligned \ + ? functionName(__VA_ARGS__) \ + : functionName(__VA_ARGS__)) \ + : (requireAligned \ + ? functionName(__VA_ARGS__) \ + : functionName(__VA_ARGS__))) \ + : functionName(__VA_ARGS__)) + +// version that takes a forced comparison type +#define CALL_WITH_T_STEP(functionName, sizeTypeID, sign,type, requireAligned, ...) \ + (sizeTypeID == 'b' \ + ? functionName(__VA_ARGS__) \ + : sizeTypeID == 'w' \ + ? (requireAligned \ + ? functionName(__VA_ARGS__) \ + : functionName(__VA_ARGS__)) \ + : sizeTypeID == 'd' \ + ? (requireAligned \ + ? functionName(__VA_ARGS__) \ + : functionName(__VA_ARGS__)) \ + : functionName(__VA_ARGS__)) + + +// basic comparison functions: +template inline bool LessCmp (T x, T y, T i) { return x < y; } +template inline bool MoreCmp (T x, T y, T i) { return x > y; } +template inline bool LessEqualCmp (T x, T y, T i) { return x <= y; } +template inline bool MoreEqualCmp (T x, T y, T i) { return x >= y; } +template inline bool EqualCmp (T x, T y, T i) { return x == y; } +template inline bool UnequalCmp (T x, T y, T i) { return x != y; } +template inline bool DiffByCmp (T x, T y, T p) { return x - y == p || y - x == p; } +template inline bool ModIsCmp (T x, T y, T p) { return p && x % p == y; } + +// compare-to type functions: +template +void SearchRelative (bool(*cmpFun)(T,T,T), T ignored, T param) +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ) + { + MemoryRegion& region = *iter; + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + unsigned int start = region.virtualIndex + startSkipSize; + unsigned int end = region.virtualIndex + region.size; + for(unsigned int i = start, hwaddr = region.hardwareAddress; i < end; i += sizeof(stepType), hwaddr += sizeof(stepType)) + if(!cmpFun(GetCurValueFromVirtualIndex(i), GetPrevValueFromVirtualIndex(i), param)) + if(2 == DeactivateRegion(region, iter, hwaddr, sizeof(stepType))) + goto outerContinue; + ++iter; +outerContinue: + continue; + } +} +template +void SearchSpecific (bool(*cmpFun)(T,T,T), T value, T param) +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ) + { + MemoryRegion& region = *iter; + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + unsigned int start = region.virtualIndex + startSkipSize; + unsigned int end = region.virtualIndex + region.size; + for(unsigned int i = start, hwaddr = region.hardwareAddress; i < end; i += sizeof(stepType), hwaddr += sizeof(stepType)) + if(!cmpFun(GetCurValueFromVirtualIndex(i), value, param)) + if(2 == DeactivateRegion(region, iter, hwaddr, sizeof(stepType))) + goto outerContinue; + ++iter; +outerContinue: + continue; + } +} +template +void SearchAddress (bool(*cmpFun)(T,T,T), T address, T param) +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ) + { + MemoryRegion& region = *iter; + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + unsigned int start = region.virtualIndex + startSkipSize; + unsigned int end = region.virtualIndex + region.size; + for(unsigned int i = start, hwaddr = region.hardwareAddress; i < end; i += sizeof(stepType), hwaddr += sizeof(stepType)) + if(!cmpFun(hwaddr, address, param)) + if(2 == DeactivateRegion(region, iter, hwaddr, sizeof(stepType))) + goto outerContinue; + ++iter; +outerContinue: + continue; + } +} +template +void SearchChanges (bool(*cmpFun)(T,T,T), T changes, T param) +{ + for(MemoryList::iterator iter = s_activeMemoryRegions.begin(); iter != s_activeMemoryRegions.end(); ) + { + MemoryRegion& region = *iter; + int startSkipSize = ((unsigned int)(sizeof(stepType) - region.hardwareAddress)) % sizeof(stepType); + unsigned int start = region.virtualIndex + startSkipSize; + unsigned int end = region.virtualIndex + region.size; + for(unsigned int i = start, hwaddr = region.hardwareAddress; i < end; i += sizeof(stepType), hwaddr += sizeof(stepType)) + if(!cmpFun(GetNumChangesFromVirtualIndex(i), changes, param)) + if(2 == DeactivateRegion(region, iter, hwaddr, sizeof(stepType))) + goto outerContinue; + ++iter; +outerContinue: + continue; + } +} + +char rs_c='s'; +char rs_o='='; +char rs_t='s'; +int rs_param=0, rs_val=0, rs_val_valid=0; +char rs_type_size = 'b', rs_last_type_size = rs_type_size; +bool noMisalign = true, rs_last_no_misalign = noMisalign; +bool littleEndian = false; +int last_rs_possible = -1; +int last_rs_regions = -1; + +void prune(char c,char o,char t,int v,int p) +{ + // repetition-reducing macros + #define DO_SEARCH(sf) \ + switch (o) \ + { \ + case '<': DO_SEARCH_2(LessCmp,sf); break; \ + case '>': DO_SEARCH_2(MoreCmp,sf); break; \ + case '=': DO_SEARCH_2(EqualCmp,sf); break; \ + case '!': DO_SEARCH_2(UnequalCmp,sf); break; \ + case 'l': DO_SEARCH_2(LessEqualCmp,sf); break; \ + case 'm': DO_SEARCH_2(MoreEqualCmp,sf); break; \ + case 'd': DO_SEARCH_2(DiffByCmp,sf); break; \ + case '%': DO_SEARCH_2(ModIsCmp,sf); break; \ + default: assert(!"Invalid operator for this search type."); break; \ + } + + // perform the search, eliminating nonmatching values + switch (c) + { + #define DO_SEARCH_2(CmpFun,sf) CALL_WITH_T_SIZE_TYPES(sf, rs_type_size, t, noMisalign, CmpFun,v,p) + case 'r': DO_SEARCH(SearchRelative); break; + case 's': DO_SEARCH(SearchSpecific); break; + + #undef DO_SEARCH_2 + #define DO_SEARCH_2(CmpFun,sf) CALL_WITH_T_STEP(sf, rs_type_size, unsigned,int, noMisalign, CmpFun,v,p); + case 'a': DO_SEARCH(SearchAddress); break; + + #undef DO_SEARCH_2 + #define DO_SEARCH_2(CmpFun,sf) CALL_WITH_T_STEP(sf, rs_type_size, unsigned,short, noMisalign, CmpFun,v,p); + case 'n': DO_SEARCH(SearchChanges); break; + + default: assert(!"Invalid search comparison type."); break; + } + + s_prevValuesNeedUpdate = true; + + int prevNumItems = last_rs_possible; + + CompactAddrs(); + + if(prevNumItems == last_rs_possible) + { + SetRamSearchUndoType(RamSearchHWnd, 0); // nothing to undo + } +} + + + + +template +bool CompareRelativeAtItem (bool(*cmpFun)(T,T,T), int itemIndex, T ignored, T param) +{ + return cmpFun(GetCurValueFromItemIndex(itemIndex), GetPrevValueFromItemIndex(itemIndex), param); +} +template +bool CompareSpecificAtItem (bool(*cmpFun)(T,T,T), int itemIndex, T value, T param) +{ + return cmpFun(GetCurValueFromItemIndex(itemIndex), value, param); +} +template +bool CompareAddressAtItem (bool(*cmpFun)(T,T,T), int itemIndex, T address, T param) +{ + return cmpFun(GetHardwareAddressFromItemIndex(itemIndex), address, param); +} +template +bool CompareChangesAtItem (bool(*cmpFun)(T,T,T), int itemIndex, T changes, T param) +{ + return cmpFun(GetNumChangesFromItemIndex(itemIndex), changes, param); +} + +int ReadControlInt(int controlID, bool forceHex, BOOL& success) +{ + int rv = 0; + BOOL ok = false; + + if(!forceHex) + { + rv = GetDlgItemInt(RamSearchHWnd,controlID,&ok,(rs_t == 's')); + } + + if(!ok) + { + if(GetDlgItemText(RamSearchHWnd,controlID,Str_Tmp,16)) + { + for(int i = 0; Str_Tmp[i]; i++) {if(toupper(Str_Tmp[i]) == 'O') Str_Tmp[i] = '0';} + const char* strPtr = Str_Tmp; + bool negate = false; + while(strPtr[0] == '-') + strPtr++, negate = !negate; + if(strPtr[0] == '+') + strPtr++; + if(strPtr[0] == '0' && tolower(strPtr[1]) == 'x') + strPtr += 2, forceHex = true; + if(strPtr[0] == '$') + strPtr++, forceHex = true; + if(!forceHex) + { + const char* strSearchPtr = strPtr; + while(*strSearchPtr) + { + int c = tolower(*strSearchPtr++); + if(c >= 'a' && c <= 'f') + forceHex = true; + } + } + const char* formatString = forceHex ? "%X" : ((rs_t=='s') ? "%d" : "%u"); + if(sscanf(strPtr, formatString, &rv) > 0) + ok = true; + if(negate) + rv = -rv; + } + } + + success = ok; + return rv; +} + + +bool Set_RS_Val() +{ + BOOL success; + + // update rs_val + switch(rs_c) + { + case 'r': + default: + rs_val = 0; + break; + case 's': + rs_val = ReadControlInt(IDC_EDIT_COMPAREVALUE, rs_t == 'h', success); + if(!success) + return false; + if((rs_type_size == 'b' && rs_t == 's' && (rs_val < -128 || rs_val > 127)) || + (rs_type_size == 'b' && rs_t != 's' && (rs_val < 0 || rs_val > 255)) || + (rs_type_size == 'w' && rs_t == 's' && (rs_val < -32768 || rs_val > 32767)) || + (rs_type_size == 'w' && rs_t != 's' && (rs_val < 0 || rs_val > 65535))) + return false; + break; + case 'a': + rs_val = ReadControlInt(IDC_EDIT_COMPAREADDRESS, true, success); + if(!success || rs_val < 0 || rs_val > 0x06040000) + return false; + break; + case 'n': { + rs_val = ReadControlInt(IDC_EDIT_COMPARECHANGES, false, success); + if(!success || rs_val < 0 || rs_val > 0xFFFF) + return false; + } break; + } + + // also update rs_param + switch(rs_o) + { + default: + rs_param = 0; + break; + case 'd': + rs_param = ReadControlInt(IDC_EDIT_DIFFBY, false, success); + if(!success) + return false; + if(rs_param < 0) + rs_param = -rs_param; + break; + case '%': + rs_param = ReadControlInt(IDC_EDIT_MODBY, false, success); + if(!success || rs_param == 0) + return false; + break; + } + + // validate that rs_param fits in the comparison data type + { + int appliedSize = rs_type_size; + int appliedSign = rs_t; + if(rs_c == 'n') + appliedSize = 'w', appliedSign = 'u'; + if(rs_c == 'a') + appliedSize = 'd', appliedSign = 'u'; + if((appliedSize == 'b' && appliedSize == 's' && (rs_param < -128 || rs_param > 127)) || + (appliedSize == 'b' && appliedSize != 's' && (rs_param < 0 || rs_param > 255)) || + (appliedSize == 'w' && appliedSize == 's' && (rs_param < -32768 || rs_param > 32767)) || + (appliedSize == 'w' && appliedSize != 's' && (rs_param < 0 || rs_param > 65535))) + return false; + } + + return true; +} + +bool IsSatisfied(int itemIndex) +{ + if(!rs_val_valid) + return true; + int o = rs_o; + switch (rs_c) + { + #undef DO_SEARCH_2 + #define DO_SEARCH_2(CmpFun,sf) return CALL_WITH_T_SIZE_TYPES(sf, rs_type_size,(rs_t=='s'),noMisalign, CmpFun,itemIndex,rs_val,rs_param); + case 'r': DO_SEARCH(CompareRelativeAtItem); break; + case 's': DO_SEARCH(CompareSpecificAtItem); break; + + #undef DO_SEARCH_2 + #define DO_SEARCH_2(CmpFun,sf) return CALL_WITH_T_STEP(sf, rs_type_size, unsigned,int, noMisalign, CmpFun,itemIndex,rs_val,rs_param); + case 'a': DO_SEARCH(CompareAddressAtItem); break; + + #undef DO_SEARCH_2 + #define DO_SEARCH_2(CmpFun,sf) return CALL_WITH_T_STEP(sf, rs_type_size, unsigned,short, noMisalign, CmpFun,itemIndex,rs_val,rs_param); + case 'n': DO_SEARCH(CompareChangesAtItem); break; + } + return false; +} + + + +// this is mainly for the RAM watch window +unsigned int ReadValueAtSoftwareAddress(const unsigned char* address, unsigned int size, int byteSwapped = false) +{ + unsigned int value = 0; + if(!byteSwapped) + { + // convert to current endianness + for(unsigned int i = 0; i < size; i++) + { + value <<= 8; + value |= *address++; + } + } + else + { + // byte-swap and convert to current endianness at the same time + for(unsigned int i = 0; i < size; i++) + { + value <<= 8; + value |= *((unsigned char*)((intptr_t)address++^1)); + } + } + return value; +} +void WriteValueAtSoftwareAddress(unsigned char* address, unsigned int value, unsigned int size, int byteSwapped = false) +{ + if(!byteSwapped) + { + // write as big endian + for(int i = size-1; i >= 0; i--) + { + address[i] = value & 0xFF; + value >>= 8; + } + } + else + { + // byte-swap and write as big endian at the same time + for(int i = size-1; i >= 0; i--) + { + *((unsigned char*)((intptr_t)(address+i)^1)) = value & 0xFF; + value >>= 8; + } + } +} +inline bool IsInRange(unsigned int x, unsigned int min, unsigned int size) +{ + x -= min; + return x < size; +} +unsigned int ReadValueAtHardwareAddress(unsigned int address, unsigned int size) +{ + if((address & ~0xFFFFFF) == ~0xFFFFFF) + address &= 0xFFFFFF; + + + char buf[4]; + MMU_DumpMemBlock(0, address, 4, (u8*)buf); + + u32 val_u32 = *(u32*)buf; + u16 val_u16 = *(u16*)buf; + u8 val_u8 = *(u8*)buf; + + switch (size) + { + case 1: return val_u8; + case 2: return val_u16; + case 4: return val_u32; + } +// if(IsInRange(address, 0xFF0000, _68K_RAM_SIZE)) +// return ReadValueAtSoftwareAddress(Ram_68k + address - 0xFF0000, size, true); +/* if(IsInRange(address, 0xFF0000, _68K_RAM_SIZE)) TODO + return ReadValueAtSoftwareAddress(Ram_68k + address - 0xFF0000, size, true); + if(IsInRange(address, 0xA00000, Z80_RAM_SIZE)) + return ReadValueAtSoftwareAddress(Ram_Z80 + address - 0xA00000, size, true); + if(SegaCD_Started && IsInRange(address, 0x020000, SEGACD_RAM_PRG_SIZE)) + return ReadValueAtSoftwareAddress(Ram_Prg + address - 0x020000, size, true); + if(SegaCD_Started && IsInRange(address, 0x200000, SEGACD_1M_RAM_SIZE)) + return ReadValueAtSoftwareAddress(((Ram_Word_State & 0x2) ? Ram_Word_1M : Ram_Word_2M) + address - 0x200000, size, true); + if(IsInRange(address, 0x0, Rom_Size)) + return ReadValueAtSoftwareAddress(Rom_Data + address, size, true); + if(_32X_Started && IsInRange(address, 0x06000000, _32X_RAM_SIZE)) + return ReadValueAtSoftwareAddress(_32X_Ram + address - 0x06000000, size, false);*/ + return 0; +} +bool WriteValueAtHardwareRAMAddress(unsigned int address, unsigned int value, unsigned int size, bool hookless) +{ + if((address & ~0xFFFFFF) == ~0xFFFFFF) + address &= 0xFFFFFF; +/* if(IsInRange(address, 0xFF0000, _68K_RAM_SIZE)) TODO + WriteValueAtSoftwareAddress(Ram_68k + address - 0xFF0000, value, size, true); + else if(IsInRange(address, 0xA00000, Z80_RAM_SIZE)) + WriteValueAtSoftwareAddress(Ram_Z80 + address - 0xA00000, value, size, true); + else if(SegaCD_Started && IsInRange(address, 0x020000, SEGACD_RAM_PRG_SIZE)) + WriteValueAtSoftwareAddress(Ram_Prg + address - 0x020000, value, size, true); + else if(SegaCD_Started && IsInRange(address, 0x200000, SEGACD_1M_RAM_SIZE)) + WriteValueAtSoftwareAddress(((Ram_Word_State & 0x2) ? Ram_Word_1M : Ram_Word_2M) + address - 0x200000, value, size, true); + else if(_32X_Started && IsInRange(address, 0x06000000, _32X_RAM_SIZE)) + WriteValueAtSoftwareAddress(_32X_Ram + address - 0x06000000, value, size, false);*/ +// else return false; +// if(!hookless) // a script that calls e.g. memory.writebyte() should trigger write hooks +// CallRegisteredLuaMemHook(address, size, value, LUAMEMHOOK_WRITE); + return true; +} +bool WriteValueAtHardwareROMAddress(unsigned int address, unsigned int value, unsigned int size) +{ + if(IsInRange(address, 0x0, Rom_Size)) + WriteValueAtSoftwareAddress(Rom_Data + address, value, size, true); + else return false; + return true; +} +bool WriteValueAtHardwareAddress(unsigned int address, unsigned int value, unsigned int size, bool hookless=false) +{ + return WriteValueAtHardwareRAMAddress(address, value, size, hookless) || + WriteValueAtHardwareROMAddress(address, value, size); +} +bool IsHardwareRAMAddressValid(unsigned int address) +{ + if((address & ~0xFFFFFF) == ~0xFFFFFF) + address &= 0xFFFFFF; +// if(IsInRange(address, 0xFF0000, _68K_RAM_SIZE)) +// return true; +// if(IsInRange(address, 0xA00000, Z80_RAM_SIZE)) +// return true; + + return true; //TODO +/* if(SegaCD_Started && IsInRange(address, 0x020000, SEGACD_RAM_PRG_SIZE)) TODO + return true; + if(SegaCD_Started && IsInRange(address, 0x200000, SEGACD_1M_RAM_SIZE)) + return true; + if(_32X_Started && IsInRange(address, 0x06000000, _32X_RAM_SIZE)) + return true;*/ + return false; +} +bool IsHardwareROMAddressValid(unsigned int address) +{ + return IsInRange(address, 0x0, Rom_Size); +} +bool IsHardwareAddressValid(unsigned int address) +{ + return IsHardwareROMAddressValid(address) || IsHardwareRAMAddressValid(address); +} + + + +int ResultCount=0; +bool AutoSearch=false; +bool AutoSearchAutoRetry=false; +LRESULT CALLBACK PromptWatchNameProc(HWND, UINT, WPARAM, LPARAM); +void UpdatePossibilities(int rs_possible, int regions); + + +void CompactAddrs() +{ + int size = (rs_type_size=='b' || !noMisalign) ? 1 : 2; + int prevResultCount = ResultCount; + + CalculateItemIndices(size); + + ResultCount = CALL_WITH_T_SIZE_TYPES(CountRegionItemsT, rs_type_size,rs_t=='s',noMisalign); + UpdatePossibilities(ResultCount, (int)s_activeMemoryRegions.size()); + + if(ResultCount != prevResultCount) + ListView_SetItemCount(GetDlgItem(RamSearchHWnd,IDC_RAMLIST),ResultCount); +} + +void soft_reset_address_info () +{ + ResetMemoryRegions(); + memset(s_numChanges, 0, sizeof(s_numChanges)); + CompactAddrs(); +} +void reset_address_info () +{ + SetRamSearchUndoType(RamSearchHWnd, 0); + s_activeMemoryRegionsBackup.clear(); // not necessary, but we'll take the time hit here instead of at the next thing that sets up an undo + memcpy(s_prevValues, s_curValues, sizeof(s_prevValues)); + s_prevValuesNeedUpdate = false; + ResetMemoryRegions(); + if(!RamSearchHWnd) + { + s_activeMemoryRegions.clear(); + ResultCount = 0; + } + else + { + // force s_prevValues to be valid + signal_new_frame(); + s_prevValuesNeedUpdate = true; + signal_new_frame(); + } + memset(s_numChanges, 0, sizeof(s_numChanges)); + CompactAddrs(); +} + +void signal_new_frame () +{ + CALL_WITH_T_SIZE_TYPES(UpdateRegionsT, rs_type_size,rs_t=='s',noMisalign); +} + + + + + + + +void RefreshRamListSelectedCountControlStatus(HWND hDlg) +{ + static int prevSelCount=-1; + int selCount = ListView_GetSelectedCount(GetDlgItem(hDlg,IDC_RAMLIST)); + if(selCount != prevSelCount) + { + if(selCount < 2 || prevSelCount < 2) + { + EnableWindow(GetDlgItem(hDlg, IDC_C_WATCH), (selCount == 1 && WatchCount < MAX_WATCH_COUNT) ? TRUE : FALSE); + EnableWindow(GetDlgItem(hDlg, IDC_C_ADDCHEAT), (selCount == 1) ? /*TRUE*/FALSE : FALSE); + EnableWindow(GetDlgItem(hDlg, IDC_C_ELIMINATE), (selCount >= 1) ? TRUE : FALSE); + } + prevSelCount = selCount; + } +} + + + + +struct AddrRange +{ + unsigned int addr; + unsigned int size; + unsigned int End() const { return addr + size; } + AddrRange(unsigned int a, unsigned int s) : addr(a),size(s){} +}; + +void signal_new_size () +{ + HWND lv = GetDlgItem(RamSearchHWnd,IDC_RAMLIST); + + int oldSize = (rs_last_type_size=='b' || !rs_last_no_misalign) ? 1 : 2; + int newSize = (rs_type_size=='b' || !noMisalign) ? 1 : 2; + bool numberOfItemsChanged = (oldSize != newSize); + + unsigned int itemsPerPage = ListView_GetCountPerPage(lv); + unsigned int oldTopIndex = ListView_GetTopIndex(lv); + unsigned int oldSelectionIndex = ListView_GetSelectionMark(lv); + unsigned int oldTopAddr = CALL_WITH_T_SIZE_TYPES(GetHardwareAddressFromItemIndex, rs_last_type_size,rs_t=='s',rs_last_no_misalign, oldTopIndex); + unsigned int oldSelectionAddr = CALL_WITH_T_SIZE_TYPES(GetHardwareAddressFromItemIndex, rs_last_type_size,rs_t=='s',rs_last_no_misalign, oldSelectionIndex); + + std::vector selHardwareAddrs; + if(numberOfItemsChanged) + { + // store selection ranges + // unfortunately this can take a while if the user has a huge range of items selected +// Clear_Sound_Buffer(); + int selCount = ListView_GetSelectedCount(lv); + int size = (rs_last_type_size=='b' || !rs_last_no_misalign) ? 1 : 2; + int watchIndex = -1; + for(int i = 0; i < selCount; ++i) + { + watchIndex = ListView_GetNextItem(lv, watchIndex, LVNI_SELECTED); + int addr = CALL_WITH_T_SIZE_TYPES(GetHardwareAddressFromItemIndex, rs_last_type_size,rs_t=='s',rs_last_no_misalign, watchIndex); + if(!selHardwareAddrs.empty() && addr == selHardwareAddrs.back().End()) + selHardwareAddrs.back().size += size; + else + selHardwareAddrs.push_back(AddrRange(addr,size)); + } + } + + CompactAddrs(); + + rs_last_type_size = rs_type_size; + rs_last_no_misalign = noMisalign; + + if(numberOfItemsChanged) + { + // restore selection ranges + unsigned int newTopIndex = CALL_WITH_T_SIZE_TYPES(HardwareAddressToItemIndex, rs_type_size,rs_t=='s',noMisalign, oldTopAddr); + unsigned int newBottomIndex = newTopIndex + itemsPerPage - 1; + SendMessage(lv, WM_SETREDRAW, FALSE, 0); + ListView_SetItemState(lv, -1, 0, LVIS_SELECTED|LVIS_FOCUSED); // deselect all + for(unsigned int i = 0; i < selHardwareAddrs.size(); i++) + { + // calculate index ranges of this selection + const AddrRange& range = selHardwareAddrs[i]; + int selRangeTop = CALL_WITH_T_SIZE_TYPES(HardwareAddressToItemIndex, rs_type_size,rs_t=='s',noMisalign, range.addr); + int selRangeBottom = -1; + for(int endAddr = range.End()-1; endAddr >= selRangeTop && selRangeBottom == -1; endAddr--) + selRangeBottom = CALL_WITH_T_SIZE_TYPES(HardwareAddressToItemIndex, rs_type_size,rs_t=='s',noMisalign, endAddr); + if(selRangeBottom == -1) + selRangeBottom = selRangeTop; + if(selRangeTop == -1) + continue; + + // select the entire range at once without deselecting the other ranges + // looks hacky but it works, and the only documentation I found on how to do this was blatantly false and equally hacky anyway + POINT pos; + ListView_EnsureVisible(lv, selRangeTop, 0); + ListView_GetItemPosition(lv, selRangeTop, &pos); + SendMessage(lv, WM_LBUTTONDOWN, MK_LBUTTON|MK_CONTROL, MAKELONG(pos.x,pos.y)); + ListView_EnsureVisible(lv, selRangeBottom, 0); + ListView_GetItemPosition(lv, selRangeBottom, &pos); + SendMessage(lv, WM_LBUTTONDOWN, MK_LBUTTON|MK_CONTROL|MK_SHIFT, MAKELONG(pos.x,pos.y)); + } + + // restore previous scroll position + if(newBottomIndex != -1) + ListView_EnsureVisible(lv, newBottomIndex, 0); + if(newTopIndex != -1) + ListView_EnsureVisible(lv, newTopIndex, 0); + + SendMessage(lv, WM_SETREDRAW, TRUE, 0); + + RefreshRamListSelectedCountControlStatus(RamSearchHWnd); + + EnableWindow(GetDlgItem(RamSearchHWnd,IDC_MISALIGN), rs_type_size != 'b'); + } + else + { + ListView_Update(lv, -1); + } +} + + + + +LRESULT CustomDraw (LPARAM lParam) +{ + LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam; + + switch(lplvcd->nmcd.dwDrawStage) + { + case CDDS_PREPAINT : + return CDRF_NOTIFYITEMDRAW; + + case CDDS_ITEMPREPAINT: + { + int rv = CDRF_DODEFAULT; + + if(lplvcd->nmcd.dwItemSpec % 2) + { + // alternate the background color slightly + lplvcd->clrTextBk = RGB(248,248,255); + rv = CDRF_NEWFONT; + } + + if(!IsSatisfied(lplvcd->nmcd.dwItemSpec)) + { + // tint red any items that would be eliminated if a search were to run now + lplvcd->clrText = RGB(192,64,64); + rv = CDRF_NEWFONT; + } + + return rv; + } break; + } + return CDRF_DODEFAULT; +} + +//extern "C" int disableRamSearchUpdate; +void Update_RAM_Search() //keeps RAM values up to date in the search and watch windows +{ + +// if(disableRamSearchUpdate) +// return; + + if (AutoSearch && !ResultCount) + { + if(!AutoSearchAutoRetry) + { +// Clear_Sound_Buffer(); + int answer = MessageBox(RamSearchHWnd,"Choosing Retry will reset the search once and continue autosearching.\nChoose Ignore will reset the search whenever necessary and continue autosearching.\nChoosing Abort will reset the search once and stop autosearching.","Autosearch - out of results.",MB_ABORTRETRYIGNORE|MB_DEFBUTTON2|MB_ICONINFORMATION); + if(answer == IDABORT) + { + SendDlgItemMessage(RamSearchHWnd, IDC_C_AUTOSEARCH, BM_SETCHECK, BST_UNCHECKED, 0); + SendMessage(RamSearchHWnd, WM_COMMAND, IDC_C_AUTOSEARCH, 0); + } + if(answer == IDIGNORE) + AutoSearchAutoRetry = true; + } + reset_address_info(); + } + + int prevValuesNeededUpdate = s_prevValuesNeedUpdate; + if (RamSearchHWnd) + { + // update active RAM values + signal_new_frame(); + } + + if (AutoSearch && ResultCount) + { + //Clear_Sound_Buffer(); + if(!rs_val_valid) + rs_val_valid = Set_RS_Val(); + if(rs_val_valid) + prune(rs_c,rs_o,rs_t=='s',rs_val,rs_param); + } + + if(RamSearchHWnd) + { + HWND lv = GetDlgItem(RamSearchHWnd,IDC_RAMLIST); + if(prevValuesNeededUpdate != s_prevValuesNeedUpdate) + { + // previous values got updated, refresh everything visible + ListView_Update(lv, -1); + } + else + { + // refresh any visible parts of the listview box that changed + static int changes[128]; + int top = ListView_GetTopIndex(lv); + int count = ListView_GetCountPerPage(lv); + int start = -1; + for(int i = top; i <= top+count; i++) + { + int changeNum = CALL_WITH_T_SIZE_TYPES(GetNumChangesFromItemIndex, rs_type_size,rs_t=='s',noMisalign, i); //s_numChanges[i]; + int changed = changeNum != changes[i-top]; + if(changed) + changes[i-top] = changeNum; + + if(start == -1) + { + if(i != top+count && changed) + { + start = i; + //somethingChanged = true; + } + } + else + { + if(i == top+count || !changed) + { + ListView_RedrawItems(lv, start, i-1); + start = -1; + } + } + } + } + } + + if(RamWatchHWnd) + { + Update_RAM_Watch(); + } +} + +static int rs_lastPercent = -1; +inline void UpdateRamSearchProgressBar(int percent) +{ + if(rs_lastPercent != percent) + { + rs_lastPercent = percent; + UpdateRamSearchTitleBar(percent); + } +} + +static void SelectEditControl(int controlID) +{ + HWND hEdit = GetDlgItem(RamSearchHWnd,controlID); + SetFocus(hEdit); + SendMessage(hEdit, EM_SETSEL, 0, -1); +} + +static BOOL SelectingByKeyboard() +{ + int a = GetKeyState(VK_LEFT); + int b = GetKeyState(VK_RIGHT); + int c = GetKeyState(VK_UP); + int d = GetKeyState(VK_DOWN); // space and tab are intentionally omitted + return (a | b | c | d) & 0x80; +} + +extern void init_list_box(HWND Box, const char* Strs[], int numColumns, int *columnWidths); + +LRESULT CALLBACK RamSearchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + RECT r; + RECT r2; + int dx1, dy1, dx2, dy2; + static int watchIndex=0; + + switch(uMsg) + { + case WM_INITDIALOG: { + RamSearchHWnd = hDlg; +/* if (Full_Screen) + { + while (ShowCursor(false) >= 0); + while (ShowCursor(true) < 0); + }*/ + + GetWindowRect(MainWindow->getHWnd(), &r); + dx1 = (r.right - r.left) / 2; + dy1 = (r.bottom - r.top) / 2; + + GetWindowRect(hDlg, &r2); + dx2 = (r2.right - r2.left) / 2; + dy2 = (r2.bottom - r2.top) / 2; + + // push it away from the main window if we can + const int width = (r.right-r.left); + const int width2 = (r2.right-r2.left); + if(r.left+width2 + width < GetSystemMetrics(SM_CXSCREEN)) + { + r.right += width; + r.left += width; + } + else if((int)r.left - (int)width2 > 0) + { + r.right -= width2; + r.left -= width2; + } + + SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + switch(rs_o) + { + case '<': + SendDlgItemMessage(hDlg, IDC_LESSTHAN, BM_SETCHECK, BST_CHECKED, 0); + break; + case '>': + SendDlgItemMessage(hDlg, IDC_MORETHAN, BM_SETCHECK, BST_CHECKED, 0); + break; + case 'l': + SendDlgItemMessage(hDlg, IDC_NOMORETHAN, BM_SETCHECK, BST_CHECKED, 0); + break; + case 'm': + SendDlgItemMessage(hDlg, IDC_NOLESSTHAN, BM_SETCHECK, BST_CHECKED, 0); + break; + case '=': + SendDlgItemMessage(hDlg, IDC_EQUALTO, BM_SETCHECK, BST_CHECKED, 0); + break; + case '!': + SendDlgItemMessage(hDlg, IDC_DIFFERENTFROM, BM_SETCHECK, BST_CHECKED, 0); + break; + case 'd': + SendDlgItemMessage(hDlg, IDC_DIFFERENTBY, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),true); + break; + case '%': + SendDlgItemMessage(hDlg, IDC_MODULO, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),true); + break; + } + switch (rs_c) + { + case 'r': + SendDlgItemMessage(hDlg, IDC_PREVIOUSVALUE, BM_SETCHECK, BST_CHECKED, 0); + break; + case 's': + SendDlgItemMessage(hDlg, IDC_SPECIFICVALUE, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREVALUE),true); + break; + case 'a': + SendDlgItemMessage(hDlg, IDC_SPECIFICADDRESS, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREADDRESS),true); + break; + case 'n': + SendDlgItemMessage(hDlg, IDC_NUMBEROFCHANGES, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPARECHANGES),true); + break; + } + switch (rs_t) + { + case 's': + SendDlgItemMessage(hDlg, IDC_SIGNED, BM_SETCHECK, BST_CHECKED, 0); + break; + case 'u': + SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_SETCHECK, BST_CHECKED, 0); + break; + case 'h': + SendDlgItemMessage(hDlg, IDC_HEX, BM_SETCHECK, BST_CHECKED, 0); + break; + } + switch (rs_type_size) + { + case 'b': + SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_SETCHECK, BST_CHECKED, 0); + break; + case 'w': + SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_SETCHECK, BST_CHECKED, 0); + break; + case 'd': + SendDlgItemMessage(hDlg, IDC_4_BYTES, BM_SETCHECK, BST_CHECKED, 0); + break; + } + + s_prevValuesNeedUpdate = true; + + SendDlgItemMessage(hDlg,IDC_C_AUTOSEARCH,BM_SETCHECK,AutoSearch?BST_CHECKED:BST_UNCHECKED,0); + //const char* names[5] = {"Address","Value","Previous","Changes","Notes"}; + //int widths[5] = {62,64,64,55,55}; + const char* names[] = {"Address","Value","Previous","Changes"}; + int widths[4] = {68,76,76,68}; + if (!ResultCount) + reset_address_info(); + else + { + signal_new_frame(); + CompactAddrs(); + } + init_list_box(GetDlgItem(hDlg,IDC_RAMLIST),names,4,widths); + //ListView_SetItemCount(GetDlgItem(hDlg,IDC_RAMLIST),ResultCount); + if (!noMisalign) SendDlgItemMessage(hDlg, IDC_MISALIGN, BM_SETCHECK, BST_CHECKED, 0); +// if (littleEndian) SendDlgItemMessage(hDlg, IDC_ENDIAN, BM_SETCHECK, BST_CHECKED, 0); + last_rs_possible = -1; + RefreshRamListSelectedCountControlStatus(hDlg); + + // force misalign checkbox to refresh + signal_new_size(); + + // force undo button to refresh + int undoType = s_undoType; + SetRamSearchUndoType(hDlg, -2); + SetRamSearchUndoType(hDlg, undoType); + + // force possibility count to refresh + last_rs_possible--; + UpdatePossibilities(ResultCount, (int)s_activeMemoryRegions.size()); + + rs_val_valid = Set_RS_Val(); + + ListView_SetCallbackMask(GetDlgItem(hDlg,IDC_RAMLIST), LVIS_FOCUSED|LVIS_SELECTED); + + return true; + } break; + + case WM_NOTIFY: + { + LPNMHDR lP = (LPNMHDR) lParam; + switch (lP->code) + { + case LVN_ITEMCHANGED: // selection changed event + { + NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)lP; + if(pNMListView->uNewState & LVIS_FOCUSED) + { + // disable buttons that we don't have the right number of selected items for + RefreshRamListSelectedCountControlStatus(hDlg); + } + } break; + + case LVN_GETDISPINFO: + { + LV_DISPINFO *Item = (LV_DISPINFO *)lParam; + Item->item.mask = LVIF_TEXT; + Item->item.state = 0; + Item->item.iImage = 0; + const unsigned int iNum = Item->item.iItem; + static char num[11]; + switch (Item->item.iSubItem) + { + case 0: + { + int addr = CALL_WITH_T_SIZE_TYPES(GetHardwareAddressFromItemIndex, rs_type_size,rs_t=='s',noMisalign, iNum); + sprintf(num,"%08X",addr); + Item->item.pszText = num; + } return true; + case 1: + { + int i = CALL_WITH_T_SIZE_TYPES(GetCurValueFromItemIndex, rs_type_size,rs_t=='s',noMisalign, iNum); + const char* formatString = ((rs_t=='s') ? "%d" : (rs_t=='u') ? "%u" : (rs_type_size=='d' ? "%08X" : rs_type_size=='w' ? "%04X" : "%02X")); + switch (rs_type_size) + { + case 'b': + default: sprintf(num, formatString, rs_t=='s' ? (char)(i&0xff) : (unsigned char)(i&0xff)); break; + case 'w': sprintf(num, formatString, rs_t=='s' ? (short)(i&0xffff) : (unsigned short)(i&0xffff)); break; + case 'd': sprintf(num, formatString, rs_t=='s' ? (long)(i&0xffffffff) : (unsigned long)(i&0xffffffff)); break; + } + Item->item.pszText = num; + } return true; + case 2: + { + int i = CALL_WITH_T_SIZE_TYPES(GetPrevValueFromItemIndex, rs_type_size,rs_t=='s',noMisalign, iNum); + const char* formatString = ((rs_t=='s') ? "%d" : (rs_t=='u') ? "%u" : (rs_type_size=='d' ? "%08X" : rs_type_size=='w' ? "%04X" : "%02X")); + switch (rs_type_size) + { + case 'b': + default: sprintf(num, formatString, rs_t=='s' ? (char)(i&0xff) : (unsigned char)(i&0xff)); break; + case 'w': sprintf(num, formatString, rs_t=='s' ? (short)(i&0xffff) : (unsigned short)(i&0xffff)); break; + case 'd': sprintf(num, formatString, rs_t=='s' ? (long)(i&0xffffffff) : (unsigned long)(i&0xffffffff)); break; + } + Item->item.pszText = num; + } return true; + case 3: + { + int i = CALL_WITH_T_SIZE_TYPES(GetNumChangesFromItemIndex, rs_type_size,rs_t=='s',noMisalign, iNum); + sprintf(num,"%d",i); + + Item->item.pszText = num; + } return true; + //case 4: + // Item->item.pszText = rsaddrs[rsresults[iNum].Index].comment ? rsaddrs[rsresults[iNum].Index].comment : ""; + // return true; + default: + return false; + } + } + + case NM_CUSTOMDRAW: + { + SetWindowLong(hDlg, DWL_MSGRESULT, CustomDraw(lParam)); + return TRUE; + } break; + + //case LVN_ODCACHEHINT: //Copied this bit from the MSDN virtual listbox code sample. Eventually it should probably do something. + //{ + // LPNMLVCACHEHINT lpCacheHint = (LPNMLVCACHEHINT)lParam; + // return 0; + //} + //case LVN_ODFINDITEM: //Copied this bit from the MSDN virtual listbox code sample. Eventually it should probably do something. + //{ + // LPNMLVFINDITEM lpFindItem = (LPNMLVFINDITEM)lParam; + // return 0; + //} + } + } break; + + case WM_COMMAND: + { + int rv = false; + switch(LOWORD(wParam)) + { + case IDC_SIGNED: + rs_t='s'; + signal_new_size(); + {rv = true; break;} + case IDC_UNSIGNED: + rs_t='u'; + signal_new_size(); + {rv = true; break;} + case IDC_HEX: + rs_t='h'; + signal_new_size(); + {rv = true; break;} + case IDC_1_BYTE: + rs_type_size = 'b'; + signal_new_size(); + {rv = true; break;} + case IDC_2_BYTES: + rs_type_size = 'w'; + signal_new_size(); + {rv = true; break;} + case IDC_4_BYTES: + rs_type_size = 'd'; + signal_new_size(); + {rv = true; break;} + case IDC_MISALIGN: + noMisalign = !noMisalign; + //CompactAddrs(); + signal_new_size(); + {rv = true; break;} +// case IDC_ENDIAN: +// littleEndian = !littleEndian; +// signal_new_size(); + {rv = true; break;} + case IDC_LESSTHAN: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = '<'; + {rv = true; break;} + case IDC_MORETHAN: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = '>'; + {rv = true; break;} + case IDC_NOMORETHAN: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = 'l'; + {rv = true; break;} + case IDC_NOLESSTHAN: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = 'm'; + {rv = true; break;} + case IDC_EQUALTO: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = '='; + {rv = true; break;} + case IDC_DIFFERENTFROM: + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + rs_o = '!'; + {rv = true; break;} + case IDC_DIFFERENTBY: + { + rs_o = 'd'; + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),true); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),false); + if(!SelectingByKeyboard()) + SelectEditControl(IDC_EDIT_DIFFBY); + } {rv = true; break;} + case IDC_MODULO: + { + rs_o = '%'; + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_DIFFBY),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_MODBY),true); + if(!SelectingByKeyboard()) + SelectEditControl(IDC_EDIT_MODBY); + } {rv = true; break;} + case IDC_PREVIOUSVALUE: + rs_c='r'; + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREVALUE),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREADDRESS),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPARECHANGES),false); + {rv = true; break;} + case IDC_SPECIFICVALUE: + { + rs_c = 's'; + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREVALUE),true); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREADDRESS),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPARECHANGES),false); + if(!SelectingByKeyboard()) + SelectEditControl(IDC_EDIT_COMPAREVALUE); + {rv = true; break;} + } + case IDC_SPECIFICADDRESS: + { + rs_c = 'a'; + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREADDRESS),true); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREVALUE),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPARECHANGES),false); + if(!SelectingByKeyboard()) + SelectEditControl(IDC_EDIT_COMPAREADDRESS); + } {rv = true; break;} + case IDC_NUMBEROFCHANGES: + { + rs_c = 'n'; + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPARECHANGES),true); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREVALUE),false); + EnableWindow(GetDlgItem(hDlg,IDC_EDIT_COMPAREADDRESS),false); + if(!SelectingByKeyboard()) + SelectEditControl(IDC_EDIT_COMPARECHANGES); + } {rv = true; break;} + case IDC_C_ADDCHEAT: + { +// watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_RAMLIST)); +// Liste_GG[CheatCount].restore = Liste_GG[CheatCount].data = rsresults[watchIndex].cur; +// Liste_GG[CheatCount].addr = rsresults[watchIndex].Address; +// Liste_GG[CheatCount].size = rs_type_size; +// Liste_GG[CheatCount].Type = rs_t; +// Liste_GG[CheatCount].oper = '='; +// Liste_GG[CheatCount].mode = 0; +// DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_EDITCHEAT), hDlg, (DLGPROC) EditCheatProc,(LPARAM) 0); + } + case IDC_C_RESET: + { + RamSearchSaveUndoStateIfNotTooBig(RamSearchHWnd); + int prevNumItems = last_rs_possible; + + soft_reset_address_info(); + + if(prevNumItems == last_rs_possible) + SetRamSearchUndoType(RamSearchHWnd, 0); // nothing to undo + + ListView_SetItemState(GetDlgItem(hDlg,IDC_RAMLIST), -1, 0, LVIS_SELECTED); // deselect all + //ListView_SetItemCount(GetDlgItem(hDlg,IDC_RAMLIST),ResultCount); + ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_RAMLIST), 0); + RefreshRamListSelectedCountControlStatus(hDlg); + {rv = true; break;} + } + case IDC_C_RESET_CHANGES: + memset(s_numChanges, 0, sizeof(s_numChanges)); + ListView_Update(GetDlgItem(hDlg,IDC_RAMLIST), -1); + //SetRamSearchUndoType(hDlg, 0); + {rv = true; break;} + case IDC_C_UNDO: + if(s_undoType>0) + { +// Clear_Sound_Buffer(); + if(s_activeMemoryRegions.size() < tooManyRegionsForUndo) + { + MemoryList tempMemoryList = s_activeMemoryRegions; + s_activeMemoryRegions = s_activeMemoryRegionsBackup; + s_activeMemoryRegionsBackup = tempMemoryList; + SetRamSearchUndoType(hDlg, 3 - s_undoType); + } + else + { + s_activeMemoryRegions = s_activeMemoryRegionsBackup; + SetRamSearchUndoType(hDlg, -1); + } + CompactAddrs(); + ListView_SetItemState(GetDlgItem(hDlg,IDC_RAMLIST), -1, 0, LVIS_SELECTED); // deselect all + ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_RAMLIST), 0); + RefreshRamListSelectedCountControlStatus(hDlg); + } + {rv = true; break;} + case IDC_C_AUTOSEARCH: + AutoSearch = SendDlgItemMessage(hDlg, IDC_C_AUTOSEARCH, BM_GETCHECK, 0, 0) != 0; + AutoSearchAutoRetry = false; + if (!AutoSearch) {rv = true; break;} + case IDC_C_SEARCH: + { +// Clear_Sound_Buffer(); + + if(!rs_val_valid && !(rs_val_valid = Set_RS_Val())) + goto invalid_field; + + if(ResultCount) + { + RamSearchSaveUndoStateIfNotTooBig(hDlg); + + prune(rs_c,rs_o,rs_t=='s',rs_val,rs_param); + + RefreshRamListSelectedCountControlStatus(hDlg); + } + + if(!ResultCount) + { + + MessageBox(RamSearchHWnd,"Resetting search.","Out of results.",MB_OK|MB_ICONINFORMATION); + soft_reset_address_info(); + } + + {rv = true; break;} + +invalid_field: + MessageBox(RamSearchHWnd,"Invalid or out-of-bound entered value.","Error",MB_OK|MB_ICONSTOP); + if(AutoSearch) // stop autosearch if it just started + { + SendDlgItemMessage(hDlg, IDC_C_AUTOSEARCH, BM_SETCHECK, BST_UNCHECKED, 0); + SendMessage(hDlg, WM_COMMAND, IDC_C_AUTOSEARCH, 0); + } + {rv = true; break;} + } + case IDC_C_WATCH: + { + int watchItemIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_RAMLIST)); + if(watchItemIndex >= 0) + { + AddressWatcher tempWatch; + tempWatch.Address = CALL_WITH_T_SIZE_TYPES(GetHardwareAddressFromItemIndex, rs_type_size,rs_t=='s',noMisalign, watchItemIndex); + tempWatch.Size = rs_type_size; + tempWatch.Type = rs_t; + tempWatch.WrongEndian = 0; //Replace when I get little endian working + tempWatch.comment = NULL; + + bool inserted = InsertWatch(tempWatch, hDlg); + //ListView_Update(GetDlgItem(hDlg,IDC_RAMLIST), -1); + + // bring up the ram watch window if it's not already showing so the user knows where the watch went + if(inserted && !RamWatchHWnd) + SendMessage(MainWindow->getHWnd(), WM_COMMAND, ID_RAM_WATCH, 0); + SetForegroundWindow(RamSearchHWnd); + } + {rv = true; break;} + } + + // eliminate all selected items + case IDC_C_ELIMINATE: + { + RamSearchSaveUndoStateIfNotTooBig(hDlg); + + HWND ramListControl = GetDlgItem(hDlg,IDC_RAMLIST); + int size = (rs_type_size=='b' || !noMisalign) ? 1 : 2; + int selCount = ListView_GetSelectedCount(ramListControl); + int watchIndex = -1; + + // time-saving trick #1: + // condense the selected items into an array of address ranges + std::vector selHardwareAddrs; + for(int i = 0, j = 1024; i < selCount; ++i, --j) + { + watchIndex = ListView_GetNextItem(ramListControl, watchIndex, LVNI_SELECTED); + int addr = CALL_WITH_T_SIZE_TYPES(GetHardwareAddressFromItemIndex, rs_type_size,rs_t=='s',noMisalign, watchIndex); + if(!selHardwareAddrs.empty() && addr == selHardwareAddrs.back().End()) + selHardwareAddrs.back().size += size; + else + selHardwareAddrs.push_back(AddrRange(addr,size)); + + if(!j) UpdateRamSearchProgressBar(i * 50 / selCount), j = 1024; + } + + // now deactivate the ranges + + // time-saving trick #2: + // take advantage of the fact that the listbox items must be in the same order as the regions + MemoryList::iterator iter = s_activeMemoryRegions.begin(); + int numHardwareAddrRanges = selHardwareAddrs.size(); + for(int i = 0, j = 16; i < numHardwareAddrRanges; ++i, --j) + { + int addr = selHardwareAddrs[i].addr; + int size = selHardwareAddrs[i].size; + bool affected = false; + while(iter != s_activeMemoryRegions.end()) + { + MemoryRegion& region = *iter; + int affNow = DeactivateRegion(region, iter, addr, size); + if(affNow) + affected = true; + else if(affected) + break; + if(affNow != 2) + ++iter; + } + + if(!j) UpdateRamSearchProgressBar(50 + (i * 50 / selCount)), j = 16; + } + UpdateRamSearchTitleBar(); + + // careful -- if the above two time-saving tricks aren't working, + // the runtime can absolutely explode (seconds -> hours) when there are lots of regions + + ListView_SetItemState(ramListControl, -1, 0, LVIS_SELECTED); // deselect all + signal_new_size(); + {rv = true; break;} + } + //case IDOK: + case IDCANCEL: +/* if (Full_Screen) + { + while (ShowCursor(true) < 0); + while (ShowCursor(false) >= 0); + }*/ +// DialogsOpen--; + RamSearchHWnd = NULL; + EndDialog(hDlg, true); + {rv = true; break;} + } + + // check refresh for comparison preview color update + // also, update rs_val if needed + bool needRefresh = false; + switch(LOWORD(wParam)) + { + case IDC_LESSTHAN: + case IDC_MORETHAN: + case IDC_NOMORETHAN: + case IDC_NOLESSTHAN: + case IDC_EQUALTO: + case IDC_DIFFERENTFROM: + case IDC_DIFFERENTBY: + case IDC_MODULO: + case IDC_PREVIOUSVALUE: + case IDC_SPECIFICVALUE: + case IDC_SPECIFICADDRESS: + case IDC_NUMBEROFCHANGES: + case IDC_SIGNED: + case IDC_UNSIGNED: + case IDC_HEX: + rs_val_valid = Set_RS_Val(); + needRefresh = true; + break; + case IDC_EDIT_COMPAREVALUE: + case IDC_EDIT_COMPAREADDRESS: + case IDC_EDIT_COMPARECHANGES: + case IDC_EDIT_DIFFBY: + case IDC_EDIT_MODBY: + if(HIWORD(wParam) == EN_CHANGE) + { + rs_val_valid = Set_RS_Val(); + needRefresh = true; + } + break; + } + if(needRefresh) + ListView_Update(GetDlgItem(hDlg,IDC_RAMLIST), -1); + + + return rv; + } break; + + case WM_CLOSE: +/* if (Full_Screen) + { + while (ShowCursor(true) < 0); + while (ShowCursor(false) >= 0); + } + DialogsOpen--;*/ + RamSearchHWnd = NULL; + EndDialog(hDlg, true); + return true; + } + + return false; +} + +void UpdateRamSearchTitleBar(int percent) +{ +#define HEADER_STR " RAM Search - " +#define PROGRESS_STR " %d%% ... " +#define STATUS_STR "%d Possibilit%s (%d Region%s)" + + int poss = last_rs_possible; + int regions = last_rs_regions; + if(poss <= 0) + strcpy(Str_Tmp," RAM Search"); + else if(percent <= 0) + sprintf(Str_Tmp, HEADER_STR STATUS_STR, poss, poss==1?"y":"ies", regions, regions==1?"":"s"); + else + sprintf(Str_Tmp, PROGRESS_STR STATUS_STR, percent, poss, poss==1?"y":"ies", regions, regions==1?"":"s"); + SetWindowText(RamSearchHWnd, Str_Tmp); +} + +void UpdatePossibilities(int rs_possible, int regions) +{ + if(rs_possible != last_rs_possible) + { + last_rs_possible = rs_possible; + last_rs_regions = regions; + UpdateRamSearchTitleBar(); + } +} + +void SetRamSearchUndoType(HWND hDlg, int type) +{ + if(s_undoType != type) + { + if((s_undoType!=2 && s_undoType!=-1)!=(type!=2 && type!=-1)) + SendDlgItemMessage(hDlg,IDC_C_UNDO,WM_SETTEXT,0,(LPARAM)((type == 2 || type == -1) ? "Redo" : "Undo")); + if((s_undoType>0)!=(type>0)) + EnableWindow(GetDlgItem(hDlg,IDC_C_UNDO),type>0); + s_undoType = type; + } +} + +void RamSearchSaveUndoStateIfNotTooBig(HWND hDlg) +{ + if(s_activeMemoryRegions.size() < tooManyRegionsForUndo) + { + s_activeMemoryRegionsBackup = s_activeMemoryRegions; + SetRamSearchUndoType(hDlg, 1); + } + else + { + SetRamSearchUndoType(hDlg, 0); + } +} diff --git a/desmume/src/windows/ram_search.h b/desmume/src/windows/ram_search.h new file mode 100644 index 000000000..bdf14e407 --- /dev/null +++ b/desmume/src/windows/ram_search.h @@ -0,0 +1,49 @@ +#ifndef RAM_SEARCH_H +#define RAM_SEARCH_H + + +//64k in Ram_68k[], 8k in Ram_Z80[] +//#define _68K_RAM_SIZE 64*1024 +//#define Z80_RAM_SIZE 8*1024 +/*#define SRAM_SIZE (((SRAM_End - SRAM_Start) > 2) ? SRAM_End - SRAM_Start : 0) +#define BRAM_SIZE ((8 << BRAM_Ex_Size) * 1024)*/ +//#define GENESIS_RAM_SIZE (_68K_RAM_SIZE + Z80_RAM_SIZE) + +//_32X_Ram[] +//#define _32X_RAM_SIZE 256*1024 + +//512k in Ram_Prg, 256k in Ram_Word_1M and Ram_Word_2M +//(docs say 6Mbit of ram, but I'm not sure what's used when) +//#define SEGACD_RAM_PRG_SIZE 512*1024 +//#define SEGACD_1M_RAM_SIZE 256*1024 +//#define SEGACD_2M_RAM_SIZE 256*1024 +//#define SEGACD_RAM_SIZE (SEGACD_RAM_PRG_SIZE + SEGACD_2M_RAM_SIZE) + + +//#define MAX_RAM_SIZE (0x112000) +//#define MAX_RAM_SIZE (0xD2000) +#define MAX_RAM_SIZE (0x400000) + +extern char rs_type_size; +extern int ResultCount; + +unsigned int sizeConv(unsigned int index,char size, char *prevSize = &rs_type_size, bool usePrev = false); +unsigned int GetRamValue(unsigned int Addr,char Size); +void prune(char Search, char Operater, char Type, int Value, int OperatorParameter); +void CompactAddrs(); +void reset_address_info(); +void signal_new_frame(); +void signal_new_size(); +void UpdateRamSearchTitleBar(int percent = 0); +void Update_RAM_Search(); +void SetRamSearchUndoType(HWND hDlg, int type); +unsigned int ReadValueAtHardwareAddress(unsigned int address, unsigned int size); +bool WriteValueAtHardwareRAMAddress(unsigned int address, unsigned int value, unsigned int size, bool hookless=false); +bool IsHardwareRAMAddressValid(unsigned int address); +extern int curr_ram_size; +extern bool noMisalign; +extern bool littleEndian; + + +#endif + diff --git a/desmume/src/windows/ramwatch.cpp b/desmume/src/windows/ramwatch.cpp new file mode 100644 index 000000000..3b9461e39 --- /dev/null +++ b/desmume/src/windows/ramwatch.cpp @@ -0,0 +1,1172 @@ + +#include "resource.h" +#include "common.h" +#include "NDSSystem.h" +#include "ramwatch.h" +#include "ram_search.h" +#include +#include +#include +#include + +HWND RamWatchHWnd; + +char Str_Tmp[1024]; +char Rom_Name[64] = "test"; + +static HMENU ramwatchmenu; +static HMENU rwrecentmenu; +static HACCEL RamWatchAccels = NULL; +char rw_recent_files[MAX_RECENT_WATCHES][1024]; +char Watch_Dir[1024]=""; +const unsigned int RW_MENU_FIRST_RECENT_FILE = 600; +bool RWfileChanged = false; //Keeps track of whether the current watch file has been changed, if so, ramwatch will prompt to save changes +bool AutoRWLoad = false; //Keeps track of whether Auto-load is checked +bool RWSaveWindowPos = false; //Keeps track of whether Save Window position is checked +char currentWatch[1024]; +int ramw_x, ramw_y; //Used to store ramwatch dialog window positions +AddressWatcher rswatches[MAX_WATCH_COUNT]; +int WatchCount=0; + +#define MESSAGEBOXPARENT (RamWatchHWnd ? RamWatchHWnd : MainWindow->getHWnd()) + +bool QuickSaveWatches(); +bool ResetWatches(); +//extern "C" int Clear_Sound_Buffer(void); + +unsigned int GetCurrentValue(AddressWatcher& watch) +{ + char buf[4]; + MMU_DumpMemBlock(0, watch.Address, 4, (u8*)buf); + + u32 val_u32 = *(u32*)buf; + u16 val_u16 = *(u16*)buf; + u8 val_u8 = *(u8*)buf; + + switch (watch.Size) + { + case 0x62: return val_u8; + case 0x77: return val_u16; + case 0x64: return val_u32; + default: return 0; + } +// return ReadValueAtHardwareAddress(watch.Address, watch.Size == 'd' ? 4 : watch.Size == 'w' ? 2 : 1); +} + +bool IsSameWatch(const AddressWatcher& l, const AddressWatcher& r) +{ + return ((l.Address == r.Address) && (l.Size == r.Size) && (l.Type == r.Type)/* && (l.WrongEndian == r.WrongEndian)*/); +} + +bool VerifyWatchNotAlreadyAdded(const AddressWatcher& watch) +{ + for (int j = 0; j < WatchCount; j++) + { + if (IsSameWatch(rswatches[j], watch)) + { + if(RamWatchHWnd) + SetForegroundWindow(RamWatchHWnd); + return false; + } + } + return true; +} + + +bool InsertWatch(const AddressWatcher& Watch, char *Comment) +{ + if(!VerifyWatchNotAlreadyAdded(Watch)) + return false; + + if(WatchCount >= MAX_WATCH_COUNT) + return false; + + int i = WatchCount++; + AddressWatcher& NewWatch = rswatches[i]; + NewWatch = Watch; + //if (NewWatch.comment) free(NewWatch.comment); + NewWatch.comment = (char *) malloc(strlen(Comment)+2); + NewWatch.CurValue = GetCurrentValue(NewWatch); + strcpy(NewWatch.comment, Comment); + ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount); + RWfileChanged=true; + + return true; +} + +LRESULT CALLBACK PromptWatchNameProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) //Gets the description of a watched address +{ + RECT r; + RECT r2; + int dx1, dy1, dx2, dy2; + + switch(uMsg) + { + case WM_INITDIALOG: +// Clear_Sound_Buffer(); + +/* if (Full_Screen) + { + while (ShowCursor(false) >= 0); + while (ShowCursor(true) < 0); + }*/ + + GetWindowRect(MainWindow->getHWnd(), &r); + dx1 = (r.right - r.left) / 2; + dy1 = (r.bottom - r.top) / 2; + + GetWindowRect(hDlg, &r2); + dx2 = (r2.right - r2.left) / 2; + dy2 = (r2.bottom - r2.top) / 2; + + //SetWindowPos(hDlg, NULL, max(0, r.left + (dx1 - dx2)), max(0, r.top + (dy1 - dy2)), NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + strcpy(Str_Tmp,"Enter a name for this RAM address."); + // SendDlgItemMessage(hDlg,IDC_PROMPT_TEXT,WM_SETTEXT,0,(LPARAM)Str_Tmp); + strcpy(Str_Tmp,""); + // SendDlgItemMessage(hDlg,IDC_PROMPT_TEXT2,WM_SETTEXT,0,(LPARAM)Str_Tmp); + return true; + break; + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDOK: + { +/* if (Full_Screen) + { + while (ShowCursor(true) < 0); + while (ShowCursor(false) >= 0); + }*/ + GetDlgItemText(hDlg,IDC_PROMPT_EDIT,Str_Tmp,80); + InsertWatch(rswatches[WatchCount],Str_Tmp); +// DialogsOpen--; + EndDialog(hDlg, true); + return true; + break; + } + // case ID_CANCEL: + case IDCANCEL: +/* if (Full_Screen) + { + while (ShowCursor(true) < 0); + while (ShowCursor(false) >= 0); + }*/ + +// DialogsOpen--; + EndDialog(hDlg, false); + return false; + break; + } + break; + + case WM_CLOSE: +/* if (Full_Screen) + { + while (ShowCursor(true) < 0); + while (ShowCursor(false) >= 0); + }*/ +// DialogsOpen--; + EndDialog(hDlg, false); + return false; + break; + } + + return false; +} + +bool InsertWatch(const AddressWatcher& Watch, HWND parent) +{ + if(!VerifyWatchNotAlreadyAdded(Watch)) + return false; + + if(!parent) + parent = RamWatchHWnd; + if(!parent) + parent = MainWindow->getHWnd(); + + int prevWatchCount = WatchCount; + + rswatches[WatchCount] = Watch; + rswatches[WatchCount].CurValue = GetCurrentValue(rswatches[WatchCount]); +// DialogBox(hAppInst, MAKEINTRESOURCE(IDD_PROMPT), parent, (DLGPROC) PromptWatchNameProc); TODO + + return WatchCount > prevWatchCount; +} + +void Update_RAM_Watch() +{ + // update cached values and detect changes to displayed listview items + BOOL watchChanged[MAX_WATCH_COUNT] = {0}; + for(int i = 0; i < WatchCount; i++) + { + unsigned int prevCurValue = rswatches[i].CurValue; + unsigned int newCurValue = GetCurrentValue(rswatches[i]); + if(prevCurValue != newCurValue) + { + rswatches[i].CurValue = newCurValue; + watchChanged[i] = TRUE; + } + } + + // refresh any visible parts of the listview box that changed + HWND lv = GetDlgItem(RamWatchHWnd,IDC_WATCHLIST); + int top = ListView_GetTopIndex(lv); + int bottom = top + ListView_GetCountPerPage(lv) + 1; // +1 is so we will update a partially-displayed last item + if(top < 0) top = 0; + if(bottom > WatchCount) bottom = WatchCount; + int start = -1; + for(int i = top; i <= bottom; i++) + { + if(start == -1) + { + if(i != bottom && watchChanged[i]) + { + start = i; + //somethingChanged = true; + } + } + else + { + if(i == bottom || !watchChanged[i]) + { + ListView_RedrawItems(lv, start, i-1); + start = -1; + } + } + } +} + +bool AskSave() +{ + //This function asks to save changes if the watch file contents have changed + //returns false only if a save was attempted but failed or was cancelled + if (RWfileChanged) + { + int answer = MessageBox(MESSAGEBOXPARENT, "Save Changes?", "Ram Watch", MB_YESNOCANCEL); + if(answer == IDYES) + if(!QuickSaveWatches()) + return false; + return (answer != IDCANCEL); + } + return true; +} + + +void UpdateRW_RMenu(HMENU menu, unsigned int mitem, unsigned int baseid) +{ + MENUITEMINFO moo; + int x; + + moo.cbSize = sizeof(moo); + moo.fMask = MIIM_SUBMENU | MIIM_STATE; + + GetMenuItemInfo(GetSubMenu(ramwatchmenu, 0), mitem, FALSE, &moo); + moo.hSubMenu = menu; + moo.fState = strlen(rw_recent_files[0]) ? MFS_ENABLED : MFS_GRAYED; + + SetMenuItemInfo(GetSubMenu(ramwatchmenu, 0), mitem, FALSE, &moo); + + // Remove all recent files submenus + for(x = 0; x < MAX_RECENT_WATCHES; x++) + { + RemoveMenu(menu, baseid + x, MF_BYCOMMAND); + } + + // Recreate the menus + for(x = MAX_RECENT_WATCHES - 1; x >= 0; x--) + { + char tmp[128 + 5]; + + // Skip empty strings + if(!strlen(rw_recent_files[x])) + { + continue; + } + + moo.cbSize = sizeof(moo); + moo.fMask = MIIM_DATA | MIIM_ID | MIIM_TYPE; + + // Fill in the menu text. + if(strlen(rw_recent_files[x]) < 128) + { + sprintf(tmp, "&%d. %s", ( x + 1 ) % 10, rw_recent_files[x]); + } + else + { + sprintf(tmp, "&%d. %s", ( x + 1 ) % 10, rw_recent_files[x] + strlen( rw_recent_files[x] ) - 127); + } + + // Insert the menu item + moo.cch = strlen(tmp); + moo.fType = 0; + moo.wID = baseid + x; + moo.dwTypeData = tmp; + InsertMenuItem(menu, 0, 1, &moo); + } +} + +void UpdateRWRecentArray(const char* addString, unsigned int arrayLen, HMENU menu, unsigned int menuItem, unsigned int baseId) +{ + // Try to find out if the filename is already in the recent files list. + for(unsigned int x = 0; x < arrayLen; x++) + { + if(strlen(rw_recent_files[x])) + { + if(!strcmp(rw_recent_files[x], addString)) // Item is already in list. + { + // If the filename is in the file list don't add it again. + // Move it up in the list instead. + + int y; + char tmp[1024]; + + // Save pointer. + strcpy(tmp,rw_recent_files[x]); + + for(y = x; y; y--) + { + // Move items down. + strcpy(rw_recent_files[y],rw_recent_files[y - 1]); + } + + // Put item on top. + strcpy(rw_recent_files[0],tmp); + + // Update the recent files menu + UpdateRW_RMenu(menu, menuItem, baseId); + + return; + } + } + } + + // The filename wasn't found in the list. That means we need to add it. + + // Move the other items down. + for(unsigned int x = arrayLen - 1; x; x--) + { + strcpy(rw_recent_files[x],rw_recent_files[x - 1]); + } + + // Add the new item. + strcpy(rw_recent_files[0], addString); + + // Update the recent files menu + UpdateRW_RMenu(menu, menuItem, baseId); +} + + +void RWAddRecentFile(const char *filename) +{ +// UpdateRWRecentArray(filename, MAX_RECENT_WATCHES, rwrecentmenu, RAMMENU_FILE_RECENT, RW_MENU_FIRST_RECENT_FILE); +} + +void OpenRWRecentFile(int memwRFileNumber) +{ + if(!ResetWatches()) + return; + + int rnum = memwRFileNumber; + if ((unsigned int)rnum >= MAX_RECENT_WATCHES) + return; //just in case + + char* x; + + while(true) + { + x = rw_recent_files[rnum]; + if (!*x) + return; //If no recent files exist just return. Useful for Load last file on startup (or if something goes screwy) + + if (rnum) //Change order of recent files if not most recent + { + RWAddRecentFile(x); + rnum = 0; + } + else + { + break; + } + } + + strcpy(currentWatch,x); + strcpy(Str_Tmp,currentWatch); + + //loadwatches here + FILE *WatchFile = fopen(Str_Tmp,"rb"); + if (!WatchFile) + { + int answer = MessageBox(MESSAGEBOXPARENT,"Error opening file.","ERROR",MB_OKCANCEL); + if (answer == IDOK) + { + rw_recent_files[rnum][0] = '\0'; //Clear file from list + if (rnum) //Update the ramwatch list + RWAddRecentFile(rw_recent_files[0]); + else + RWAddRecentFile(rw_recent_files[1]); + } + return; + } + const char DELIM = '\t'; + AddressWatcher Temp; + char mode; + fgets(Str_Tmp,1024,WatchFile); + sscanf(Str_Tmp,"%c%*s",&mode); +/* if ((mode == '1' && !(SegaCD_Started)) || (mode == '2' && !(_32X_Started))) + { + char Device[8]; + strcpy(Device,(mode > '1')?"32X":"SegaCD"); + sprintf(Str_Tmp,"Warning: %s not started. \nWatches for %s addresses will be ignored.",Device,Device); + MessageBox(MESSAGEBOXPARENT,Str_Tmp,"Possible Device Mismatch",MB_OK); + }*/ + int WatchAdd; + fgets(Str_Tmp,1024,WatchFile); + sscanf(Str_Tmp,"%d%*s",&WatchAdd); + WatchAdd+=WatchCount; + for (int i = WatchCount; i < WatchAdd; i++) + { + while (i < 0) + i++; + do { + fgets(Str_Tmp,1024,WatchFile); + } while (Str_Tmp[0] == '\n'); + sscanf(Str_Tmp,"%*05X%*c%08X%*c%c%*c%c%*c%d",&(Temp.Address),&(Temp.Size),&(Temp.Type),&(Temp.WrongEndian)); + Temp.WrongEndian = 0; + char *Comment = strrchr(Str_Tmp,DELIM) + 1; + *strrchr(Comment,'\n') = '\0'; + InsertWatch(Temp,Comment); + } + + fclose(WatchFile); + if (RamWatchHWnd) + ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount); + RWfileChanged=false; + return; +} + +char Gens_Path[64]= "M:\\"; //TODO + +int Change_File_S(char *Dest, char *Dir, char *Titre, char *Filter, char *Ext, HWND hwnd) +{ + OPENFILENAME ofn; + + SetCurrentDirectory(Gens_Path); + + if (!strcmp(Dest, "")) + { + strcpy(Dest, "default."); + strcat(Dest, Ext); + } + + memset(&ofn, 0, sizeof(OPENFILENAME)); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hwnd; + ofn.hInstance = hAppInst; + ofn.lpstrFile = Dest; + ofn.nMaxFile = 2047; + ofn.lpstrFilter = Filter; + ofn.nFilterIndex = 1; + ofn.lpstrInitialDir = Dir; + ofn.lpstrTitle = Titre; + ofn.lpstrDefExt = Ext; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY; + + if (GetSaveFileName(&ofn)) return 1; + + return 0; +} + + +bool Save_Watches() +{ + strncpy(Str_Tmp,Rom_Name,512); + strcat(Str_Tmp,".wch"); + if(Change_File_S(Str_Tmp, Gens_Path, "Save Watches", "GENs Watchlist\0*.wch\0All Files\0*.*\0\0", "wch", RamWatchHWnd)) + { + FILE *WatchFile = fopen(Str_Tmp,"r+b"); + if (!WatchFile) WatchFile = fopen(Str_Tmp,"w+b"); +// fputc(SegaCD_Started?'1':(_32X_Started?'2':'0'),WatchFile); + fputc('\n',WatchFile); + strcpy(currentWatch,Str_Tmp); + RWAddRecentFile(currentWatch); + sprintf(Str_Tmp,"%d\n",WatchCount); + fputs(Str_Tmp,WatchFile); + const char DELIM = '\t'; + for (int i = 0; i < WatchCount; i++) + { + sprintf(Str_Tmp,"%05X%c%08X%c%c%c%c%c%d%c%s\n",i,DELIM,rswatches[i].Address,DELIM,rswatches[i].Size,DELIM,rswatches[i].Type,DELIM,rswatches[i].WrongEndian,DELIM,rswatches[i].comment); + fputs(Str_Tmp,WatchFile); + } + + fclose(WatchFile); + RWfileChanged=false; + //TODO: Add to recent list function call here + return true; + } + return false; +} + +bool QuickSaveWatches() +{ +if (RWfileChanged==false) return true; //If file has not changed, no need to save changes +if (currentWatch[0] == NULL) //If there is no currently loaded file, run to Save as and then return + { + return Save_Watches(); + } + + strcpy(Str_Tmp,currentWatch); + FILE *WatchFile = fopen(Str_Tmp,"r+b"); + if (!WatchFile) WatchFile = fopen(Str_Tmp,"w+b"); +// fputc(SegaCD_Started?'1':(_32X_Started?'2':'0'),WatchFile); + fputc('\n',WatchFile); + sprintf(Str_Tmp,"%d\n",WatchCount); + fputs(Str_Tmp,WatchFile); + const char DELIM = '\t'; + for (int i = 0; i < WatchCount; i++) + { + sprintf(Str_Tmp,"%05X%c%08X%c%c%c%c%c%d%c%s\n",i,DELIM,rswatches[i].Address,DELIM,rswatches[i].Size,DELIM,rswatches[i].Type,DELIM,rswatches[i].WrongEndian,DELIM,rswatches[i].comment); + fputs(Str_Tmp,WatchFile); + } + fclose(WatchFile); + RWfileChanged=false; + return true; +} + +bool Load_Watches(bool clear, const char* filename) +{ + const char DELIM = '\t'; + FILE* WatchFile = fopen(filename,"rb"); + if (!WatchFile) + { + MessageBox(MESSAGEBOXPARENT,"Error opening file.","ERROR",MB_OK); + return false; + } + if(clear) + { + if(!ResetWatches()) + { + fclose(WatchFile); + return false; + } + } + strcpy(currentWatch,filename); + RWAddRecentFile(currentWatch); + AddressWatcher Temp; + char mode; + fgets(Str_Tmp,1024,WatchFile); + sscanf(Str_Tmp,"%c%*s",&mode); +/* if ((mode == '1' && !(SegaCD_Started)) || (mode == '2' && !(_32X_Started))) + { + char Device[8]; + strcpy(Device,(mode > '1')?"32X":"SegaCD"); + sprintf(Str_Tmp,"Warning: %s not started. \nWatches for %s addresses will be ignored.",Device,Device); + MessageBox(MESSAGEBOXPARENT,Str_Tmp,"Possible Device Mismatch",MB_OK); + }*/ + int WatchAdd; + fgets(Str_Tmp,1024,WatchFile); + sscanf(Str_Tmp,"%d%*s",&WatchAdd); + WatchAdd+=WatchCount; + for (int i = WatchCount; i < WatchAdd; i++) + { + while (i < 0) + i++; + do { + fgets(Str_Tmp,1024,WatchFile); + } while (Str_Tmp[0] == '\n'); + sscanf(Str_Tmp,"%*05X%*c%08X%*c%c%*c%c%*c%d",&(Temp.Address),&(Temp.Size),&(Temp.Type),&(Temp.WrongEndian)); + Temp.WrongEndian = 0; + char *Comment = strrchr(Str_Tmp,DELIM) + 1; + *strrchr(Comment,'\n') = '\0'; + InsertWatch(Temp,Comment); + } + + fclose(WatchFile); + if (RamWatchHWnd) + ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount); + RWfileChanged=false; + return true; +} + + + +int Change_File_L(char *Dest, char *Dir, char *Titre, char *Filter, char *Ext, HWND hwnd) +{ + OPENFILENAME ofn; + + SetCurrentDirectory(Gens_Path); + + if (!strcmp(Dest, "")) + { + strcpy(Dest, "default."); + strcat(Dest, Ext); + } + + memset(&ofn, 0, sizeof(OPENFILENAME)); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hwnd; + ofn.hInstance = hAppInst; + ofn.lpstrFile = Dest; + ofn.nMaxFile = 2047; + ofn.lpstrFilter = Filter; + ofn.nFilterIndex = 1; + ofn.lpstrInitialDir = Dir; + ofn.lpstrTitle = Titre; + ofn.lpstrDefExt = Ext; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + + if (GetOpenFileName(&ofn)) return 1; + + return 0; +} + +bool Load_Watches(bool clear) +{ + strncpy(Str_Tmp,Rom_Name,512); + strcat(Str_Tmp,".wch"); + if(Change_File_L(Str_Tmp, Watch_Dir, "Load Watches", "GENs Watchlist\0*.wch\0All Files\0*.*\0\0", "wch", RamWatchHWnd)) + { + return Load_Watches(clear, Str_Tmp); + } + return false; +} + +bool ResetWatches() +{ + if(!AskSave()) + return false; + for (;WatchCount>=0;WatchCount--) + { + free(rswatches[WatchCount].comment); + rswatches[WatchCount].comment = NULL; + } + WatchCount++; + if (RamWatchHWnd) + ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount); + RWfileChanged = false; + currentWatch[0] = NULL; + return true; +} + +void RemoveWatch(int watchIndex) +{ + free(rswatches[watchIndex].comment); + rswatches[watchIndex].comment = NULL; + for (int i = watchIndex; i <= WatchCount; i++) + rswatches[i] = rswatches[i+1]; + WatchCount--; +} +/* +bool IsHardwareRAMAddressValid(unsigned int address) { + + return true; + + +};*/ + +LRESULT CALLBACK EditWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) //Gets info for a RAM Watch, and then inserts it into the Watch List +{ + RECT r; + RECT r2; + int dx1, dy1, dx2, dy2; + static int index; + static char s,t = s = 0; + + switch(uMsg) + { + case WM_INITDIALOG: +// Clear_Sound_Buffer(); + +/* if (Full_Screen) + { + while (ShowCursor(false) >= 0); + while (ShowCursor(true) < 0); + }*/ + + GetWindowRect(MainWindow->getHWnd(), &r); + dx1 = (r.right - r.left) / 2; + dy1 = (r.bottom - r.top) / 2; + + GetWindowRect(hDlg, &r2); + dx2 = (r2.right - r2.left) / 2; + dy2 = (r2.bottom - r2.top) / 2; + + //SetWindowPos(hDlg, NULL, max(0, r.left + (dx1 - dx2)), max(0, r.top + (dy1 - dy2)), NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + index = (int)lParam; + sprintf(Str_Tmp,"%08X",rswatches[index].Address); + SetDlgItemText(hDlg,IDC_EDIT_COMPAREADDRESS,Str_Tmp); + if (rswatches[index].comment != NULL) + SetDlgItemText(hDlg,IDC_PROMPT_EDIT,rswatches[index].comment); + s = rswatches[index].Size; + t = rswatches[index].Type; + switch (s) + { + case 'b': + SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_SETCHECK, BST_CHECKED, 0); + break; + case 'w': + SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_SETCHECK, BST_CHECKED, 0); + break; + case 'd': + SendDlgItemMessage(hDlg, IDC_4_BYTES, BM_SETCHECK, BST_CHECKED, 0); + break; + default: + s = 0; + break; + } + switch (t) + { + case 's': + SendDlgItemMessage(hDlg, IDC_SIGNED, BM_SETCHECK, BST_CHECKED, 0); + break; + case 'u': + SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_SETCHECK, BST_CHECKED, 0); + break; + case 'h': + SendDlgItemMessage(hDlg, IDC_HEX, BM_SETCHECK, BST_CHECKED, 0); + break; + default: + t = 0; + break; + } + + return true; + break; + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_SIGNED: + t='s'; + return true; + case IDC_UNSIGNED: + t='u'; + return true; + case IDC_HEX: + t='h'; + return true; + case IDC_1_BYTE: + s = 'b'; + return true; + case IDC_2_BYTES: + s = 'w'; + return true; + case IDC_4_BYTES: + s = 'd'; + return true; + case IDOK: + { +/* if (Full_Screen) + { + while (ShowCursor(true) < 0); + while (ShowCursor(false) >= 0); + }*/ + if (s && t) + { + AddressWatcher Temp; + Temp.Size = s; + Temp.Type = t; + Temp.WrongEndian = false; //replace this when I get little endian working properly + GetDlgItemText(hDlg,IDC_EDIT_COMPAREADDRESS,Str_Tmp,1024); + char *addrstr = Str_Tmp; + if (strlen(Str_Tmp) > 8) addrstr = &(Str_Tmp[strlen(Str_Tmp) - 9]); + for(int i = 0; addrstr[i]; i++) {if(toupper(addrstr[i]) == 'O') addrstr[i] = '0';} + sscanf(addrstr,"%08X",&(Temp.Address)); + + if((Temp.Address & ~0xFFFFFF) == ~0xFFFFFF) + Temp.Address &= 0xFFFFFF; + + if(IsHardwareRAMAddressValid(Temp.Address)) + { + GetDlgItemText(hDlg,IDC_PROMPT_EDIT,Str_Tmp,80); + if (index < WatchCount) RemoveWatch(index); + InsertWatch(Temp,Str_Tmp); + if(RamWatchHWnd) + { + ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount); + } +// DialogsOpen--; + EndDialog(hDlg, true); + } + else + { + MessageBox(hDlg,"Invalid Address","ERROR",MB_OK); + } + } + else + { + strcpy(Str_Tmp,"Error:"); + if (!s) + strcat(Str_Tmp," Size must be specified."); + if (!t) + strcat(Str_Tmp," Type must be specified."); + MessageBox(hDlg,Str_Tmp,"ERROR",MB_OK); + } + RWfileChanged=true; + return true; + break; + } +// case ID_CANCEL: + case IDCANCEL: +/* if (Full_Screen) + { + while (ShowCursor(true) < 0); + while (ShowCursor(false) >= 0); + }*/ +// DialogsOpen--; + EndDialog(hDlg, false); + return false; + break; + } + break; + + case WM_CLOSE: +/* if (Full_Screen) + { + while (ShowCursor(true) < 0); + while (ShowCursor(false) >= 0); + }*/ +// DialogsOpen--; + EndDialog(hDlg, false); + return false; + break; + } + + return false; +} + +void init_list_box(HWND Box, const char* Strs[], int numColumns, int *columnWidths) //initializes the ram search and/or ram watch listbox +{ + LVCOLUMN Col; + Col.mask = LVCF_FMT | LVCF_ORDER | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH; + Col.fmt = LVCFMT_CENTER; + for (int i = 0; i < numColumns; i++) + { + Col.iOrder = i; + Col.iSubItem = i; + Col.pszText = (LPSTR)(Strs[i]); + Col.cx = columnWidths[i]; + ListView_InsertColumn(Box,i,&Col); + } + + ListView_SetExtendedListViewStyle(Box, LVS_EX_FULLROWSELECT); +} + +//int ResultCount = 0; //TODO make sure this isn't breaking anything + +LRESULT CALLBACK RamWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + RECT r; + RECT r2; + int dx1, dy1, dx2, dy2; + static int watchIndex=0; + + Update_RAM_Watch(); + + switch(uMsg) + { + case WM_MOVE: { + RECT wrect; + GetWindowRect(hDlg,&wrect); + ramw_x = wrect.left; + ramw_y = wrect.top; + break; + }; + + case WM_INITDIALOG: { +/* if (Full_Screen) + { + while (ShowCursor(false) >= 0); + while (ShowCursor(true) < 0); + }*/ + + GetWindowRect(MainWindow->getHWnd(), &r); //Ramwatch window + dx1 = (r.right - r.left) / 2; + dy1 = (r.bottom - r.top) / 2; + + GetWindowRect(hDlg, &r2); // Gens window + dx2 = (r2.right - r2.left) / 2; + dy2 = (r2.bottom - r2.top) / 2; + + + // push it away from the main window if we can + const int width = (r.right-r.left); + const int height = (r.bottom - r.top); + const int width2 = (r2.right-r2.left); + if(r.left+width2 + width < GetSystemMetrics(SM_CXSCREEN)) + { + r.right += width; + r.left += width; + } + else if((int)r.left - (int)width2 > 0) + { + r.right -= width2; + r.left -= width2; + } + + //----------------------------------------------------------------------------------- + //If user has Save Window Pos selected, override default positioning + if (RWSaveWindowPos) + { + //If ramwindow is for some reason completely off screen, use default instead + if (ramw_x > (-width*2) || ramw_x < (width*2 + GetSystemMetrics(SM_CYSCREEN)) ) + r.left = ramw_x; //This also ignores cases of windows -32000 error codes + //If ramwindow is for some reason completely off screen, use default instead + if (ramw_y > (0-height*2) ||ramw_y < (height*2 + GetSystemMetrics(SM_CYSCREEN)) ) + r.top = ramw_y; //This also ignores cases of windows -32000 error codes + } + //------------------------------------------------------------------------------------- + SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + + ramwatchmenu=GetMenu(hDlg); + rwrecentmenu=CreateMenu(); + UpdateRW_RMenu(rwrecentmenu, RAMMENU_FILE_RECENT, RW_MENU_FIRST_RECENT_FILE); + + const char* names[3] = {"Address","Value","Notes"}; + int widths[3] = {62,64,64+51+53}; + init_list_box(GetDlgItem(hDlg,IDC_WATCHLIST),names,3,widths); +/* if (!ResultCount) //TODO what do these do + reset_address_info(); + else + signal_new_frame();*/ + ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount); +// if (!noMisalign) SendDlgItemMessage(hDlg, IDC_MISALIGN, BM_SETCHECK, BST_CHECKED, 0); +// if (littleEndian) SendDlgItemMessage(hDlg, IDC_ENDIAN, BM_SETCHECK, BST_CHECKED, 0); + +//TODO what? RamWatchAccels = LoadAccelerators(hAppInst, MAKEINTRESOURCE(IDR_ACCELERATOR1)); + + // due to some bug in windows, the arrow button width from the resource gets ignored, so we have to set it here + SetWindowPos(GetDlgItem(hDlg,ID_WATCHES_UPDOWN), 0,0,0, 30,60, SWP_NOMOVE); + + Update_RAM_Watch(); + + DragAcceptFiles(hDlg, TRUE); + + return true; + break; + } + + case WM_INITMENU: + CheckMenuItem(ramwatchmenu, RAMMENU_FILE_AUTOLOAD, AutoRWLoad ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(ramwatchmenu, RAMMENU_FILE_SAVEWINDOW, RWSaveWindowPos ? MF_CHECKED : MF_UNCHECKED); + break; + + case WM_MENUSELECT: + case WM_ENTERSIZEMOVE: +// Clear_Sound_Buffer(); + break; + + case WM_NOTIFY: + { + LPNMHDR lP = (LPNMHDR) lParam; + switch (lP->code) + { + case LVN_GETDISPINFO: + { + LV_DISPINFO *Item = (LV_DISPINFO *)lParam; + Item->item.mask = LVIF_TEXT; + Item->item.state = 0; + Item->item.iImage = 0; + const unsigned int iNum = Item->item.iItem; + static char num[11]; + switch (Item->item.iSubItem) + { + case 0: + sprintf(num,"%08X",rswatches[iNum].Address); + Item->item.pszText = num; + return true; + case 1: { + int i = rswatches[iNum].CurValue; + int t = rswatches[iNum].Type; + int size = rswatches[iNum].Size; + const char* formatString = ((t=='s') ? "%d" : (t=='u') ? "%u" : (size=='d' ? "%08X" : size=='w' ? "%04X" : "%02X")); + switch (size) + { + case 'b': + default: sprintf(num, formatString, t=='s' ? (char)(i&0xff) : (unsigned char)(i&0xff)); break; + case 'w': sprintf(num, formatString, t=='s' ? (short)(i&0xffff) : (unsigned short)(i&0xffff)); break; + case 'd': sprintf(num, formatString, t=='s' ? (long)(i&0xffffffff) : (unsigned long)(i&0xffffffff)); break; + } + + Item->item.pszText = num; + } return true; + case 2: + Item->item.pszText = rswatches[iNum].comment ? rswatches[iNum].comment : ""; + return true; + + default: + return false; + } + } + case LVN_ODFINDITEM: + { + // disable search by keyboard typing, + // because it interferes with some of the accelerators + // and it isn't very useful here anyway + SetWindowLong(hDlg, DWL_MSGRESULT, ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST))); + return 1; + } + } + break; + } + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case RAMMENU_FILE_SAVE: + QuickSaveWatches(); + break; + + case RAMMENU_FILE_SAVEAS: + //case IDC_C_SAVE: + return Save_Watches(); + case RAMMENU_FILE_OPEN: + return Load_Watches(true); + case RAMMENU_FILE_APPEND: + //case IDC_C_LOAD: + return Load_Watches(false); + case RAMMENU_FILE_NEW: + //case IDC_C_RESET: + ResetWatches(); + return true; + case IDC_C_WATCH_REMOVE: + watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)); + RemoveWatch(watchIndex); + ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount); + RWfileChanged=true; + SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST)); + return true; + case IDC_C_WATCH_EDIT: + watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)); + DialogBoxParam(hAppInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) watchIndex); + SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST)); + return true; + case IDC_C_WATCH: + rswatches[WatchCount].Address = rswatches[WatchCount].WrongEndian = 0; + rswatches[WatchCount].Size = 'b'; + rswatches[WatchCount].Type = 's'; + DialogBoxParam(hAppInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) WatchCount); + SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST)); + return true; + case IDC_C_WATCH_DUPLICATE: + watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)); + rswatches[WatchCount].Address = rswatches[watchIndex].Address; + rswatches[WatchCount].WrongEndian = rswatches[watchIndex].WrongEndian; + rswatches[WatchCount].Size = rswatches[watchIndex].Size; + rswatches[WatchCount].Type = rswatches[watchIndex].Type; + DialogBoxParam(hAppInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) WatchCount); + SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST)); + return true; + case IDC_C_WATCH_UP: + { + watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)); + if (watchIndex == 0 || watchIndex == -1) + return true; + void *tmp = malloc(sizeof(AddressWatcher)); + memcpy(tmp,&(rswatches[watchIndex]),sizeof(AddressWatcher)); + memcpy(&(rswatches[watchIndex]),&(rswatches[watchIndex - 1]),sizeof(AddressWatcher)); + memcpy(&(rswatches[watchIndex - 1]),tmp,sizeof(AddressWatcher)); + free(tmp); + ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex-1); + ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex-1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED); + ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount); + RWfileChanged=true; + return true; + } + case IDC_C_WATCH_DOWN: + { + watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)); + if (watchIndex >= WatchCount - 1 || watchIndex == -1) + return true; + void *tmp = malloc(sizeof(AddressWatcher)); + memcpy(tmp,&(rswatches[watchIndex]),sizeof(AddressWatcher)); + memcpy(&(rswatches[watchIndex]),&(rswatches[watchIndex + 1]),sizeof(AddressWatcher)); + memcpy(&(rswatches[watchIndex + 1]),tmp,sizeof(AddressWatcher)); + free(tmp); + ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex+1); + ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex+1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED); + ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount); + RWfileChanged=true; + return true; + } + case ID_WATCHES_UPDOWN: + { + int delta = ((LPNMUPDOWN)lParam)->iDelta; + SendMessage(hDlg, WM_COMMAND, delta<0 ? IDC_C_WATCH_UP : IDC_C_WATCH_DOWN,0); + break; + } + case RAMMENU_FILE_AUTOLOAD: + { + AutoRWLoad ^= 1; + CheckMenuItem(ramwatchmenu, RAMMENU_FILE_AUTOLOAD, AutoRWLoad ? MF_CHECKED : MF_UNCHECKED); + break; + } + case RAMMENU_FILE_SAVEWINDOW: + { + RWSaveWindowPos ^=1; + CheckMenuItem(ramwatchmenu, RAMMENU_FILE_SAVEWINDOW, RWSaveWindowPos ? MF_CHECKED : MF_UNCHECKED); + break; + } + case IDC_C_ADDCHEAT: + { + watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)) | (1 << 24); +// DialogBoxParam(hAppInst, MAKEINTRESOURCE(IDD_EDITCHEAT), hDlg, (DLGPROC) EditCheatProc,(LPARAM) searchIndex); + break; + } + case IDOK: + case IDCANCEL: //TODO this immediately activates and closes the window if you uncomment it +/* if (Full_Screen) + { + while (ShowCursor(true) < 0); + while (ShowCursor(false) >= 0); + }*/ +// DialogsOpen--; + RamWatchHWnd = NULL; + DragAcceptFiles(hDlg, FALSE); + EndDialog(hDlg, true); + return true; + default: + if (LOWORD(wParam) >= RW_MENU_FIRST_RECENT_FILE && LOWORD(wParam) < RW_MENU_FIRST_RECENT_FILE+MAX_RECENT_WATCHES) + OpenRWRecentFile(LOWORD(wParam) - RW_MENU_FIRST_RECENT_FILE); + } + break; + + case WM_KEYDOWN: // handle accelerator keys + { + SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST)); + MSG msg; + msg.hwnd = hDlg; + msg.message = uMsg; + msg.wParam = wParam; + msg.lParam = lParam; + if(RamWatchAccels && TranslateAccelerator(hDlg, RamWatchAccels, &msg)) + return true; + } break; + + case WM_CLOSE: +/* if (Full_Screen) + { + while (ShowCursor(true) < 0); + while (ShowCursor(false) >= 0); + }*/ +// DialogsOpen--; + RamWatchHWnd = NULL; + DragAcceptFiles(hDlg, FALSE); + EndDialog(hDlg, true); + return true; + + case WM_DROPFILES: + { + HDROP hDrop = (HDROP)wParam; + DragQueryFile(hDrop, 0, Str_Tmp, 1024); + DragFinish(hDrop); + return Load_Watches(true, Str_Tmp); + } break; + } + + return false; +} + diff --git a/desmume/src/windows/ramwatch.h b/desmume/src/windows/ramwatch.h new file mode 100644 index 000000000..b8c588d23 --- /dev/null +++ b/desmume/src/windows/ramwatch.h @@ -0,0 +1,39 @@ +#ifndef RAMWATCH_H +#define RAMWATCH_H +#include "windows.h" +bool ResetWatches(); +void OpenRWRecentFile(int memwRFileNumber); +extern bool AutoRWLoad; +extern bool RWSaveWindowPos; +#define MAX_RECENT_WATCHES 5 +extern char rw_recent_files[MAX_RECENT_WATCHES][1024]; +extern bool AskSave(); +extern int ramw_x; +extern int ramw_y; +extern bool RWfileChanged; + +// AddressWatcher is self-contained now +struct AddressWatcher +{ + unsigned int Address; // hardware address + char Size; + char Type; + char* comment; // NULL means no comment, non-NULL means allocated comment + bool WrongEndian; + unsigned int CurValue; +}; +#define MAX_WATCH_COUNT 256 +extern AddressWatcher rswatches[MAX_WATCH_COUNT]; +extern int WatchCount; // number of valid items in rswatches + +extern char Watch_Dir[1024]; + +bool InsertWatch(const AddressWatcher& Watch, char *Comment); +bool InsertWatch(const AddressWatcher& Watch, HWND parent=NULL); // asks user for comment +void Update_RAM_Watch(); +bool Load_Watches(bool clear, const char* filename); + +LRESULT CALLBACK RamWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +extern HWND RamWatchHWnd; + +#endif diff --git a/desmume/src/windows/replay.cpp b/desmume/src/windows/replay.cpp new file mode 100644 index 000000000..6a75cc70b --- /dev/null +++ b/desmume/src/windows/replay.cpp @@ -0,0 +1,157 @@ +#include +#include "resource.h" +#include "replay.h" +#include "common.h" +#include "main.h" +#include "movie.h" +#include "utils/xstring.h" + +bool replayreadonly=1; + +//adelikat: TODO: put this in one of the header files +template +inline std::wstring GetDlgItemTextW(HWND hDlg, int nIDDlgItem) { + wchar_t buf[BUFSIZE]; + GetDlgItemTextW(hDlg, nIDDlgItem, buf, BUFSIZE); + return buf; +} + +template +inline std::string GetDlgItemText(HWND hDlg, int nIDDlgItem) { + char buf[BUFSIZE]; + GetDlgItemText(hDlg, nIDDlgItem, buf, BUFSIZE); + return buf; +} + + +char playfilename[MAX_PATH] = ""; + +//Replay movie dialog +BOOL CALLBACK ReplayDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + OPENFILENAME ofn; + char szChoice[MAX_PATH]={0}; + char filename[MAX_PATH] = ""; + + switch(uMsg) + { + case WM_INITDIALOG: + SendDlgItemMessage(hwndDlg, IDC_CHECK_READONLY, BM_SETCHECK, replayreadonly?BST_CHECKED:BST_UNCHECKED, 0); + return FALSE; + + case WM_COMMAND: + int wID = LOWORD(wParam); + switch(wID) + { + case ID_BROWSE: + + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = MainWindow->getHWnd(); + ofn.lpstrFilter = "Desmume Movie File (*.dsm)\0*.dsm\0All files(*.*)\0*.*\0\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFile = filename; + ofn.lpstrTitle = "Replay Movie from File"; + ofn.nMaxFile = MAX_PATH; + ofn.lpstrDefExt = "dsm"; + GetOpenFileName(&ofn); + strcpy(playfilename, filename); + SetDlgItemText(hwndDlg, PM_FILENAME, playfilename); + return true; + + case IDC_CHECK_READONLY: + replayreadonly ^= 1; + return true; + + case IDOK: + FCEUI_LoadMovie(playfilename, replayreadonly, false, 80000); + ZeroMemory(&playfilename, sizeof(playfilename)); + EndDialog(hwndDlg, 0); + return true; + + case IDCANCEL: + ZeroMemory(&playfilename, sizeof(playfilename)); + EndDialog(hwndDlg, 0); + return true; + } + } + + return false; +} + +//Record movie dialog +static BOOL CALLBACK RecordDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static struct CreateMovieParameters* p = NULL; + std::wstring author = L""; + switch(uMsg) + { + case WM_INITDIALOG: + + return false; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDOK: { + author = GetDlgItemTextW<500>(hwndDlg,IDC_EDIT_AUTHOR); + std::string fname = GetDlgItemText(hwndDlg,IDC_EDIT_FILENAME); + FCEUI_SaveMovie(fname.c_str(), author); + EndDialog(hwndDlg, 0); + return true; + } + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return true; + + case IDC_BUTTON_BROWSEFILE: + { + OPENFILENAME ofn; + char szChoice[MAX_PATH]={0}; + char recordfilename[MAX_PATH]; + + // browse button + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = MainWindow->getHWnd(); + ofn.lpstrFilter = "Desmume Movie File (*.dsm)\0*.dsm\0All files(*.*)\0*.*\0\0"; + ofn.lpstrFile = szChoice; + ofn.lpstrTitle = "Record a new movie"; + ofn.lpstrDefExt = "dsm"; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; + GetSaveFileName(&ofn); + strcpy(recordfilename, szChoice); + SetDlgItemText(hwndDlg, IDC_EDIT_FILENAME, recordfilename); + //if(GetSaveFileName(&ofn)) + // UpdateRecordDialogPath(hwndDlg,szChoice); + + return true; + } + + } + + } + + return false; +} + + +//Show the play movie dialog and play a movie +void Replay_LoadMovie() +{ + char* fn = (char*)DialogBoxParam(hAppInst, "IDD_REPLAYINP", GetMainHWND(), ReplayDialogProc, false); + + if(fn) + { + FCEUI_LoadMovie(fn, movie_readonly, false, 100000); + + free(fn); + } +} + +//Show the record movie dialog and record a movie. +void MovieRecordTo() +{ + DialogBoxParam(hAppInst, MAKEINTRESOURCE(IDD_RECORDMOVIE), GetMainHWND(), RecordDialogProc, (LPARAM)0); +} diff --git a/desmume/src/windows/replay.h b/desmume/src/windows/replay.h new file mode 100644 index 000000000..6a0880b94 --- /dev/null +++ b/desmume/src/windows/replay.h @@ -0,0 +1,2 @@ +void Replay_LoadMovie(); +void MovieRecordTo(); diff --git a/desmume/src/windows/resource.h b/desmume/src/windows/resource.h index 205811e3a..b1d4e508c 100644 --- a/desmume/src/windows/resource.h +++ b/desmume/src/windows/resource.h @@ -2,6 +2,7 @@ #define IDC_STATIC (-1) #endif +#define IDD_RECORDMOVIE 101 #define IDM_OPEN 101 #define IDM_QUIT 102 #define IDC_FERMER 103 @@ -45,7 +46,6 @@ #define IDM_SUBMITBUGREPORT 134 #define IDM_STATE_LOAD 135 #define IDM_STATE_SAVE 136 -#define IDR_MAIN_ACCEL 137 #define IDM_STATE_SAVE_F1 140 #define IDM_STATE_SAVE_F2 141 #define IDM_STATE_SAVE_F3 142 @@ -88,7 +88,10 @@ #define IDC_FRAMESKIP6 197 #define IDC_FRAMESKIP7 198 #define IDC_FRAMESKIP8 199 +#define IDC_EDIT_FILENAME 200 #define IDC_FRAMESKIP9 200 +#define IDC_BUTTON_BROWSEFILE 201 +#define IDC_CHECK_READONLY 201 #define IDM_IMPORTBACKUPMEMORY 201 #define IDC_LANGENGLISH 210 #define IDC_LANGFRENCH 211 @@ -120,7 +123,10 @@ #define IDC_GOTODES 423 #define IDC_TMP 424 #define IDD_GAME_INFO 501 +#define ID_RAM_WATCH 550 #define IDD_IO_REG 601 +#define IDM_RECORD_MOVIE 602 +#define IDM_PLAY_MOVIE 603 #define IDD_PAL 703 #define IDD_TILE 704 #define IDC_PAL_SELECT 705 @@ -140,6 +146,7 @@ #define IDC_SCR 806 #define IDC_MSIZE 807 #define IDC_SCROLL 808 +#define IDD_CHEAT_ADD_XX_CODE 809 #define IDD_OAM 900 #define IDC_SCR_SELECT 901 #define IDC_TILE 902 @@ -155,16 +162,71 @@ #define IDD_EMULATIONSETTINGS 916 #define IDD_WIFISETTINGS 918 #define IDD_MEM_VIEW 920 -#define IDD_CHEAT_ADD_XX_CODE 921 +#define IDC_DIFFERENTFROM 921 +#define IDC_DIFFERENTBY 922 +#define IDC_MODULO 923 +#define IDC_EDIT_DIFFBY 924 +#define IDC_EDIT_MODBY 925 +#define IDC_PREVIOUSVALUE 926 +#define IDC_SPECIFICVALUE 927 +#define IDC_NUMBEROFCHANGES 928 +#define IDC_EDIT_COMPAREVALUE 929 +#define IDC_EDIT_COMPARECHANGES 930 +#define IDC_C_AUTOSEARCH 931 +#define IDC_MISALIGN 932 +#define IDC_C_RESET_CHANGES 933 +#define IDC_C_UNDO 934 +#define ID_RAM_SEARCH 935 +#define IDD_RAMSEARCH 945 +#define IDC_SPECIFICADDRESS 950 +#define IDC_EDIT_COMPAREADDRESS 951 +#define IDC_PROMPT_TEXT 952 +#define IDC_PROMPT_EDIT 953 +#define IDC_SIGNED 954 +#define IDC_UNSIGNED 955 +#define IDC_HEX 956 +#define IDC_1_BYTE 957 +#define IDC_2_BYTES 958 +#define IDC_4_BYTES 959 +#define IDC_WATCHLIST 960 +#define IDC_C_WATCH_EDIT 961 +#define IDC_C_WATCH_REMOVE 962 +#define IDC_C_WATCH 963 +#define IDC_C_WATCH_DUPLICATE 964 +#define IDC_C_ADDCHEAT 965 +#define ID_WATCHES_UPDOWN 966 +#define IDD_RAMWATCH 967 +#define IDC_C_WATCH_UP 980 +#define IDD_EDITWATCH 980 +#define IDC_C_WATCH_DOWN 981 +#define RAMMENU_FILE_AUTOLOAD 982 +#define RAMMENU_FILE_SAVEWINDOW 983 +#define RAMMENU_FILE_SAVE 984 +#define RAMMENU_FILE_SAVEAS 985 +#define RAMMENU_FILE_OPEN 986 +#define RAMMENU_FILE_APPEND 987 +#define RAMMENU_FILE_NEW 988 +#define RAMMENU_FILE_RECENT 990 +#define IDC_RAMLIST 991 +#define IDC_C_SEARCH 992 +#define IDC_C_RESET 993 +#define IDC_C_ELIMINATE 994 +#define IDC_LESSTHAN 995 +#define IDC_MORETHAN 996 +#define IDC_NOMORETHAN 997 +#define IDC_NOLESSTHAN 998 +#define IDC_EQUALTO 999 #define IDC_BGMAP_SEL 1000 #define IDC_BRIDGEADAPTER 1000 #define IDC_EDIT03 1000 #define IDC_SOUNDCORECB 1000 #define IDC_USEEXTBIOS 1000 +#define ID_BROWSE 1000 #define IDC_BGMAP_BGXCNT 1001 #define IDC_CHECKBOX_DEBUGGERMODE 1001 #define IDC_EDIT01 1001 #define IDC_SOUNDBUFFERET 1001 +#define PM_FILENAME 1001 #define IDC_ARM9BIOS 1002 #define IDC_EDIT05 1002 #define IDC_SLVOLUME 1002 @@ -279,6 +341,7 @@ #define IDC_EDIT13 1104 #define IDC_EDIT3 1104 #define IDC_EDIT14 1105 +#define IDC_EDIT_AUTHOR 1180 #define IDD_MATRIX_VIEWER 1200 #define IDM_MATRIX_VIEWER 1200 #define IDC_MATRIX_VIEWER_COORD_GROUP 1201 @@ -381,6 +444,7 @@ #define IDC_LIGHT_VIEWER_LIGHT3COLOR_EDIT 1333 #define IDC_LIGHT_VIEWER_LIGHT3VECTOR_EDIT 1334 #define IDC_GI_FATOFS 4464 +#define IDC_INTERPOLATECOLOR 4464 #define IDC_GI_FATSIZE 4465 #define IDC_GI_ICONTITLEOFS 4466 #define IDC_GI_USEDROMSIZE 4467 @@ -425,6 +489,7 @@ #define ID_VIEW_DISPLAYMICROPHONE 40014 #define IDM_FILE_RECORDAVI 40015 #define IDM_FILE_STOPAVI 40016 +#define IDM_STOPMOVIE 40019 #define ID_FILE_RECENTROM 40034 #define IDC_SAVETYPE7 40037 #define IDM_DEFSIZE 40038 @@ -517,4 +582,17 @@ #define IDC_HKCOMBO 60077 #define IDD_KEYCUSTOM 60078 #define IDM_HOTKEY_CONFIG 60079 -#define IDC_INTERPOLATECOLOR 70000 +#define IDM_RECENT_RESERVED0 65500 +#define IDM_RECENT_RESERVED1 65501 +#define IDM_RECENT_RESERVED2 65502 +#define IDM_RECENT_RESERVED3 65503 +#define IDM_RECENT_RESERVED4 65504 +#define IDM_RECENT_RESERVED5 65505 +#define IDM_RECENT_RESERVED6 65506 +#define IDM_RECENT_RESERVED7 65507 +#define IDM_RECENT_RESERVED8 65508 +#define IDM_RECENT_RESERVED9 65509 +#define IDM_RECENT_RESERVED10 65510 +#define IDM_RECENT_RESERVED11 65511 +#define IDM_RECENT_RESERVED12 65512 +#define IDM_RECENT_RESERVED13 65513 diff --git a/desmume/src/windows/resources.rc b/desmume/src/windows/resources.rc index 2fe3ba0ca..9e90de791 100644 --- a/desmume/src/windows/resources.rc +++ b/desmume/src/windows/resources.rc @@ -242,6 +242,10 @@ MENU_PRINCIPAL MENU MENUITEM "Record &AVI...", IDM_FILE_RECORDAVI MENUITEM "Stop AVI", IDM_FILE_STOPAVI MENUITEM SEPARATOR + MENUITEM "&Record Movie...", IDM_RECORD_MOVIE + MENUITEM "&Play Movie...", IDM_PLAY_MOVIE + MENUITEM "&Stop Movie", IDM_STOPMOVIE + MENUITEM SEPARATOR MENUITEM "&Quit\tAlt F4", IDM_QUIT } POPUP "&Emulation" @@ -290,6 +294,8 @@ MENU_PRINCIPAL MENU MENUITEM "Display Lag Counter", ID_VIEW_DISPLAYLAG MENUITEM "Display Microphone", ID_VIEW_DISPLAYMICROPHONE MENUITEM "HUD Editing Mode", ID_VIEW_HUDEDITOR + MENUITEM "RAM Watch", ID_RAM_WATCH + MENUITEM "RAM Search", ID_RAM_SEARCH } POPUP "&Config" { @@ -307,7 +313,7 @@ MENU_PRINCIPAL MENU MENUITEM "Control Config", IDM_CONFIG MENUITEM "Hotkey Config", IDM_HOTKEY_CONFIG MENUITEM "Sound Settings", IDM_SOUNDSETTINGS - MENUITEM "Wifi settings", IDM_WIFISETTINGS, GRAYED + MENUITEM "Wifi settings", IDM_WIFISETTINGS MENUITEM "Firmware Settings", IDM_FIRMSETTINGS MENUITEM "Emulation Settings", IDM_EMULATIONSETTINGS MENUITEM "Microphone Settings", IDM_MICROPHONESETTINGS @@ -345,7 +351,7 @@ MENU_PRINCIPAL MENU MENUITEM "View Matrices", IDM_MATRIX_VIEWER MENUITEM "View Lights", IDM_LIGHT_VIEWER MENUITEM SEPARATOR - POPUP "&View &Layers" + POPUP "&View Layers" { MENUITEM "Main BG 0", IDM_MBG0, CHECKED MENUITEM "Main BG 1", IDM_MBG1, CHECKED @@ -540,6 +546,37 @@ MENU_PRINCIPAL MENU +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +RAMWATCH_MENU MENU +{ + POPUP "File" + { + MENUITEM "&New list\tCtrl N", RAMMENU_FILE_NEW + MENUITEM "&Open...\tCtrl O", RAMMENU_FILE_OPEN + MENUITEM "&Save\tCtrl S", RAMMENU_FILE_SAVE + MENUITEM "Sa&ve As...\tCtrl Shift S", RAMMENU_FILE_SAVEAS + MENUITEM "&Append file...", RAMMENU_FILE_APPEND + MENUITEM "Recent", RAMMENU_FILE_RECENT + MENUITEM SEPARATOR + MENUITEM "Auto-&load", RAMMENU_FILE_AUTOLOAD + MENUITEM "Save Window Position", RAMMENU_FILE_SAVEWINDOW + MENUITEM SEPARATOR + MENUITEM "&Close\tAlt F4", IDCANCEL + } + POPUP "Watches" + { + MENUITEM "&New Watch\tN", IDC_C_WATCH + MENUITEM "&Edit Watch\tE", IDC_C_WATCH_EDIT + MENUITEM "&Remove Watch\tR", IDC_C_WATCH_REMOVE + MENUITEM "Duplicate Watch\tA", IDC_C_WATCH_DUPLICATE + MENUITEM SEPARATOR + MENUITEM "Move Up\tU", IDC_C_WATCH_UP + MENUITEM "Move Down\tD", IDC_C_WATCH_DOWN + } +} + + + LANGUAGE LANG_DANISH, SUBLANG_DANISH_DENMARK RECENTROMS MENU { @@ -583,10 +620,10 @@ RECENTROMS MENU // Dialog resources // LANGUAGE LANG_DANISH, SUBLANG_DANISH_DENMARK -IDD_3DSETTINGS DIALOGEX 0, 0, 174, 120 +IDD_3DSETTINGS DIALOG 0, 0, 174, 120 STYLE DS_MODALFRAME | DS_SETFONT | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_POPUP | WS_SYSMENU CAPTION "3D settings" -FONT 8, "MS Sans Serif", 0, 0, 0 +FONT 8, "MS Sans Serif" { DEFPUSHBUTTON "OK", IDOK, 62, 92, 50, 14, BS_DEFPUSHBUTTON PUSHBUTTON "Cancel", IDCANCEL, 117, 92, 50, 14, BS_PUSHBUTTON @@ -617,10 +654,10 @@ FONT 8, "MS Sans Serif", 0, 0, 0 LANGUAGE LANG_FRENCH, SUBLANG_FRENCH -IDD_3DSETTINGS DIALOGEX 0, 0, 174, 120 +IDD_3DSETTINGS DIALOG 0, 0, 174, 120 STYLE DS_MODALFRAME | DS_SETFONT | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_POPUP | WS_SYSMENU CAPTION "Configuration 3D" -FONT 8, "MS Sans Serif", 0, 0, 0 +FONT 8, "MS Sans Serif" { DEFPUSHBUTTON "OK", IDOK, 62, 92, 50, 14, BS_DEFPUSHBUTTON PUSHBUTTON "Annuler", IDCANCEL, 117, 92, 50, 14, BS_PUSHBUTTON @@ -1220,6 +1257,30 @@ FONT 8, "MS Sans Serif", 0, 0, 1 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +IDD_EDITWATCH DIALOGEX 0, 0, 181, 95 +STYLE DS_MODALFRAME | DS_SHELLFONT | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_POPUP | WS_SYSMENU +CAPTION " Edit Watch" +FONT 8, "MS Shell Dlg", 400, 0, 1 +{ + CTEXT "Address:", IDC_SPECIFICADDRESS, 18, 12, 35, 10, SS_CENTER + EDITTEXT IDC_EDIT_COMPAREADDRESS, 55, 10, 65, 12, ES_AUTOHSCROLL | ES_UPPERCASE + CTEXT "Notes:", IDC_PROMPT_TEXT, 18, 24, 45, 10, SS_CENTER + EDITTEXT IDC_PROMPT_EDIT, 55, 22, 65, 12, ES_AUTOHSCROLL + GROUPBOX "Data Type", IDC_STATIC, 14, 37, 75, 42, 0, WS_EX_TRANSPARENT + AUTORADIOBUTTON "&Signed", IDC_SIGNED, 18, 47, 67, 9, WS_GROUP | WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "&Unsigned", IDC_UNSIGNED, 18, 57, 67, 9, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "&Hexadecimal", IDC_HEX, 18, 67, 67, 9, WS_TABSTOP | BS_AUTORADIOBUTTON + GROUPBOX "Data Size", IDC_STATIC, 94, 37, 73, 42, 0, WS_EX_TRANSPARENT + AUTORADIOBUTTON "&1 byte", IDC_1_BYTE, 98, 47, 61, 11, WS_GROUP | WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "&2 bytes", IDC_2_BYTES, 98, 57, 61, 11, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "&4 bytes", IDC_4_BYTES, 98, 67, 61, 11, WS_TABSTOP | BS_AUTORADIOBUTTON + DEFPUSHBUTTON "&OK", IDOK, 66, 80, 50, 14, BS_DEFPUSHBUTTON + PUSHBUTTON "&Cancel", IDCANCEL, 120, 80, 50, 14, BS_PUSHBUTTON +} + + + LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US IDD_EMULATIONSETTINGS DIALOG 0, 0, 241, 270 STYLE DS_CENTER | DS_SHELLFONT | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_SYSMENU @@ -2094,7 +2155,6 @@ FONT 8, "MS Shell Dlg", 400, 0, 1 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US IDD_MEM_VIEW DIALOG 0, 0, 436, 231 STYLE DS_CENTER | DS_SHELLFONT | WS_VISIBLE | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_SYSMENU -CAPTION "" FONT 8, "MS Sans Serif" { PUSHBUTTON "Close", IDCANCEL, 188, 210, 60, 15, BS_PUSHBUTTON @@ -2137,7 +2197,7 @@ CAPTION "OAM Fremviser" FONT 8, "MS Sans Serif", 0, 0, 1 { COMBOBOX IDC_SCR_SELECT, 4, 4, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWNLIST - SCROLLBAR IDC_SCROLLER, 48, 20, 20, 14, WS_VISIBLE | WS_VISIBLE + SCROLLBAR IDC_SCROLLER, 48, 20, 20, 14, NOT WS_VISIBLE | NOT WS_VISIBLE LTEXT "Mode :", IDC_STATIC, 4, 36, 45, 8, SS_LEFT LTEXT "Tile :", IDC_STATIC, 4, 46, 45, 8, SS_LEFT LTEXT "Palette :", IDC_STATIC, 4, 56, 45, 8, SS_LEFT @@ -2173,7 +2233,7 @@ CAPTION "OAM Viewer" FONT 8, "MS Sans Serif", 0, 0, 1 { COMBOBOX IDC_SCR_SELECT, 4, 4, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWNLIST - SCROLLBAR IDC_SCROLLER, 48, 20, 20, 14, WS_VISIBLE | WS_VISIBLE + SCROLLBAR IDC_SCROLLER, 48, 20, 20, 14, NOT WS_VISIBLE | NOT WS_VISIBLE LTEXT "Mode :", IDC_STATIC, 4, 36, 45, 8, SS_LEFT LTEXT "Tile :", IDC_STATIC, 4, 46, 45, 8, SS_LEFT LTEXT "Palette :", IDC_STATIC, 4, 56, 45, 8, SS_LEFT @@ -2209,7 +2269,7 @@ CAPTION "OAM Viewer" FONT 8, "MS Sans Serif", 0, 0, 1 { COMBOBOX IDC_SCR_SELECT, 4, 4, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWNLIST - SCROLLBAR IDC_SCROLLER, 48, 20, 20, 14, WS_VISIBLE | WS_VISIBLE + SCROLLBAR IDC_SCROLLER, 48, 20, 20, 14, NOT WS_VISIBLE | NOT WS_VISIBLE LTEXT "Mode :", IDC_STATIC, 4, 36, 45, 8, SS_LEFT LTEXT "Tile :", IDC_STATIC, 4, 46, 45, 8, SS_LEFT LTEXT "Palette :", IDC_STATIC, 4, 56, 45, 8, SS_LEFT @@ -2246,7 +2306,7 @@ FONT 8, "MS Sans Serif", 0, 0, 1 { COMBOBOX IDC_PAL_SELECT, 15, 3, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWN LTEXT "Pal : 0", IDC_PALNUM, 3, 21, 25, 8, SS_LEFT - SCROLLBAR IDC_SCROLLER, 30, 18, 20, 14, WS_VISIBLE | WS_VISIBLE + SCROLLBAR IDC_SCROLLER, 30, 18, 20, 14, NOT WS_VISIBLE | NOT WS_VISIBLE PUSHBUTTON "&Luk", IDC_FERMER, 35, 169, 50, 14, BS_PUSHBUTTON AUTOCHECKBOX "&Auto-update", IDC_AUTO_UPDATE, 13, 150, 54, 14, BS_AUTOCHECKBOX EDITTEXT IDC_AUTO_UPDATE_SECS, 68, 151, 12, 14, WS_DISABLED | ES_AUTOHSCROLL | ES_READONLY @@ -2264,7 +2324,7 @@ FONT 8, "MS Sans Serif", 0, 0, 1 { COMBOBOX IDC_PAL_SELECT, 15, 3, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWN LTEXT "Pal : 0", IDC_PALNUM, 3, 21, 25, 8, SS_LEFT - SCROLLBAR IDC_SCROLLER, 30, 18, 20, 14, WS_VISIBLE | WS_VISIBLE + SCROLLBAR IDC_SCROLLER, 30, 18, 20, 14, NOT WS_VISIBLE | NOT WS_VISIBLE PUSHBUTTON "&Close", IDC_FERMER, 35, 169, 50, 14, BS_PUSHBUTTON AUTOCHECKBOX "&Auto-update", IDC_AUTO_UPDATE, 13, 150, 54, 14, BS_AUTOCHECKBOX EDITTEXT IDC_AUTO_UPDATE_SECS, 68, 151, 12, 14, WS_DISABLED | ES_AUTOHSCROLL | ES_READONLY @@ -2282,7 +2342,7 @@ FONT 8, "MS Sans Serif", 0, 0, 1 { COMBOBOX IDC_PAL_SELECT, 15, 3, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWN LTEXT "Pal : 0", IDC_PALNUM, 3, 21, 25, 8, SS_LEFT - SCROLLBAR IDC_SCROLLER, 30, 18, 20, 14, WS_VISIBLE | WS_VISIBLE + SCROLLBAR IDC_SCROLLER, 30, 18, 20, 14, NOT WS_VISIBLE | NOT WS_VISIBLE AUTOCHECKBOX "Auto-update", IDC_AUTO_UPDATE, 60, 18, 75, 14, BS_AUTOCHECKBOX PUSHBUTTON "&Fermer", IDC_FERMER, 35, 169, 50, 14, BS_PUSHBUTTON AUTOCHECKBOX "&Auto-update", IDC_AUTO_UPDATE2, 13, 150, 54, 14, BS_AUTOCHECKBOX @@ -2293,6 +2353,120 @@ FONT 8, "MS Sans Serif", 0, 0, 1 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +IDD_RAMSEARCH DIALOGEX 0, 0, 287, 292 +STYLE DS_MODALFRAME | DS_SHELLFONT | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_GROUP | WS_POPUP | WS_SYSMENU +CAPTION " RAM Search" +FONT 8, "MS Shell Dlg", 400, 0, 1 +{ + CONTROL "", IDC_RAMLIST, WC_LISTVIEW, WS_TABSTOP | WS_BORDER | LVS_SHOWSELALWAYS | LVS_OWNERDATA | LVS_REPORT, 9, 9, 214, 151, WS_EX_CLIENTEDGE + PUSHBUTTON "&Search", IDC_C_SEARCH, 226, 9, 52, 16, BS_PUSHBUTTON + PUSHBUTTON "&Add Cheat", IDC_C_ADDCHEAT, 226, 145, 52, 16, WS_DISABLED | BS_PUSHBUTTON + PUSHBUTTON "&Watch", IDC_C_WATCH, 226, 127, 52, 16, BS_PUSHBUTTON + PUSHBUTTON "&Reset", IDC_C_RESET, 226, 27, 52, 16, BS_PUSHBUTTON + PUSHBUTTON "&Eliminate", IDC_C_ELIMINATE, 226, 109, 52, 16, BS_PUSHBUTTON + GROUPBOX "Comparison Operator", IDC_STATIC, 10, 166, 102, 118, 0, WS_EX_TRANSPARENT + AUTORADIOBUTTON "Less Than", IDC_LESSTHAN, 14, 178, 95, 11, WS_GROUP | WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Greater Than", IDC_MORETHAN, 14, 191, 95, 11, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Less Than or Equal To", IDC_NOMORETHAN, 14, 204, 95, 11, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Greater Than or Equal To", IDC_NOLESSTHAN, 14, 217, 95, 11, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Equal To", IDC_EQUALTO, 14, 230, 95, 11, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Not Equal To", IDC_DIFFERENTFROM, 14, 243, 95, 11, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Different By:", IDC_DIFFERENTBY, 14, 256, 52, 11, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Modulo", IDC_MODULO, 14, 269, 35, 11, WS_TABSTOP | BS_AUTORADIOBUTTON + EDITTEXT IDC_EDIT_DIFFBY, 69, 255, 38, 12, WS_DISABLED | ES_AUTOHSCROLL | ES_UPPERCASE + EDITTEXT IDC_EDIT_MODBY, 51, 267, 38, 12, WS_DISABLED | ES_AUTOHSCROLL | ES_UPPERCASE + GROUPBOX "Compare To / By", IDC_STATIC, 118, 166, 153, 58, 0, WS_EX_TRANSPARENT + AUTORADIOBUTTON "Previous Value", IDC_PREVIOUSVALUE, 121, 176, 67, 10, WS_GROUP | WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Specific Value:", IDC_SPECIFICVALUE, 121, 187, 67, 10, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Specific Address:", IDC_SPECIFICADDRESS, 121, 198, 67, 10, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Number of Changes:", IDC_NUMBEROFCHANGES, 121, 209, 76, 10, WS_TABSTOP | BS_AUTORADIOBUTTON + EDITTEXT IDC_EDIT_COMPAREVALUE, 203, 183, 63, 12, WS_DISABLED | ES_AUTOHSCROLL | ES_UPPERCASE + EDITTEXT IDC_EDIT_COMPAREADDRESS, 203, 195, 63, 12, WS_DISABLED | ES_AUTOHSCROLL | ES_UPPERCASE + EDITTEXT IDC_EDIT_COMPARECHANGES, 203, 207, 63, 12, WS_DISABLED | ES_AUTOHSCROLL | ES_UPPERCASE + GROUPBOX "Data Type / Display", IDC_STATIC, 196, 227, 75, 44, 0, WS_EX_TRANSPARENT + AUTORADIOBUTTON "Signed", IDC_SIGNED, 200, 237, 67, 9, WS_GROUP | WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Unsigned", IDC_UNSIGNED, 200, 248, 67, 9, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "Hexadecimal", IDC_HEX, 200, 259, 67, 9, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTOCHECKBOX "Autosearch", IDC_C_AUTOSEARCH, 200, 273, 52, 11, BS_AUTOCHECKBOX + GROUPBOX "Data Size", IDC_STATIC, 117, 227, 73, 57, 0, WS_EX_TRANSPARENT + AUTORADIOBUTTON "1 byte", IDC_1_BYTE, 121, 237, 61, 11, WS_GROUP | WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "2 bytes", IDC_2_BYTES, 121, 248, 61, 11, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTORADIOBUTTON "4 bytes", IDC_4_BYTES, 121, 259, 61, 11, WS_TABSTOP | BS_AUTORADIOBUTTON + AUTOCHECKBOX "Check Misaligned", IDC_MISALIGN, 121, 272, 65, 8, BS_AUTOCHECKBOX + PUSHBUTTON "&Clear Change Counts", IDC_C_RESET_CHANGES, 226, 46, 52, 20, BS_PUSHBUTTON | BS_MULTILINE + PUSHBUTTON "&Undo", IDC_C_UNDO, 226, 69, 52, 16, WS_DISABLED | BS_PUSHBUTTON + LTEXT "Is", IDC_STATIC, 92, 270, 12, 8, SS_LEFT +} + + + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +IDD_RAMWATCH DIALOGEX 0, 0, 269, 298 +STYLE DS_MODALFRAME | DS_SHELLFONT | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_GROUP | WS_POPUP | WS_SYSMENU +CAPTION " RAM Watch" +MENU RAMWATCH_MENU +FONT 8, "MS Shell Dlg", 400, 0, 1 +{ + CONTROL "", IDC_WATCHLIST, WC_LISTVIEW, WS_TABSTOP | WS_BORDER | LVS_SHOWSELALWAYS | LVS_OWNERDATA | LVS_SINGLESEL | LVS_REPORT, 9, 9, 210, 279, WS_EX_CLIENTEDGE + PUSHBUTTON "Edit", IDC_C_WATCH_EDIT, 226, 66, 34, 14, BS_PUSHBUTTON + PUSHBUTTON "Remove", IDC_C_WATCH_REMOVE, 226, 83, 34, 14, BS_PUSHBUTTON + PUSHBUTTON "New", IDC_C_WATCH, 226, 100, 34, 14, BS_PUSHBUTTON + PUSHBUTTON "Duplicate", IDC_C_WATCH_DUPLICATE, 226, 117, 34, 14, BS_PUSHBUTTON + PUSHBUTTON "Add Cheat", IDC_C_ADDCHEAT, 222, 140, 42, 16, WS_DISABLED | BS_PUSHBUTTON + GROUPBOX "Watches", IDC_STATIC, 222, 11, 42, 125 + CONTROL "", ID_WATCHES_UPDOWN, UPDOWN_CLASS, WS_TABSTOP, 232, 23, 19, 36 +} + + + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +IDD_RECORDMOVIE DIALOGEX 0, 0, 301, 89 +STYLE DS_MODALFRAME | DS_SHELLFONT | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_POPUP | WS_SYSMENU +CAPTION "Record movie" +FONT 8, "MS Shell Dlg", 400, 0, 1 +{ + DEFPUSHBUTTON "OK", IDOK, 177, 62, 50, 14, BS_DEFPUSHBUTTON + GROUPBOX "", 65501, 11, 9, 269, 47 + PUSHBUTTON "Cancel", IDCANCEL, 231, 62, 50, 14, BS_PUSHBUTTON + EDITTEXT IDC_EDIT_FILENAME, 49, 20, 189, 12, ES_AUTOHSCROLL + PUSHBUTTON "...", IDC_BUTTON_BROWSEFILE, 249, 20, 18, 14, BS_PUSHBUTTON + EDITTEXT IDC_EDIT_AUTHOR, 49, 36, 189, 14, ES_AUTOHSCROLL + LTEXT "File:", IDC_STATIC, 31, 22, 14, 8, SS_LEFT + LTEXT "Author:", IDC_STATIC, 21, 38, 25, 8, SS_LEFT +} + + + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +IDD_REPLAYINP DIALOGEX 0, 0, 300, 79 +STYLE DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_POPUP | WS_SYSMENU +CAPTION "Play Movie" +FONT 8, "MS Shell Dlg", 0, 0, 0 +{ + DEFPUSHBUTTON "OK", 1, 186, 60, 50, 14, BS_DEFPUSHBUTTON + GROUPBOX "", 65497, 3, 0, 293, 57 + RTEXT "File:", 65498, 7, 14, 24, 10, NOT WS_GROUP | SS_RIGHT | SS_CENTERIMAGE + PUSHBUTTON "Cancel", 2, 246, 60, 50, 14, BS_PUSHBUTTON + AUTOCHECKBOX "Open &Read-Only", IDC_CHECK_READONLY, 18, 33, 69, 10, BS_AUTOCHECKBOX + PUSHBUTTON "Browse...", ID_BROWSE, 224, 12, 50, 14, BS_PUSHBUTTON + EDITTEXT PM_FILENAME, 35, 12, 184, 14, ES_AUTOHSCROLL | ES_READONLY +} + + + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +IDD_REPLAY_METADATA DIALOGEX 0, 0, 325, 250 +STYLE DS_MODALFRAME | DS_SHELLFONT | WS_VISIBLE | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_POPUP | WS_SYSMENU +CAPTION "Movie Metadata" +FONT 8, "MS Shell Dlg", 400, 0, 1 +{ + DEFPUSHBUTTON "Close", IDCANCEL, 268, 229, 50, 14, BS_DEFPUSHBUTTON + CONTROL "", 1130, WC_LISTVIEW, WS_TABSTOP | WS_BORDER | LVS_ALIGNLEFT | LVS_REPORT, 7, 7, 311, 214 +} + + + LANGUAGE LANG_DANISH, SUBLANG_DANISH_DENMARK IDD_SOUNDSETTINGS DIALOGEX 0, 0, 174, 96 STYLE DS_CENTER | DS_MODALFRAME | DS_SETFONT | WS_VISIBLE | WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_POPUP | WS_SYSMENU @@ -2341,7 +2515,7 @@ FONT 8, "MS Sans Serif", 0, 0, 1 { COMBOBOX IDC_PAL_SELECT, 4, 13, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWN LTEXT "Pal : 0", IDC_PALNUM, 4, 31, 25, 8, SS_LEFT - SCROLLBAR IDC_SCROLLER, 30, 28, 20, 14, WS_VISIBLE | WS_VISIBLE + SCROLLBAR IDC_SCROLLER, 30, 28, 20, 14, NOT WS_VISIBLE | NOT WS_VISIBLE COMBOBOX IDC_MEM_SELECT, 4, 50, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWN AUTORADIOBUTTON "Bitmap", IDC_BITMAP, 4, 68, 35, 10, WS_GROUP | WS_TABSTOP | BS_AUTORADIOBUTTON AUTORADIOBUTTON "256 Farver", IDC_256COUL, 4, 80, 55, 10, WS_TABSTOP | BS_AUTORADIOBUTTON @@ -2366,7 +2540,7 @@ FONT 8, "MS Sans Serif", 0, 0, 1 { COMBOBOX IDC_PAL_SELECT, 4, 13, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWN LTEXT "Pal : 0", IDC_PALNUM, 4, 31, 25, 8, SS_LEFT - SCROLLBAR IDC_SCROLLER, 30, 28, 20, 14, WS_VISIBLE | WS_VISIBLE + SCROLLBAR IDC_SCROLLER, 30, 28, 20, 14, NOT WS_VISIBLE | NOT WS_VISIBLE COMBOBOX IDC_MEM_SELECT, 4, 50, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWN AUTORADIOBUTTON "Bitmap", IDC_BITMAP, 4, 68, 35, 10, WS_GROUP | WS_TABSTOP | BS_AUTORADIOBUTTON AUTORADIOBUTTON "256 colors", IDC_256COUL, 4, 80, 55, 10, WS_TABSTOP | BS_AUTORADIOBUTTON @@ -2391,7 +2565,7 @@ FONT 8, "MS Sans Serif", 0, 0, 1 { COMBOBOX IDC_PAL_SELECT, 4, 13, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWN LTEXT "Pal : 0", IDC_PALNUM, 4, 31, 25, 8, SS_LEFT - SCROLLBAR IDC_SCROLLER, 30, 28, 20, 14, WS_VISIBLE | WS_VISIBLE + SCROLLBAR IDC_SCROLLER, 30, 28, 20, 14, NOT WS_VISIBLE | NOT WS_VISIBLE COMBOBOX IDC_MEM_SELECT, 4, 50, 90, 14, WS_TABSTOP | WS_TABSTOP | CBS_DROPDOWN AUTORADIOBUTTON "Bitmap", IDC_BITMAP, 4, 68, 35, 10, WS_GROUP | WS_TABSTOP | BS_AUTORADIOBUTTON AUTORADIOBUTTON "256 couleurs", IDC_256COUL, 4, 80, 55, 10, WS_TABSTOP | BS_AUTORADIOBUTTON @@ -2421,13 +2595,3 @@ FONT 8, "Ms Shell Dlg" COMBOBOX IDC_BRIDGEADAPTER, 15, 30, 300, 45, CBS_DROPDOWNLIST | CBS_HASSTRINGS LTEXT "(wifi emulation is not yet complete. this doesn't work.)", IDC_STATIC, 15, 48, 166, 8, SS_LEFT } - - - -// -// Accelerator resources -// -LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL -IDR_MAIN_ACCEL ACCELERATORS -{ -} diff --git a/desmume/src/windows/targetver.h b/desmume/src/windows/targetver.h new file mode 100644 index 000000000..f583181df --- /dev/null +++ b/desmume/src/windows/targetver.h @@ -0,0 +1,24 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef WINVER // Specifies that the minimum required platform is Windows Vista. +#define WINVER 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Specifies that the minimum required platform is Windows 98. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Specifies that the minimum required platform is Internet Explorer 7.0. +#define _WIN32_IE 0x0700 // Change this to the appropriate value to target other versions of IE. +#endif