Browse Source

[WIP] Refactor 2: new configuration model & WP8.1 enhancement

1. New configuration model, with configurable game & save path and configuration file has its specific folder prefix
2. Configuration interface on WP8.1 and WIN32
3. Make inline overlay work on big-endian platforms
4. The first time game starts, it will enter the configuration interface until user disable it. The UI can be re-enabled inside the game through a new menu item.
5. Save slot import & export feature
louyihua 8 years ago
parent
commit
174f929f43
49 changed files with 8063 additions and 3386 deletions
  1. 2 0
      .gitignore
  2. 6 3
      common.h
  3. 201 215
      global.c
  4. 19 10
      global.h
  5. 2 0
      makemessage.py
  6. 2 2
      mp3play.c
  7. 5296 2719
      overlay.c
  8. 186 0
      palcfg.c
  9. 135 0
      palcfg.h
  10. 3 0
      palcommon.h
  11. 2 18
      rixplay.cpp
  12. 4 4
      script.c
  13. 2 2
      sound.c
  14. 0 4
      sound.h
  15. 18 11
      text.c
  16. 1 1
      text.h
  17. 1 0
      ui.h
  18. 24 11
      uigame.c
  19. 17 5
      util.c
  20. 5 5
      util.h
  21. 3 3
      video.c
  22. 7 5
      win32/resource.h
  23. 192 142
      win32/sdlpal.rc
  24. 2 0
      win32/sdlpal.vcxproj
  25. 6 0
      win32/sdlpal.vcxproj.filters
  26. 129 91
      win32/win32.cpp
  27. 0 0
      winrt/SDLPal.Common/SDLPal.Common.def
  28. 35 0
      winrt/SDLPal.Common/SDLPal.cpp
  29. 54 0
      winrt/SDLPal.Common/StringHelper.h
  30. 28 10
      winrt/SDLPal.Common/WinRTIO.cpp
  31. 79 96
      winrt/SDLPal.WindowsPhone/WinRTUtil.cpp
  32. 7 0
      winrt/SDLPal.WindowsPhone/App.xaml
  33. 174 0
      winrt/SDLPal.WindowsPhone/App.xaml.cpp
  34. 38 0
      winrt/SDLPal.WindowsPhone/App.xaml.h
  35. 99 0
      winrt/SDLPal.WindowsPhone/MainPage.xaml
  36. 263 0
      winrt/SDLPal.WindowsPhone/MainPage.xaml.cpp
  37. 43 0
      winrt/SDLPal.WindowsPhone/MainPage.xaml.h
  38. 15 0
      winrt/SDLPal.WindowsPhone/Package.appxmanifest
  39. 16 7
      winrt/SDLPal.WindowsPhone/SDLPal.Common.vcxproj
  40. 4 3
      winrt/SDLPal.WindowsPhone/SDLPal.Common.vcxproj.filters
  41. 10 4
      winrt/SDLPal.WindowsPhone/SDLPal.Core.vcxproj
  42. 6 0
      winrt/SDLPal.WindowsPhone/SDLPal.Core.vcxproj.filters
  43. 53 14
      winrt/SDLPal.WindowsPhone/SDLPal.vcxproj
  44. 39 1
      winrt/SDLPal.WindowsPhone/SDLPal.vcxproj.filters
  45. 273 0
      winrt/SDLPal.WindowsPhone/Strings/en/Resources.resw
  46. 273 0
      winrt/SDLPal.WindowsPhone/Strings/zh-hans/Resources.resw
  47. 273 0
      winrt/SDLPal.WindowsPhone/Strings/zh-hant/Resources.resw
  48. 6 0
      winrt/SDLPal.WindowsPhone/pch.cpp
  49. 10 0
      winrt/SDLPal.WindowsPhone/pch.h

+ 2 - 0
.gitignore

@@ -21,12 +21,14 @@ build/
 bld/
 [Bb]in/
 [Oo]bj/
+Generated Files/
 
 # Visual Studio 2015 cache/options directory
 .vs/
 
 # Visual Studio 2015 intellisense database
 *.VC.db
+*.VC.opendb
 
 # MSTest test Results
 [Tt]est[Rr]esult*/

+ 6 - 3
common.h

@@ -227,10 +227,11 @@ extern "C"
 # define PAL_CREDIT           "Rikku2000"
 # define PAL_PORTYEAR         "2013"
 
-#elif defined (__WINPHONE__)
+#elif defined (__WINPHONE__) || defined(__WINRT__)
 
 #define PAL_PREFIX            UTIL_BasePath()
 #define PAL_SAVE_PREFIX       UTIL_SavePath()
+#define PAL_CONFIG_PREFIX     UTIL_ConfigPath()
 #define PAL_HAS_TOUCH         1
 #define PAL_AUDIO_DEFAULT_BUFFER_SIZE   4096
 #define PAL_DEFAULT_WINDOW_WIDTH   320
@@ -289,7 +290,6 @@ extern "C"
 
 #define PAL_HAS_MP3           1   /* Try always enable MP3. If compilation/run failed, please change this value to 0. */
 #define PAL_HAS_OGG           1   /* Try always enable OGG. If compilation/run failed, please change this value to 0. */
-#define PAL_HAS_MAME          1   /* Use M.A.M.E */
 
 #ifndef SDL_INIT_CDROM
 #define SDL_INIT_CDROM        0	  /* Compatibility with SDL 1.2 */
@@ -299,6 +299,10 @@ extern "C"
 # define SDL_AUDIO_BITSIZE(x)         (x & 0xFF)
 #endif
 
+#ifndef PAL_CONFIG_PREFIX
+# define PAL_CONFIG_PREFIX PAL_PREFIX
+#endif
+
 #ifdef _WIN32
 
 #include <windows.h>
@@ -392,7 +396,6 @@ typedef const WCHAR        *LPCWSTR;
    }
 
 typedef enum tagCODEPAGE {
-	CP_UNKNOWN = -1,
 	CP_MIN = 0,
 	CP_BIG5 = 0,
 	CP_GBK = 1,

+ 201 - 215
global.c

@@ -23,6 +23,7 @@
 
 #include "main.h"
 #include "resampler.h"
+#include "palcfg.h"
 
 static GLOBALVARS _gGlobals;
 GLOBALVARS * const  gpGlobals = &_gGlobals;
@@ -54,24 +55,7 @@ PAL_LoadConfig(
 )
 {
 	FILE     *fp;
-	CODEPAGE  iCodePage = CP_BIG5;		// Default for BIG5
-	DWORD     dwScreenWidth = 0;
-	DWORD     dwScreenHeight = 0;
-	DWORD     dwFullScreen = FALSE;
-	DWORD     dwKeepAspectRatio = TRUE;
-	DWORD     dwIsDOS = 1;				// Default for DOS
-	DWORD     dwUseEmbeddedFonts = 1;	// Default for using embedded fonts in DOS version
-	DWORD     dwUseSurroundOPL = 1;		// Default for using surround opl
-	DWORD     dwUseStereo = 1;			// Default for stereo audio
-#if PAL_HAS_TOUCH
-	DWORD     dwUseTouchOverlay = UTIL_TouchEnabled();
-#endif
-	float     flSurroundOPLOffset = 384.0f;// Default for 384.0
-	INT       iSampleRate = 44100;		// Default for 44100 Hz
-	INT       iOPLSampleRate = 49716;	// Default for 49716 Hz
-	INT       iResampleQuality = RESAMPLER_QUALITY_MAX;	// Default to maximum quality
-	INT       iAudioBufferSize = PAL_AUDIO_DEFAULT_BUFFER_SIZE;
-	INT       iVolume = 100;				// Default for 100%
+	ConfigValue  values[PALCFG_ALL_MAX];
 	MUSICTYPE eMusicType = MUSIC_RIX;
 	MUSICTYPE eCDType = MUSIC_OGG;
 	OPLTYPE   eOPLType = OPL_DOSBOX;
@@ -97,7 +81,9 @@ PAL_LoadConfig(
 		PAL_XY(0, 0), PAL_XY(0, 0)
 	};
 
-	if (fFromFile && (fp = fopen(va("%ssdlpal.cfg", PAL_SAVE_PREFIX), "r")))
+	for (PALCFG_ITEM i = PALCFG_ALL_MIN; i < PALCFG_ALL_MAX; i++) values[i] = PAL_DefaultConfig(i);
+
+	if (fFromFile && (fp = fopen(va("%ssdlpal.cfg", PAL_CONFIG_PREFIX), "r")))
 	{
 		PAL_LARGE char buf[512];
 
@@ -106,197 +92,134 @@ PAL_LoadConfig(
 		//
 		while (fgets(buf, 512, fp) != NULL)
 		{
-			char *p = buf;
-
-			//
-			// Skip leading spaces
-			//
-			while (*p && isspace(*p)) p++;
-
-			//
-			// Skip comments
-			//
-			if (*p && *p != '#')
+			ConfigValue value;
+			const ConfigItem * item;
+			if (PAL_ParseConfigLine(buf, &item, &value))
 			{
-				char *ptr;
-				if (ptr = strchr(p, '='))
+				switch (item->Item)
 				{
-					char *end = ptr - 1;
-					*ptr++ = 0;
-
-					//
-					// Skip tailing & leading spaces
-					//
-					while (isspace(*end) && end >= p) *end-- = 0;
-
-					if (SDL_strcasecmp(p, "CODEPAGE") == 0)
-					{
-						sscanf(ptr, "%d", &iCodePage);
-						if (iCodePage < 0) iCodePage = 0;
-						if (iCodePage >= CP_MAX) iCodePage = CP_MAX - 1;
-					}
-					else if (SDL_strcasecmp(p, "DOS") == 0)
-					{
-						sscanf(ptr, "%u", &dwIsDOS);
-					}
-					else if (SDL_strcasecmp(p, "USEEMBEDDEDFONTS") == 0)
-					{
-						sscanf(ptr, "%u", &dwUseEmbeddedFonts);
-					}
-					else if (SDL_strcasecmp(p, "USESURROUNDOPL") == 0)
-					{
-						sscanf(ptr, "%u", &dwUseSurroundOPL);
-					}
-					else if (SDL_strcasecmp(p, "STEREO") == 0)
-					{
-						sscanf(ptr, "%u", &dwUseStereo);
-					}
-					else if (SDL_strcasecmp(p, "SAMPLERATE") == 0)
-					{
-						sscanf(ptr, "%d", &iSampleRate);
-						if (iSampleRate > PAL_MAX_SAMPLERATE) iSampleRate = PAL_MAX_SAMPLERATE;
-					}
-					else if (SDL_strcasecmp(p, "OPLSAMPLERATE") == 0)
-					{
-						sscanf(ptr, "%d", &iOPLSampleRate);
-					}
-					else if (SDL_strcasecmp(p, "RESAMPLEQUALITY") == 0)
-					{
-						sscanf(ptr, "%d", &iResampleQuality);
-					}
-					else if (SDL_strcasecmp(p, "SURROUNDOPLOFFSET") == 0)
-					{
-						sscanf(ptr, "%f", &flSurroundOPLOffset);
-					}
-					else if (SDL_strcasecmp(p, "WINDOWWIDTH") == 0)
-					{
-						sscanf(ptr, "%u", &dwScreenWidth);
-					}
-					else if (SDL_strcasecmp(p, "WINDOWHEIGHT") == 0)
-					{
-						sscanf(ptr, "%u", &dwScreenHeight);
-					}
-					else if (SDL_strcasecmp(p, "FULLSCREEN") == 0)
-					{
-						sscanf(ptr, "%u", &dwFullScreen);
-					}
-					else if (SDL_strcasecmp(p, "KEEPASPECTRATIO") == 0)
-					{
-						sscanf(ptr, "%u", &dwKeepAspectRatio);
-					}
-#if PAL_HAS_TOUCH
-					else if (SDL_strcasecmp(p, "USETOUCHOVERLAY") == 0)
+				case PALCFG_AUDIOBUFFERSIZE:
+					if ((value.uValue & (value.uValue - 1)) != 0)
 					{
-						sscanf(ptr, "%d", &dwUseTouchOverlay);
+						/* Make sure iAudioBufferSize is power of 2 */
+						int n = 0;
+						while (value.uValue) { value.uValue >>= 1; n++; }
+						value.uValue = 1 << (n - 1);
 					}
-#endif
-					else if (SDL_strcasecmp(p, "AUDIOBUFFERSIZE") == 0)
+					values[item->Item] = value;
+					break;
+				case PALCFG_MESSAGEFILE:
+				{
+					int n = strlen(value.sValue);
+					while (n > 0 && isspace(value.sValue[n - 1])) n--;
+					if (n > 0)
 					{
-						sscanf(ptr, "%d", &iAudioBufferSize);
-						if (iAudioBufferSize > 32768)
-							iAudioBufferSize = 32768;
-						else if (iAudioBufferSize < 2)
-							iAudioBufferSize = 2;
-						if ((iAudioBufferSize & (iAudioBufferSize - 1)) != 0)
-						{
-							/* Make sure iAudioBufferSize is power of 2 */
-							int n = 0;
-							while (iAudioBufferSize) { iAudioBufferSize >>= 1; n++; }
-							iAudioBufferSize = 1 << (n - 1);
-						}
+						gConfig.pszMsgFile = (char *)realloc(gConfig.pszMsgFile, n + 1);
+						memcpy(gConfig.pszMsgFile, value.sValue, n);
+						gConfig.pszMsgFile[n] = '\0';
 					}
-					else if (SDL_strcasecmp(p, "VOLUME") == 0)
+					break;
+				}
+				case PALCFG_GAMEPATH:
+				{
+					int n = strlen(value.sValue);
+					while (n > 0 && isspace(value.sValue[n - 1])) n--;
+					if (n > 0)
 					{
-						sscanf(ptr, "%d", &iVolume);
-						if (iVolume > 100)
-							iVolume = 100;
-						else if (iVolume < 0)
-							iVolume = 0;
+						gConfig.pszGamePath = (char *)realloc(gConfig.pszGamePath, n + 1);
+						memcpy(gConfig.pszGamePath, value.sValue, n);
+						gConfig.pszGamePath[n] = '\0';
 					}
-					else if (SDL_strcasecmp(p, "MESSAGEFILENAME") == 0)
+					break;
+				}
+				case PALCFG_SAVEPATH:
+				{
+					int n = strlen(value.sValue);
+					while (n > 0 && isspace(value.sValue[n - 1])) n--;
+					if (n > 0)
 					{
-						int n = strlen(ptr);
-						if (n > 0 && ptr[n - 1] == '\n') ptr[--n] = 0;
-						if (n > 0 && ptr[n - 1] == '\r') ptr[--n] = 0;
-						if (n > 0) gConfig.pszMsgName = strdup(ptr);
+						gConfig.pszSavePath = (char *)realloc(gConfig.pszSavePath, n + 1);
+						memcpy(gConfig.pszSavePath, value.sValue, n);
+						gConfig.pszSavePath[n] = '\0';
 					}
+					break;
+				}
+				case PALCFG_CD:
+				{
+					if (PAL_HAS_MP3 && SDL_strncasecmp(value.sValue, "MP3", 3) == 0)
+						eCDType = MUSIC_MP3;
+					else if (PAL_HAS_OGG && SDL_strncasecmp(value.sValue, "OGG", 3) == 0)
+						eCDType = MUSIC_OGG;
+					else if (PAL_HAS_SDLCD && SDL_strncasecmp(value.sValue, "RAW", 3) == 0)
+						eCDType = MUSIC_SDLCD;
+					break;
+				}
+				case PALCFG_MUSIC:
+				{
+					if (PAL_HAS_NATIVEMIDI && SDL_strncasecmp(value.sValue, "MIDI", 4) == 0)
+						eMusicType = MUSIC_MIDI;
+					else if (PAL_HAS_MP3 && SDL_strncasecmp(value.sValue, "MP3", 3) == 0)
+						eMusicType = MUSIC_MP3;
+					else if (PAL_HAS_OGG && SDL_strncasecmp(value.sValue, "OGG", 3) == 0)
+						eMusicType = MUSIC_OGG;
+					else if (SDL_strncasecmp(value.sValue, "RIX", 3) == 0)
+						eMusicType = MUSIC_RIX;
+					break;
+				}
+				case PALCFG_OPL:
+				{
+					if (SDL_strncasecmp(value.sValue, "DOSBOX", 6) == 0)
+						eOPLType = OPL_DOSBOX;
+					else if (SDL_strncasecmp(value.sValue, "MAME", 4) == 0)
+						eOPLType = OPL_MAME;
+					break;
+				}
+				case PALCFG_RIXEXTRAINIT:
+				{
 #if USE_RIX_EXTRA_INIT
-					else if (SDL_strcasecmp(p, "RIXEXTRAINIT") == 0)
+					int n = 1;
+					char *p;
+					for (p = ptr; *p < *end; p++)
 					{
-						int n = 1;
-						char *p;
-						for (p = ptr; *p < *end; p++)
-						{
-							if (*p == ',')
-								n++;
-						}
-						n &= ~0x1;
+						if (*p == ',')
+							n++;
+					}
+					n &= ~0x1;
 
-						if (n > 0)
+					if (n > 0)
+					{
+						uint32_t *regs = malloc(sizeof(uint32_t) * (n >> 1));
+						uint8_t *vals = malloc(sizeof(uint8_t) * (n >> 1));
+						uint32_t d, i, v = 1;
+						if (regs && vals)
 						{
-							uint32_t *regs = malloc(sizeof(uint32_t) * (n >> 1));
-							uint8_t *vals = malloc(sizeof(uint8_t) * (n >> 1));
-							uint32_t d, i, v = 1;
-							if (regs && vals)
+							for (p = ptr, i = 0; *p < *end; p++, i++)
 							{
-								for (p = ptr, i = 0; *p < *end; p++, i++)
-								{
-									if (sscanf(p, "%u", &regs[i]) == 0) { v = 0; break; }
-									while (*p < *end && *p != ',') p++; p++;
-									if (sscanf(p, "%u", &d) == 0) { v = 0; break; }
-									while (*p < *end && *p != ',') p++;
-									vals[i] = (uint8_t)d;
-								}
-								if (v)
-								{
-									gConfig.pExtraFMRegs = regs;
-									gConfig.pExtraFMVals = vals;
-									gConfig.dwExtraLength = n >> 1;
-								}
-								else
-								{
-									free(regs);
-									free(vals);
-								}
+								if (sscanf(p, "%u", &regs[i]) == 0) { v = 0; break; }
+								while (*p < *end && *p != ',') p++; p++;
+								if (sscanf(p, "%u", &d) == 0) { v = 0; break; }
+								while (*p < *end && *p != ',') p++;
+								vals[i] = (uint8_t)d;
+							}
+							if (v)
+							{
+								gConfig.pExtraFMRegs = regs;
+								gConfig.pExtraFMVals = vals;
+								gConfig.dwExtraLength = n >> 1;
+							}
+							else
+							{
+								free(regs);
+								free(vals);
 							}
 						}
 					}
 #endif
-					else if (SDL_strcasecmp(p, "CD") == 0)
-					{
-						char cd_type[32];
-						sscanf(ptr, "%31s", cd_type);
-						if (PAL_HAS_MP3 && SDL_strcasecmp(cd_type, "MP3") == 0)
-							eCDType = MUSIC_MP3;
-						else if (PAL_HAS_OGG && SDL_strcasecmp(cd_type, "OGG") == 0)
-							eCDType = MUSIC_OGG;
-						else if (PAL_HAS_SDLCD && SDL_strcasecmp(cd_type, "RAW") == 0)
-							eCDType = MUSIC_SDLCD;
-					}
-					else if (SDL_strcasecmp(p, "MUSIC") == 0)
-					{
-						char music_type[32];
-						sscanf(ptr, "%31s", music_type);
-						if (PAL_HAS_NATIVEMIDI && SDL_strcasecmp(music_type, "MIDI") == 0)
-							eMusicType = MUSIC_MIDI;
-						else if (PAL_HAS_MP3 && SDL_strcasecmp(music_type, "MP3") == 0)
-							eMusicType = MUSIC_MP3;
-						else if (PAL_HAS_OGG && SDL_strcasecmp(music_type, "OGG") == 0)
-							eMusicType = MUSIC_OGG;
-						else if (SDL_strcasecmp(music_type, "RIX") == 0)
-							eMusicType = MUSIC_RIX;
-					}
-					else if (SDL_strcasecmp(p, "OPL") == 0)
-					{
-						char opl_type[32];
-						sscanf(ptr, "%31s", opl_type);
-						if (SDL_strcasecmp(opl_type, "DOSBOX") == 0)
-							eOPLType = OPL_DOSBOX;
-						else if (SDL_strcasecmp(opl_type, "DOSBOXOLD") == 0)
-							eOPLType = OPL_DOSBOX_OLD;
-						else if (PAL_HAS_MAME && SDL_strcasecmp(opl_type, "MAME") == 0)
-							eOPLType = OPL_MAME;
-					}
+					break;
+				}
+				default:
+					values[item->Item] = value;
+					break;
 				}
 			}
 		}
@@ -307,40 +230,101 @@ PAL_LoadConfig(
 	//
 	// Set configurable global options
 	//
-	gConfig.fIsWIN95 = dwIsDOS ? FALSE : TRUE;
-	gConfig.fUseEmbeddedFonts = dwIsDOS && dwUseEmbeddedFonts ? TRUE : FALSE;
-	gConfig.fUseSurroundOPL = dwUseStereo && dwUseSurroundOPL ? TRUE : FALSE;
-	gConfig.iAudioChannels = dwUseStereo ? 2 : 1;
-#if PAL_HAS_TOUCH
-	gConfig.fUseTouchOverlay = dwUseTouchOverlay ? TRUE : FALSE;
-#endif
-	gConfig.iSampleRate = iSampleRate;
-	gConfig.iOPLSampleRate = iOPLSampleRate;
-	gConfig.iResampleQuality = iResampleQuality;
-	gConfig.dSurroundOPLOffset = flSurroundOPLOffset;
+	if (!gConfig.pszGamePath) gConfig.pszGamePath = strdup(PAL_PREFIX);
+	if (!gConfig.pszSavePath) gConfig.pszSavePath = strdup(PAL_SAVE_PREFIX);
 	gConfig.eMusicType = eMusicType;
 	gConfig.eCDType = eCDType;
 	gConfig.eOPLType = eOPLType;
-	gConfig.iCodePage = iCodePage;
 	gConfig.dwWordLength = 10;	// This is the default value for Chinese version
-	gConfig.wAudioBufferSize = (WORD)iAudioBufferSize;
-	gConfig.iVolume = SDL_MIX_MAXVOLUME * iVolume / 100;
-	if (UTIL_GetScreenSize(&dwScreenWidth, &dwScreenHeight))
+	gConfig.ScreenLayout = screen_layout;
+
+	gConfig.fIsWIN95 = !values[PALCFG_DOS].bValue;
+	gConfig.fUseEmbeddedFonts = values[PALCFG_DOS].bValue && values[PALCFG_USEEMBEDDEDFONTS].bValue;
+	gConfig.fUseSurroundOPL = values[PALCFG_STEREO].bValue && values[PALCFG_USESURROUNDOPL].bValue;
+	gConfig.fLaunchSetting = values[PALCFG_LAUNCHSETTING].bValue;
+#if PAL_HAS_TOUCH
+	gConfig.fUseTouchOverlay = values[PALCFG_USETOUCHOVERLAY].bValue;
+#endif
+#if SDL_VERSION_ATLEAST(2,0,0)
+	gConfig.fKeepAspectRatio = values[PALCFG_KEEPASPECTRATIO].bValue;
+#else
+	gConfig.fFullScreen = values[PALCFG_FULLSCREEN].bValue;
+#endif
+	gConfig.iAudioChannels = values[PALCFG_STEREO].bValue ? 2 : 1;
+
+	gConfig.iSurroundOPLOffset = values[PALCFG_SURROUNDOPLOFFSET].iValue;
+
+	gConfig.iSampleRate = values[PALCFG_SAMPLERATE].uValue;
+	gConfig.iOPLSampleRate = values[PALCFG_OPLSAMPLERATE].uValue;
+	gConfig.iResampleQuality = values[PALCFG_RESAMPLEQUALITY].uValue;
+	gConfig.uCodePage = values[PALCFG_CODEPAGE].uValue;
+	gConfig.wAudioBufferSize = (WORD)values[PALCFG_AUDIOBUFFERSIZE].uValue;
+	gConfig.iVolume = SDL_MIX_MAXVOLUME * values[PALCFG_VOLUME].uValue / 100;
+
+	if (UTIL_GetScreenSize(&values[PALCFG_WINDOWWIDTH].uValue, &values[PALCFG_WINDOWHEIGHT].uValue))
 	{
-		gConfig.dwScreenWidth = dwScreenWidth;
-		gConfig.dwScreenHeight = dwScreenHeight;
+		gConfig.dwScreenWidth = values[PALCFG_WINDOWWIDTH].uValue;
+		gConfig.dwScreenHeight = values[PALCFG_WINDOWHEIGHT].uValue;
 	}
 	else
 	{
 		gConfig.dwScreenWidth = PAL_DEFAULT_WINDOW_WIDTH;
 		gConfig.dwScreenHeight = PAL_DEFAULT_WINDOW_HEIGHT;
 	}
+}
+
+
+BOOL
+PAL_SaveConfig(
+	VOID
+)
+{
+	static const char *music_types[] = { "RIX", "MIDI", "MP3", "OGG", "RAW" };
+	static const char *opl_types[] = { "DOSBOX", "MAME" };
+	char buf[512];
+	FILE *fp = fopen(va("%ssdlpal.cfg", PAL_CONFIG_PREFIX), "w");
+
+	if (fp)
+	{
+		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_DOS), !gConfig.fIsWIN95); fputs(buf, fp);
 #if SDL_VERSION_ATLEAST(2,0,0)
-	gConfig.fKeepAspectRatio = dwKeepAspectRatio ? TRUE : FALSE;
+		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_KEEPASPECTRATIO), gConfig.fKeepAspectRatio); fputs(buf, fp);
 #else
-	gConfig.fFullScreen = dwFullScreen ? TRUE : FALSE;
+		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_FULLSCREEN), gConfig.fKeepAspectRatio); fputs(buf, fp);
 #endif
-	gConfig.ScreenLayout = screen_layout;
+		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_LAUNCHSETTING), gConfig.fLaunchSetting); fputs(buf, fp);
+		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_STEREO), gConfig.iAudioChannels == 2 ? TRUE : FALSE); fputs(buf, fp);
+		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_USEEMBEDDEDFONTS), gConfig.fUseEmbeddedFonts); fputs(buf, fp);
+		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_USESURROUNDOPL), gConfig.fUseSurroundOPL); fputs(buf, fp);
+#if PAL_HAS_TOUCH
+		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_USETOUCHOVERLAY), gConfig.fUseTouchOverlay); fputs(buf, fp);
+#endif
+
+		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_SURROUNDOPLOFFSET), gConfig.iSurroundOPLOffset); fputs(buf, fp);
+
+		sprintf(buf, "%s=%u\n", PAL_ConfigName(PALCFG_AUDIOBUFFERSIZE), gConfig.wAudioBufferSize); fputs(buf, fp);
+		sprintf(buf, "%s=%u\n", PAL_ConfigName(PALCFG_CODEPAGE), gConfig.uCodePage); fputs(buf, fp);
+		sprintf(buf, "%s=%u\n", PAL_ConfigName(PALCFG_OPLSAMPLERATE), gConfig.iOPLSampleRate); fputs(buf, fp);
+		sprintf(buf, "%s=%u\n", PAL_ConfigName(PALCFG_RESAMPLEQUALITY), gConfig.iResampleQuality); fputs(buf, fp);
+		sprintf(buf, "%s=%u\n", PAL_ConfigName(PALCFG_SAMPLERATE), gConfig.iSampleRate); fputs(buf, fp);
+		sprintf(buf, "%s=%u\n", PAL_ConfigName(PALCFG_VOLUME), gConfig.iVolume); fputs(buf, fp);
+		sprintf(buf, "%s=%u\n", PAL_ConfigName(PALCFG_WINDOWHEIGHT), gConfig.dwScreenHeight); fputs(buf, fp);
+		sprintf(buf, "%s=%u\n", PAL_ConfigName(PALCFG_WINDOWWIDTH), gConfig.dwScreenWidth); fputs(buf, fp);
+
+		sprintf(buf, "%s=%s\n", PAL_ConfigName(PALCFG_CD), music_types[gConfig.eCDType]); fputs(buf, fp);
+		sprintf(buf, "%s=%s\n", PAL_ConfigName(PALCFG_MUSIC), music_types[gConfig.eMusicType]); fputs(buf, fp);
+		sprintf(buf, "%s=%s\n", PAL_ConfigName(PALCFG_OPL), opl_types[gConfig.eOPLType]); fputs(buf, fp);
+
+		if (gConfig.pszGamePath) { sprintf(buf, "%s=%s\n", PAL_ConfigName(PALCFG_GAMEPATH), gConfig.pszGamePath); fputs(buf, fp); }
+		if (gConfig.pszSavePath) { sprintf(buf, "%s=%s\n", PAL_ConfigName(PALCFG_SAVEPATH), gConfig.pszSavePath); fputs(buf, fp); }
+		if (gConfig.pszMsgFile) { sprintf(buf, "%s=%s\n", PAL_ConfigName(PALCFG_MESSAGEFILE), gConfig.pszMsgFile); fputs(buf, fp); }
+
+		fclose(fp);
+
+		return TRUE;
+	}
+	else
+		return FALSE;
 }
 
 INT
@@ -354,7 +338,7 @@ PAL_InitGlobals(
 
   Parameters:
 
-    [IN]  iCodePage - the code page for text conversion.
+    [IN]  uCodePage - the code page for text conversion.
 	[IN]  dwWordLength - the length of each word.
 
   Return value:
@@ -380,7 +364,7 @@ PAL_InitGlobals(
    gpGlobals->f.fpRGM = UTIL_OpenRequiredFile("rgm.mkf");
    gpGlobals->f.fpSSS = UTIL_OpenRequiredFile("sss.mkf");
 
-   gpGlobals->lpObjectDesc = gConfig.fIsWIN95 ? NULL : PAL_LoadObjectDesc(va("%s%s", PAL_PREFIX, "desc.dat"));
+   gpGlobals->lpObjectDesc = gConfig.fIsWIN95 ? NULL : PAL_LoadObjectDesc(va("%s%s", gConfig.pszGamePath, "desc.dat"));
    gpGlobals->bCurrentSaveSlot = 1;
 
    return 0;
@@ -440,7 +424,9 @@ PAL_FreeGlobals(
    free(gConfig.pExtraFMVals);
    free(gConfig.dwExtraLength);
 #endif
-   free(gConfig.pszMsgName);
+   free(gConfig.pszMsgFile);
+   free(gConfig.pszGamePath);
+   free(gConfig.pszSavePath);
 
    //
    // Clear the instance
@@ -1153,7 +1139,7 @@ PAL_InitGameData(
    //
    // try loading from the saved game file.
    //
-   if (iSaveSlot == 0 || PAL_LoadGame(va("%s%d%s", PAL_SAVE_PREFIX, iSaveSlot, ".rpg")) != 0)
+   if (iSaveSlot == 0 || PAL_LoadGame(va("%s%d%s", gConfig.pszSavePath, iSaveSlot, ".rpg")) != 0)
    {
       //
       // Cannot load the saved game file. Load the defaults.

+ 19 - 10
global.h

@@ -91,7 +91,9 @@ extern "C"
 #define     OBJECT_MAGIC_START           0x127
 #define     OBJECT_MAGIC_END             0x18D
 
-#define     MINIMAL_WORD_COUNT           (MAX_OBJECTS + 12)
+#define     MINIMAL_WORD_COUNT           (MAX_OBJECTS + 13)
+
+#define     PAL_MAX_SAMPLERATE           48000
 
 // status of characters
 typedef enum tagSTATUS
@@ -559,7 +561,6 @@ typedef enum tagMUSICTYPE
 
 typedef enum tagOPLTYPE
 {
-	OPL_DOSBOX_OLD,
 	OPL_DOSBOX,
 	OPL_MAME
 } OPLTYPE, *LPOPLTYPE;
@@ -668,17 +669,14 @@ typedef struct tagCONFIGURATION
 	}                ScreenLayoutFlag[sizeof(SCREENLAYOUT) / sizeof(PAL_POS)];
 
 	/* Configurable options */
-	char            *pszMsgName;
-#if USE_RIX_EXTRA_INIT
-	uint32_t        *pExtraFMRegs;
-	uint8_t         *pExtraFMVals;
-	uint32_t         dwExtraLength;
-#endif
-	CODEPAGE         iCodePage;
+	char            *pszGamePath;
+	char            *pszSavePath;
+	char            *pszMsgFile;
+	CODEPAGE         uCodePage;
 	DWORD            dwWordLength;
 	DWORD            dwScreenWidth;
 	DWORD            dwScreenHeight;
-	double           dSurroundOPLOffset;
+	INT              iSurroundOPLOffset;
 	INT              iAudioChannels;
 	INT              iSampleRate;
 	INT              iOPLSampleRate;
@@ -698,9 +696,15 @@ typedef struct tagCONFIGURATION
 #endif
 	BOOL             fEnableJoyStick;
 	BOOL             fUseCustomScreenLayout;
+	BOOL             fLaunchSetting;
 #if PAL_HAS_TOUCH
 	BOOL             fUseTouchOverlay;
 #endif
+#if USE_RIX_EXTRA_INIT
+	uint32_t        *pExtraFMRegs;
+	uint8_t         *pExtraFMVals;
+	uint32_t         dwExtraLength;
+#endif
 } CONFIGURATION, *LPCONFIGURATION;
 
 extern CONFIGURATION gConfig;
@@ -710,6 +714,11 @@ PAL_LoadConfig(
    BOOL fFromFile
 );
 
+BOOL
+PAL_SaveConfig(
+	VOID
+);
+
 INT
 PAL_InitGlobals(
    VOID

+ 2 - 0
makemessage.py

@@ -220,6 +220,8 @@ def main():
     output += "609=3\n"
     output += "610=4\n"
     output += "611=5\n"
+    output += "# The following word is used to ask user whether to launch setting interface on next game start.\n"
+    output += "612=Setting\n"
     output += "[END WORDS]\n\n"
 
     output += "# The following sections contain dialog/description texts used by the game.\n\n"

+ 2 - 2
mp3play.c

@@ -116,9 +116,9 @@ MP3_Play(
 
 	if (iNum > 0)
 	{
-		if ((player->pMP3 = mad_openFile(va("%smp3/%.2d.mp3", PAL_PREFIX, iNum), SOUND_GetAudioSpec(), gConfig.iResampleQuality)) == NULL)
+		if ((player->pMP3 = mad_openFile(va("%smp3/%.2d.mp3", gConfig.pszGamePath, iNum), SOUND_GetAudioSpec(), gConfig.iResampleQuality)) == NULL)
 		{
-			player->pMP3 = mad_openFile(va("%sMP3/%.2d.MP3", PAL_PREFIX, iNum), SOUND_GetAudioSpec(), gConfig.iResampleQuality);
+			player->pMP3 = mad_openFile(va("%sMP3/%.2d.MP3", gConfig.pszGamePath, iNum), SOUND_GetAudioSpec(), gConfig.iResampleQuality);
 		}
 
 		if (player->pMP3)

File diff suppressed because it is too large
+ 5296 - 2719
overlay.c


+ 186 - 0
palcfg.c

@@ -0,0 +1,186 @@
+
+#include "global.h"
+#include "resampler.h"
+#include "palcfg.h"
+#include <stdint.h>
+
+static const ConfigItem gConfigItems[PALCFG_ALL_MAX] = {
+	{ PALCFG_DOS,               PALCFG_BOOLEAN,  "DOS",                3, TRUE,  FALSE, TRUE },								// Default for DOS
+	{ PALCFG_FULLSCREEN,        PALCFG_BOOLEAN,  "FULLSCREEN",        10, FALSE, FALSE, TRUE },
+	{ PALCFG_KEEPASPECTRATIO,   PALCFG_BOOLEAN,  "KEEPASPECTRATIO",   15, TRUE,  FALSE, TRUE },
+	{ PALCFG_LAUNCHSETTING,     PALCFG_BOOLEAN,  "LAUNCHSETTING",     13, TRUE,  FALSE, TRUE },
+	{ PALCFG_STEREO,            PALCFG_BOOLEAN,  "STEREO",             6, TRUE,  FALSE, TRUE },								// Default for stereo audio
+	{ PALCFG_USEEMBEDDEDFONTS,  PALCFG_BOOLEAN,  "USEEMBEDDEDFONTS",  16, TRUE,  FALSE, TRUE },								// Default for using embedded fonts in DOS version
+	{ PALCFG_USESURROUNDOPL,    PALCFG_BOOLEAN,  "USESURROUNDOPL",    14, TRUE,  FALSE, TRUE },								// Default for using surround opl
+	{ PALCFG_USETOUCHOVERLAY,   PALCFG_BOOLEAN,  "USETOUCHOVERLAY",   15, TRUE,  FALSE, TRUE },
+
+	{ PALCFG_SURROUNDOPLOFFSET, PALCFG_INTEGER,  "SURROUNDOPLOFFSET", 17, 384,   INT32_MIN, INT32_MAX },
+
+	{ PALCFG_AUDIOBUFFERSIZE,   PALCFG_UNSIGNED, "AUDIOBUFFERSIZE",   15, PAL_AUDIO_DEFAULT_BUFFER_SIZE, 2, 32768 },
+	{ PALCFG_CODEPAGE,          PALCFG_UNSIGNED, "CODEPAGE",           8, CP_BIG5, CP_BIG5, CP_MAX - 1 },											// Default for BIG5
+	{ PALCFG_OPLSAMPLERATE,     PALCFG_UNSIGNED, "OPLSAMPLERATE",     13, 49716,   0, UINT32_MAX },
+	{ PALCFG_RESAMPLEQUALITY,   PALCFG_UNSIGNED, "RESAMPLEQUALITY",   15, RESAMPLER_QUALITY_MAX, RESAMPLER_QUALITY_MIN, RESAMPLER_QUALITY_MAX },	// Default for best quality
+	{ PALCFG_SAMPLERATE,        PALCFG_UNSIGNED, "SAMPLERATE",        10, 44100,   0, PAL_MAX_SAMPLERATE },
+	{ PALCFG_VOLUME,            PALCFG_UNSIGNED, "VOLUME",             6, 100,     0, 100 },														// Default for maximum volume
+	{ PALCFG_WINDOWHEIGHT,      PALCFG_UNSIGNED, "WINDOWHEIGHT",      12, PAL_DEFAULT_WINDOW_HEIGHT, 0, UINT32_MAX },
+	{ PALCFG_WINDOWWIDTH,       PALCFG_UNSIGNED, "WINDOWWIDTH",       11, PAL_DEFAULT_WINDOW_WIDTH,  0, UINT32_MAX },
+
+	{ PALCFG_CD,                PALCFG_STRING,   "CD",                 2, "OGG", NULL, NULL },
+	{ PALCFG_GAMEPATH,          PALCFG_STRING,   "GAMEPATH",           8, NULL, NULL, NULL },
+	{ PALCFG_MESSAGEFILE,       PALCFG_STRING,   "MESSAGEFILENAME",   15, NULL, NULL, NULL },
+	{ PALCFG_MUSIC,             PALCFG_STRING,   "MUSIC",              5, "RIX", NULL, NULL },
+	{ PALCFG_OPL,               PALCFG_STRING,   "OPL",                3, "DOSBOX", NULL, NULL },
+	{ PALCFG_RIXEXTRAINIT,      PALCFG_STRING,   "RIXEXTRAINIT",      12, NULL, NULL, NULL },
+	{ PALCFG_SAVEPATH,          PALCFG_STRING,   "SAVEPATH",           8, NULL, NULL, NULL },
+};
+
+
+BOOL
+PAL_ParseConfigLine(
+	const char * line,
+	const ConfigItem ** ppItem,
+	ConfigValue * pValue
+)
+{
+	//
+	// Skip leading spaces
+	//
+	while (*line && isspace(*line)) line++;
+
+	//
+	// Skip comments
+	//
+	if (*line && *line != '#')
+	{
+		const char *ptr;
+		if (ptr = strchr(line, '='))
+		{
+			const char *end = ptr++;
+
+			//
+			// Skip tailing spaces
+			//
+			while (end > line && isspace(end[-1])) end--;
+
+			int len = end - line;
+
+			for (int i = 0; i < sizeof(gConfigItems) / sizeof(ConfigItem); i++)
+			{
+				if (gConfigItems[i].NameLength == len &&
+					SDL_strncasecmp(line, gConfigItems[i].Name, len) == 0)
+				{
+					if (ppItem) *ppItem = &gConfigItems[i];
+					if (pValue)
+					{
+						if (gConfigItems[i].Type != PALCFG_STRING)
+						{
+							switch (gConfigItems[i].Type)
+							{
+							case PALCFG_UNSIGNED:
+								sscanf(ptr, "%u", &pValue->uValue);
+								if (pValue->uValue < gConfigItems[i].MinValue.uValue)
+									pValue->uValue = gConfigItems[i].MinValue.uValue;
+								else if (pValue->uValue > gConfigItems[i].MaxValue.uValue)
+									pValue->uValue = gConfigItems[i].MaxValue.uValue;
+								break;
+							case PALCFG_INTEGER:
+								sscanf(ptr, "%d", &pValue->iValue);
+								if (pValue->iValue < gConfigItems[i].MinValue.iValue)
+									pValue->iValue = gConfigItems[i].MinValue.iValue;
+								else if (pValue->iValue > gConfigItems[i].MaxValue.iValue)
+									pValue->iValue = gConfigItems[i].MaxValue.iValue;
+								break;
+							case PALCFG_BOOLEAN:
+								sscanf(ptr, "%d", &pValue->bValue);
+								pValue->bValue = pValue->bValue ? TRUE : FALSE;
+								break;
+							}
+						}
+						else
+						{
+							//
+							// Skip leading spaces
+							//
+							while (*ptr && isspace(*ptr)) ptr++;
+							pValue->sValue = ptr;
+						}
+						return TRUE;
+					}
+				}
+			}
+		}
+	}
+	return FALSE;
+}
+
+ConfigValue
+PAL_DefaultConfig(
+	PALCFG_ITEM item
+)
+{
+	return gConfigItems[item].DefaultValue;
+}
+
+const char *
+PAL_ConfigName(
+	PALCFG_ITEM item
+)
+{
+	return gConfigItems[item].Name;
+}
+
+BOOL
+PAL_LimitConfig(
+	PALCFG_ITEM item,
+	ConfigValue * pValue
+)
+{
+	if (!pValue) return FALSE;
+
+	switch (gConfigItems[item].Type)
+	{
+	case PALCFG_UNSIGNED:
+		if (pValue->uValue < gConfigItems[item].MinValue.uValue)
+		{
+			pValue->uValue = gConfigItems[item].MinValue.uValue;
+			return TRUE;
+		}
+		else if (pValue->uValue > gConfigItems[item].MaxValue.uValue)
+		{
+			pValue->uValue = gConfigItems[item].MaxValue.uValue;
+			return TRUE;
+		}
+		else
+			return FALSE;
+	case PALCFG_INTEGER:
+		if (pValue->iValue < gConfigItems[item].MinValue.iValue)
+		{
+			pValue->iValue = gConfigItems[item].MinValue.iValue;
+			return TRUE;
+		}
+		else if (pValue->iValue > gConfigItems[item].MaxValue.iValue)
+		{
+			pValue->iValue = gConfigItems[item].MaxValue.iValue;
+			return TRUE;
+		}
+		else
+			return FALSE;
+	case PALCFG_BOOLEAN:
+		if (pValue->bValue != TRUE && pValue->bValue != FALSE)
+		{
+			pValue->bValue = pValue->bValue ? TRUE : FALSE;
+			return TRUE;
+		}
+		else
+			return FALSE;
+	default:
+		return FALSE;
+	}
+}
+
+static char *move_to_next_line(char *ptr)
+{
+	while (*ptr && (*ptr != '\r' && *ptr != '\n')) ptr++;
+	while (*ptr && (*ptr == '\r' || *ptr == '\n')) ptr++;
+	return (*ptr == '\0') ? NULL : ptr;
+}

+ 135 - 0
palcfg.h

@@ -0,0 +1,135 @@
+/* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
+//
+// Copyright (c) 2016, Lou Yihua <louyihua@21cn.com>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+# ifdef __cplusplus
+extern "C"
+{
+# endif
+
+typedef enum tagPALCFG_ITEM
+{
+	PALCFG_ALL_MIN = 0,
+
+	PALCFG_BOOLEAN_MIN = PALCFG_ALL_MIN,
+	/* Booleans */
+	PALCFG_DOS = PALCFG_BOOLEAN_MIN,
+	PALCFG_FULLSCREEN,
+	PALCFG_KEEPASPECTRATIO,
+	PALCFG_LAUNCHSETTING,
+	PALCFG_STEREO,
+	PALCFG_USEEMBEDDEDFONTS,
+	PALCFG_USESURROUNDOPL,
+	PALCFG_USETOUCHOVERLAY,
+	/* Booleans */
+	PALCFG_BOOLEAN_MAX,
+
+	PALCFG_INTEGER_MIN = PALCFG_BOOLEAN_MAX,
+	/* Integers */
+	PALCFG_SURROUNDOPLOFFSET = PALCFG_INTEGER_MIN,
+	/* Integers */
+	PALCFG_INTEGER_MAX,
+
+	PALCFG_UNSIGNED_MIN = PALCFG_INTEGER_MAX,
+	/* Unsigneds */
+	PALCFG_AUDIOBUFFERSIZE = PALCFG_UNSIGNED_MIN,
+	PALCFG_CODEPAGE,
+	PALCFG_OPLSAMPLERATE,
+	PALCFG_RESAMPLEQUALITY,
+	PALCFG_SAMPLERATE,
+	PALCFG_VOLUME,
+	PALCFG_WINDOWHEIGHT,
+	PALCFG_WINDOWWIDTH,
+	/* Unsigneds */
+	PALCFG_UNSIGNED_MAX,
+
+	PALCFG_STRING_MIN = PALCFG_UNSIGNED_MAX,
+	/* Strings */
+	PALCFG_CD = PALCFG_STRING_MIN,
+	PALCFG_GAMEPATH,
+	PALCFG_MESSAGEFILE,
+	PALCFG_MUSIC,
+	PALCFG_OPL,
+	PALCFG_RIXEXTRAINIT,
+	PALCFG_SAVEPATH,
+	/* Strings */
+	PALCFG_STRING_MAX,
+
+	PALCFG_ALL_MAX = PALCFG_STRING_MAX
+} PALCFG_ITEM;
+
+typedef enum tagPALCFG_TYPE
+{
+	PALCFG_STRING,
+	PALCFG_BOOLEAN,
+	PALCFG_INTEGER,
+	PALCFG_UNSIGNED,
+} PALCFG_TYPE;
+
+typedef union tagConfigValue
+{
+	LPCSTR   sValue;
+	DWORD    uValue;
+	INT      iValue;
+	BOOL     bValue;
+} ConfigValue;
+
+typedef struct tagConfigItem
+{
+	PALCFG_ITEM        Item;
+	PALCFG_TYPE        Type;
+	const char*        Name;
+	int                NameLength;
+	const ConfigValue  DefaultValue;
+	const ConfigValue  MinValue;
+	const ConfigValue  MaxValue;
+} ConfigItem;
+
+BOOL
+PAL_ParseConfigLine(
+	const char * line,
+	const ConfigItem ** pItem,
+	ConfigValue * pValue
+);
+
+ConfigValue
+PAL_DefaultConfig(
+	PALCFG_ITEM item
+);
+
+const char *
+PAL_ConfigName(
+	PALCFG_ITEM item
+);
+
+BOOL
+PAL_LimitConfig(
+	PALCFG_ITEM item,
+	ConfigValue * pValue
+);
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif

+ 3 - 0
palcommon.h

@@ -32,7 +32,10 @@ extern "C"
 typedef LPBYTE      LPSPRITE, LPBITMAPRLE;
 typedef LPCBYTE     LPCSPRITE, LPCBITMAPRLE;
 
+#ifndef PAL_POS_DEFINED
+#define PAL_POS_DEFINED
 typedef DWORD           PAL_POS;
+#endif
 
 #define PAL_XY(x, y)    (PAL_POS)(((((WORD)(y)) << 16) & 0xFFFF0000) | (((WORD)(x)) & 0xFFFF))
 #define PAL_X(xy)       (SHORT)((xy) & 0xFFFF)

+ 2 - 18
rixplay.cpp

@@ -27,11 +27,8 @@
 
 #include "resampler.h"
 #include "adplug/opl.h"
-#include "adplug/demuopl.h"
 #include "adplug/dbemuopl.h"
-#if PAL_HAS_MAME
 #include "adplug/emuopl.h"
-#endif
 #include "adplug/surroundopl.h"
 #include "adplug/rix.h"
 
@@ -416,25 +413,17 @@ RIX_Init(
 	{
 		switch (gConfig.eOPLType)
 		{
-		case OPL_DOSBOX_OLD:
-			pRixPlayer->opl = new CSurroundopl(
-				new CDemuopl(gConfig.iOPLSampleRate, true, false),
-				new CDemuopl(gConfig.iOPLSampleRate, true, false),
-				true, gConfig.iOPLSampleRate, gConfig.dSurroundOPLOffset);
-			break;
 		case OPL_DOSBOX:
 			pRixPlayer->opl = new CSurroundopl(
 				new CDBemuopl(gConfig.iOPLSampleRate, true, false),
 				new CDBemuopl(gConfig.iOPLSampleRate, true, false),
-				true, gConfig.iOPLSampleRate, gConfig.dSurroundOPLOffset);
+				true, gConfig.iOPLSampleRate, gConfig.iSurroundOPLOffset);
 			break;
-#if PAL_HAS_MAME
 		case OPL_MAME:
 			pRixPlayer->opl = new CSurroundopl(
 				new CEmuopl(gConfig.iOPLSampleRate, true, false),
 				new CEmuopl(gConfig.iOPLSampleRate, true, false),
-				true, gConfig.iOPLSampleRate, gConfig.dSurroundOPLOffset);
-#endif
+				true, gConfig.iOPLSampleRate, gConfig.iSurroundOPLOffset);
 			break;
 		}
 	}
@@ -442,17 +431,12 @@ RIX_Init(
 	{
 		switch (gConfig.eOPLType)
 		{
-		case OPL_DOSBOX_OLD:
-			pRixPlayer->opl = new CDemuopl(gConfig.iOPLSampleRate, true, gConfig.iAudioChannels == 2);
-			break;
 		case OPL_DOSBOX:
 			pRixPlayer->opl = new CDBemuopl(gConfig.iOPLSampleRate, true, gConfig.iAudioChannels == 2);
 			break;
-#if PAL_HAS_MAME
 		case OPL_MAME:
 			pRixPlayer->opl = new CEmuopl(gConfig.iOPLSampleRate, true, gConfig.iAudioChannels == 2);
 			break;
-#endif
 		}
 	}
 

+ 4 - 4
script.c

@@ -567,13 +567,13 @@ PAL_AdditionalCredits(
 	  if (wcsncmp(rgszStrings[i], L"%ls", 3) == 0)
 	  {
 		  // We've limited the length of g_rcCredits[i] in text.c, so no need to double check here.
-		  wcscpy(buffer, gConfig.pszMsgName ? g_rcCredits[i] : rgszcps[i][gConfig.iCodePage]);
+		  wcscpy(buffer, gConfig.pszMsgFile ? g_rcCredits[i] : rgszcps[i][gConfig.uCodePage]);
 		  wcscat(buffer, rgszStrings[i] + 3);
 	  }
 	  else
 		  wcscpy(buffer, rgszStrings[i]);
 #else
-	  swprintf(buffer, 48, rgszStrings[i], gConfig.pszMsgName ? g_rcCredits[i] : rgszcps[i][gConfig.iCodePage]);
+	  swprintf(buffer, 48, rgszStrings[i], gConfig.pszMsgFile ? g_rcCredits[i] : rgszcps[i][gConfig.uCodePage]);
 #endif
 	  PAL_DrawText(buffer, PAL_XY(0, 2 + i * 16), DESCTEXT_COLOR, TRUE, FALSE, FALSE);
    }
@@ -3302,7 +3302,7 @@ PAL_RunTriggerScript(
          //
          // Print dialog text
          //
-         if (gConfig.pszMsgName)
+         if (gConfig.pszMsgFile)
          {
             int idx = 0, iMsg;
             while ((iMsg = PAL_GetMsgNum(pScript->rgwOperand[0], idx++)) >= 0)
@@ -3477,7 +3477,7 @@ begin:
 		   int YBase = (wEventObjectID & PAL_ITEM_DESC_BOTTOM) ? 150 - gConfig.ScreenLayout.ExtraItemDescLines * 16 : 3;
 		   int iDescLine = (wEventObjectID & ~PAL_ITEM_DESC_BOTTOM);
 
-		   if (gConfig.pszMsgName)
+		   if (gConfig.pszMsgFile)
 		   {
 			   int idx = 0, iMsg;
 			   while ((iMsg = PAL_GetMsgNum(pScript->rgwOperand[0], idx++)) >= 0)

+ 2 - 2
sound.c

@@ -627,9 +627,9 @@ SOUND_OpenAudio(
    switch (gConfig.eMusicType)
    {
    case MUSIC_RIX:
-	   if (!(gSndPlayer.pMusPlayer = RIX_Init(va("%s%s", PAL_PREFIX, "mus.mkf"))))
+	   if (!(gSndPlayer.pMusPlayer = RIX_Init(va("%s%s", gConfig.pszGamePath, "mus.mkf"))))
 	   {
-		   gSndPlayer.pMusPlayer = RIX_Init(va("%s%s", PAL_PREFIX, "MUS.MKF"));
+		   gSndPlayer.pMusPlayer = RIX_Init(va("%s%s", gConfig.pszGamePath, "MUS.MKF"));
 	   }
 	   break;
    case MUSIC_MP3:

+ 0 - 4
sound.h

@@ -24,10 +24,6 @@
 
 #include "common.h"
 
-#ifndef PAL_MAX_SAMPLERATE
-#define PAL_MAX_SAMPLERATE 48000
-#endif
-
 #ifdef __cplusplus
 extern "C"
 {

+ 18 - 11
text.c

@@ -49,6 +49,12 @@ static LPWSTR gc_rgszAdditionalWords[CP_MAX][ATB_WORD_COUNT] = {
 static LPWSTR gc_rgszDefaultAdditionalWords[ATB_WORD_COUNT] = { NULL, L"\xFF11", L"\xFF12", L"\xFF13", L"\xFF14", L"\xFF15" };
 #endif
 
+#define SDLPAL_EXTRA_WORD_COUNT     1
+static LPWSTR gc_rgszSDLPalWords[CP_MAX][SDLPAL_EXTRA_WORD_COUNT] = {
+	{ L"\x555F\x52D5\x8A2D\x5B9A" },
+	{ L"\x542F\x52A8\x8BBE\x7F6E" },
+};
+
 LPWSTR g_rcCredits[12];
 
 typedef struct tagTEXTLIB
@@ -157,8 +163,7 @@ PAL_ReadOneLine(
 		}
 		else
 		{
-			if (n > 0 && temp[n - 1] == '\n') temp[--n] = 0;
-			if (n > 0 && temp[n - 1] == '\r') temp[--n] = 0;
+			while (n > 0 && (temp[n - 1] == '\n' || temp[n - 1] == '\r')) temp[--n] = 0;
 			return temp;
 		}
 	}
@@ -493,12 +498,12 @@ PAL_InitText(
 
 --*/
 {
-   if (gConfig.pszMsgName)
+   if (gConfig.pszMsgFile)
    {
 	   //
 	   // Open the message, index and word data files.
 	   //
-	   FILE *fp = UTIL_OpenRequiredFileForMode(gConfig.pszMsgName, "r");
+	   FILE *fp = UTIL_OpenRequiredFileForMode(gConfig.pszMsgFile, "r");
 
 	   //
 	   // Read the contents of the message, index and word data files.
@@ -681,8 +686,10 @@ PAL_InitText(
 
 	   g_TextLib.lpIndexBuf = NULL;
 
+	   memcpy(g_TextLib.lpWordBuf + SYSMENU_LABEL_LAUNCHSETTING, gc_rgszSDLPalWords[gConfig.uCodePage], SDLPAL_EXTRA_WORD_COUNT * sizeof(LPCWSTR));
+
 #ifndef PAL_CLASSIC
-	   memcpy(g_TextLib.lpWordBuf + SYSMENU_LABEL_BATTLEMODE, gc_rgszAdditionalWords[gConfig.iCodePage], ATB_WORD_COUNT * sizeof(LPCWSTR));
+	   memcpy(g_TextLib.lpWordBuf + SYSMENU_LABEL_BATTLEMODE, gc_rgszAdditionalWords[gConfig.uCodePage], ATB_WORD_COUNT * sizeof(LPCWSTR));
 #endif
    }
 
@@ -723,7 +730,7 @@ PAL_FreeText(
    int i;
    if (g_TextLib.lpMsgBuf != NULL)
    {
-      if (gConfig.pszMsgName)
+      if (gConfig.pszMsgFile)
          for(i = 0; i < g_TextLib.nMsgs; i++) free(g_TextLib.lpMsgBuf[i]);
       else
          free(g_TextLib.lpMsgBuf[0]);
@@ -732,7 +739,7 @@ PAL_FreeText(
    }
    if (g_TextLib.lpWordBuf != NULL)
    {
-      if (gConfig.pszMsgName)
+      if (gConfig.pszMsgFile)
          for(i = 0; i < g_TextLib.nWords; i++) free(g_TextLib.lpWordBuf[i]);
       else
          free(g_TextLib.lpWordBuf[0]);
@@ -741,7 +748,7 @@ PAL_FreeText(
    }
    if (g_TextLib.lpIndexBuf != NULL)
    {
-      if (gConfig.pszMsgName)
+      if (gConfig.pszMsgFile)
          for(i = 0; i < g_TextLib.nIndices; i++) free(g_TextLib.lpIndexBuf[i]);
       else
          free(g_TextLib.lpIndexBuf[0]);
@@ -1770,15 +1777,15 @@ PAL_MultiByteToWideChar(
 
 --*/
 {
-	return PAL_MultiByteToWideCharCP(gConfig.iCodePage, mbs, mbslength, wcs, wcslength);
+	return PAL_MultiByteToWideCharCP(gConfig.uCodePage, mbs, mbslength, wcs, wcslength);
 }
 
 WCHAR
 PAL_GetInvalidChar(
-   CODEPAGE      iCodePage
+   CODEPAGE      uCodePage
 )
 {
-   switch(iCodePage)
+   switch(uCodePage)
    {
    case CP_BIG5:     return 0x3f;
    case CP_GBK:      return 0x3f;

+ 1 - 1
text.h

@@ -127,7 +127,7 @@ PAL_MultiByteToWideCharCP(
 
 WCHAR
 PAL_GetInvalidChar(
-   CODEPAGE      iCodePage
+   CODEPAGE      uCodePage
 );
 
 #endif

+ 1 - 0
ui.h

@@ -76,6 +76,7 @@ extern "C"
 #define SYSMENU_LABEL_SOUND                14
 #define SYSMENU_LABEL_QUIT                 15
 #define SYSMENU_LABEL_BATTLEMODE           606
+#define SYSMENU_LABEL_LAUNCHSETTING        612
 
 #define BATTLESPEEDMENU_LABEL_1            (SYSMENU_LABEL_BATTLEMODE + 1)
 #define BATTLESPEEDMENU_LABEL_2            (SYSMENU_LABEL_BATTLEMODE + 2)

+ 24 - 11
uigame.c

@@ -190,7 +190,7 @@ PAL_SaveSlotMenu(
    //
    for (i = 1; i <= 5; i++)
    {
-      fp = fopen(va("%s%d%s", PAL_SAVE_PREFIX, i, ".rpg"), "rb");
+      fp = fopen(va("%s%d%s", gConfig.pszSavePath, i, ".rpg"), "rb");
       if (fp == NULL)
       {
          wSavedTimes = 0;
@@ -528,14 +528,15 @@ PAL_SystemMenu(
    //
    const MENUITEM      rgSystemMenuItem[] =
    {
-      // value  label                      enabled   pos
-      { 1,      SYSMENU_LABEL_SAVE,        TRUE,     PAL_XY(53, 72) },
-      { 2,      SYSMENU_LABEL_LOAD,        TRUE,     PAL_XY(53, 72 + 18) },
-      { 3,      SYSMENU_LABEL_MUSIC,       TRUE,     PAL_XY(53, 72 + 36) },
-      { 4,      SYSMENU_LABEL_SOUND,       TRUE,     PAL_XY(53, 72 + 54) },
-      { 5,      SYSMENU_LABEL_QUIT,        TRUE,     PAL_XY(53, 72 + 72) },
+      // value  label                        enabled   pos
+      { 1,      SYSMENU_LABEL_SAVE,          TRUE,     PAL_XY(53, 72) },
+      { 2,      SYSMENU_LABEL_LOAD,          TRUE,     PAL_XY(53, 72 + 18) },
+      { 3,      SYSMENU_LABEL_MUSIC,         TRUE,     PAL_XY(53, 72 + 36) },
+      { 4,      SYSMENU_LABEL_SOUND,         TRUE,     PAL_XY(53, 72 + 54) },
+      { 5,      SYSMENU_LABEL_QUIT,          TRUE,     PAL_XY(53, 72 + 72) },
+	  { 6,      SYSMENU_LABEL_LAUNCHSETTING, TRUE,     PAL_XY(53, 72 + 90) },
 #if !defined(PAL_CLASSIC)
-      { 6,      SYSMENU_LABEL_BATTLEMODE,  TRUE,     PAL_XY(53, 72 + 90) },
+      { 7,      SYSMENU_LABEL_BATTLEMODE,    TRUE,     PAL_XY(53, 72 + 108) },
 #endif
    };
    const int           nSystemMenuItem = sizeof(rgSystemMenuItem) / sizeof(MENUITEM);
@@ -576,7 +577,7 @@ PAL_SystemMenu(
          iSavedTimes = 0;
          for (i = 1; i <= 5; i++)
          {
-            fp = fopen(va("%s%d%s", PAL_SAVE_PREFIX, i, ".rpg"), "rb");
+            fp = fopen(va("%s%d%s", gConfig.pszSavePath, i, ".rpg"), "rb");
             if (fp != NULL)
             {
                WORD wSavedTimes;
@@ -589,7 +590,7 @@ PAL_SystemMenu(
                }
             }
          }
-         PAL_SaveGame(va("%s%d%s", PAL_SAVE_PREFIX, iSlot, ".rpg"), iSavedTimes + 1);
+         PAL_SaveGame(va("%s%d%s", gConfig.pszSavePath, iSlot, ".rpg"), iSavedTimes + 1);
       }
       break;
 
@@ -644,8 +645,20 @@ PAL_SystemMenu(
       }
       break;
 
-#if !defined(PAL_CLASSIC)
    case 6:
+	   //
+	   // Launch setting
+	   //
+	   wReturnValue = (WORD)PAL_ConfirmMenu();
+	   if (wReturnValue != gConfig.fLaunchSetting)
+	   {
+		   gConfig.fLaunchSetting = wReturnValue;
+		   PAL_SaveConfig();
+	   }
+	   break;
+
+#if !defined(PAL_CLASSIC)
+   case 7:
       //
       // Battle Mode
       //

+ 17 - 5
util.c

@@ -24,6 +24,8 @@
 
 #include "util.h"
 #include "input.h"
+#include "global.h"
+#include "palcfg.h"
 
 #include "midi.h"
 #if SDL_VERSION_ATLEAST(2, 0, 0)
@@ -420,7 +422,17 @@ UTIL_OpenRequiredFileForMode(
 
    if (fp == NULL)
    {
-      TerminateOnError("File not found: %s!\n", lpszFileName);
+	   extern SDL_Window *gpWindow;
+	   SDL_MessageBoxButtonData buttons[2] = { { 0, 0, "Yes" }, { 0, 1, "No"} };
+	   SDL_MessageBoxData mbd = { SDL_MESSAGEBOX_ERROR, gpWindow, "FATAL ERROR", va("File not found: %s!\nLaunch setting dialog on next start?\n", lpszFileName), 2, buttons, NULL };
+	   int btnid;
+	   if (SDL_ShowMessageBox(&mbd, &btnid) == 0 && btnid == 0)
+	   {
+		   gConfig.fLaunchSetting = TRUE;
+		   PAL_SaveConfig();
+	   }
+	   PAL_Shutdown();
+	   exit(255);
    }
 
    return fp;
@@ -471,7 +483,7 @@ UTIL_OpenFileForMode(
 {
 	FILE         *fp;
 
-	fp = fopen(va("%s%s", PAL_PREFIX, lpszFileName), szMode);
+	fp = fopen(va("%s%s", gConfig.pszGamePath, lpszFileName), szMode);
 
 #ifndef _WIN32
 	if (fp == NULL)
@@ -480,11 +492,11 @@ UTIL_OpenFileForMode(
 		// try to find the matching file in the directory.
 		//
 		struct dirent **list;
-		int n = scandir(PAL_PREFIX, &list, 0, alphasort);
+		int n = scandir(gConfig.pszGamePath, &list, 0, alphasort);
 		while (n-- > 0)
 		{
 			if (!fp && strcasecmp(list[n]->d_name, lpszFileName) == 0)
-				fp = fopen(va("%s%s", PAL_PREFIX, list[n]->d_name), szMode);
+				fp = fopen(va("%s%s", gConfig.pszGamePath, list[n]->d_name), szMode);
 			free(list[n]);
 		}
 		free(list);
@@ -528,7 +540,7 @@ UTIL_OpenLog(
    VOID
 )
 {
-   if ((pLogFile = fopen(_PATH_LOG, "a+")) == NULL)
+   if ((pLogFile = fopen(va("%slog.txt", gConfig.pszSavePath), "a+")) == NULL)
    {
       return NULL;
    }

+ 5 - 5
util.h

@@ -113,6 +113,11 @@ UTIL_SavePath(
    VOID
 );
 
+LPCSTR
+UTIL_ConfigPath(
+   VOID
+);
+
 BOOL
 UTIL_GetScreenSize(
    DWORD *pdwScreenWidth,
@@ -139,7 +144,6 @@ UTIL_Platform_Quit(
    VOID
 );
 
-#define _PATH_LOG           PAL_PREFIX "log.txt"
 #define LOG_EMERG           0 /* system is unusable */
 #define LOG_ALERT           1 /* action must be taken immediately */
 #define LOG_CRIT            2 /* critical conditions */
@@ -173,11 +177,7 @@ UTIL_WriteLog(
 
 #define UTIL_OpenLog()       ((void)(0))
 #define UTIL_CloseLog()      ((void)(0))
-#ifdef _MSC_VER
-__forceinline VOID UTIL_WriteLog(int i, const char *p, ...) {}
-#else
 #define UTIL_WriteLog(...)   ((void)(0))
-#endif
 
 #endif
 

+ 3 - 3
video.c

@@ -776,7 +776,7 @@ VIDEO_SaveScreenshot(
    //
    for (iNumBMP = 0; iNumBMP <= 9999; iNumBMP++)
    {
-      fp = fopen(va("%sscrn%.4d.bmp", PAL_PREFIX, iNumBMP), "rb");
+      fp = fopen(va("%sscrn%.4d.bmp", gConfig.pszSavePath, iNumBMP), "rb");
       if (fp == NULL)
       {
          break;
@@ -793,9 +793,9 @@ VIDEO_SaveScreenshot(
    // Save the screenshot.
    //
 #if SDL_VERSION_ATLEAST(2,0,0)
-   SDL_SaveBMP(gpScreen, va("%sscrn%.4d.bmp", PAL_PREFIX, iNumBMP));
+   SDL_SaveBMP(gpScreen, va("%sscrn%.4d.bmp", gConfig.pszSavePath, iNumBMP));
 #else
-   SDL_SaveBMP(gpScreenReal, va("%sscrn%.4d.bmp", PAL_PREFIX, iNumBMP));
+   SDL_SaveBMP(gpScreenReal, va("%sscrn%.4d.bmp", gConfig.pszSavePath, iNumBMP));
 #endif
 }
 

+ 7 - 5
win32/resource.h

@@ -4,13 +4,14 @@
 //
 #define IDI_SDLPAL                      101
 #define IDD_LAUNCHER                    101
-#define IDD_LAUNCHER1                   102
+#define IDS_LAUNCHSETTING               102
+#define IDS_CONFIRM                     103
 #define IDC_GAMEPATH                    1000
+#define IDC_SAVEPATH                    1001
 #define IDC_DOS                         1002
 #define IDC_WIN95                       1003
 #define IDC_CHT                         1005
 #define IDC_CHS                         1006
-#define IDC_CUSTOM                      1007
 #define IDC_EMBEDFONT                   1009
 #define IDC_WIDTH                       1012
 #define IDC_HEIGHT                      1013
@@ -21,7 +22,6 @@
 #define IDC_BGM                         1020
 #define IDC_OPL                         1021
 #define IDC_STEREO                      1022
-#define IDC_SAVE                        1023
 #define IDC_DEFAULT                     1024
 #define IDC_SURROUNDOPL                 1025
 #define IDC_SAMPLERATE                  1027
@@ -30,15 +30,17 @@
 #define IDC_VOLUME                      1030
 #define IDC_OPLOFFSET                   1031
 #define IDC_AUDIOBUFFER                 1032
+#define IDC_BRGAME                      1033
+#define IDC_BRSAVE                      1034
 #define IDC_STATIC                      -1
 
 // Next default values for new objects
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE        104
+#define _APS_NEXT_RESOURCE_VALUE        105
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1030
+#define _APS_NEXT_CONTROL_VALUE         1035
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif

+ 192 - 142
win32/sdlpal.rc

@@ -24,57 +24,59 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
 // Dialog
 //
 
-IDD_LAUNCHER DIALOGEX 0, 0, 405, 211
+IDD_LAUNCHER DIALOGEX 0, 0, 405, 225
 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
 EXSTYLE WS_EX_APPWINDOW
 CAPTION "SDL PAL Launcher"
 FONT 8, "MS Shell Dlg", 400, 0, 0x1
 BEGIN
-    DEFPUSHBUTTON   "&Launch game",IDOK,348,190,50,14
-    PUSHBUTTON      "E&xit",IDCANCEL,7,190,50,14
+    DEFPUSHBUTTON   "&Launch game",IDOK,177,204,50,14
+    PUSHBUTTON      "E&xit",IDCANCEL,7,204,50,14
     LTEXT           "Game resource path:",IDC_STATIC,14,10,68,8
-    EDITTEXT        IDC_GAMEPATH,84,7,314,14,ES_AUTOHSCROLL | ES_READONLY
-    LTEXT           "CD source:",IDC_STATIC,14,117,36,8
-    GROUPBOX        "Game version && Language",IDC_STATIC,7,31,192,64
-    CONTROL         "&DOS version",IDC_DOS,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,46,55,10
-    CONTROL         "&WIN95 version",IDC_WIN95,"Button",BS_AUTORADIOBUTTON,102,46,63,10
-    CONTROL         "&Traditional Chinese",IDC_CHT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,62,77,10
-    CONTROL         "Simplified &Chinese",IDC_CHS,"Button",BS_AUTORADIOBUTTON,102,62,73,10
-    CONTROL         "C&ustomized",IDC_CUSTOM,"Button",BS_AUTORADIOBUTTON,14,78,48,10
-    CONTROL         "Use &embedded font",IDC_EMBEDFONT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,46,79,10
-    GROUPBOX        "Display",IDC_STATIC,206,31,192,64
-    LTEXT           "Window size:",IDC_STATIC,215,78,43,8
-    EDITTEXT        IDC_WIDTH,260,75,32,14,ES_AUTOHSCROLL | ES_NUMBER
-    CTEXT           "X",IDC_STATIC,295,78,8,8
-    EDITTEXT        IDC_HEIGHT,306,75,32,14,ES_AUTOHSCROLL | ES_NUMBER
-    CONTROL         "&Keep aspect ratio",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,62,72,10
-    LTEXT           "BGM source:",IDC_STATIC,115,117,41,8
-    CONTROL         "&Full screen",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,348,46,50,10
-    EDITTEXT        IDC_MSGFILE,72,75,118,14,ES_AUTOHSCROLL
-    GROUPBOX        "Audio",IDC_STATIC,7,102,391,78
-    COMBOBOX        IDC_CD,54,115,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    COMBOBOX        IDC_BGM,157,115,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    LTEXT           "OPL type:",IDC_STATIC,14,137,33,8
-    COMBOBOX        IDC_OPL,54,135,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    CONTROL         "S&tereo",IDC_STEREO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,117,37,10
-    PUSHBUTTON      "S&ave",IDC_SAVE,118,190,50,14
-    PUSHBUTTON      "&Default",IDC_DEFAULT,239,190,50,14
-    CONTROL         "Surround &OPL parameter:                     Hz",IDC_SURROUNDOPL,
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,250,137,148,10
-    LTEXT           "Audio sample rate:                      Hz",IDC_STATIC,280,117,113,8
-    EDITTEXT        IDC_SAMPLERATE,342,114,40,14,ES_AUTOHSCROLL | ES_NUMBER
-    LTEXT           "OPL sample rate:                       Hz",IDC_STATIC,115,137,110,8
-    EDITTEXT        IDC_OPLSR,172,134,40,14,ES_AUTOHSCROLL | ES_NUMBER
-    LTEXT           "Audio quality:",IDC_STATIC,14,157,45,8
-    LTEXT           "Low                                   High",IDC_STATIC,47,166,98,8
-    CONTROL         "",IDC_QUALITY,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,155,70,15
-    LTEXT           "Volume:",IDC_STATIC,145,157,26,8
-    CONTROL         "",IDC_VOLUME,"msctls_trackbar32",WS_TABSTOP,170,155,100,15
-    LTEXT           "Low                                              High",IDC_STATIC,163,166,120,8
-    LTEXT           "参数:                     Hz",IDC_STATIC,281,138,75,8
-    EDITTEXT        IDC_OPLOFFSET,343,134,40,14,ES_AUTOHSCROLL | ES_NUMBER
-    LTEXT           "Audio buffer:",IDC_STATIC,299,157,44,8
-    EDITTEXT        IDC_AUDIOBUFFER,343,154,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    EDITTEXT        IDC_GAMEPATH,84,7,256,14,ES_AUTOHSCROLL | ES_READONLY
+    LTEXT           "CD source:",IDC_STATIC,14,133,36,8
+    GROUPBOX        "Game version && Language",IDC_STATIC,7,47,192,64
+    CONTROL         "&DOS version",IDC_DOS,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,62,55,10
+    CONTROL         "&WIN95 version",IDC_WIN95,"Button",BS_AUTORADIOBUTTON,102,62,63,10
+    CONTROL         "&Traditional Chinese",IDC_CHT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,78,77,10
+    CONTROL         "Simplified &Chinese",IDC_CHS,"Button",BS_AUTORADIOBUTTON,102,78,73,10
+    CONTROL         "Use &embedded font",IDC_EMBEDFONT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,62,79,10
+    GROUPBOX        "Display",IDC_STATIC,206,47,192,64
+    LTEXT           "Window size:",IDC_STATIC,215,94,43,8
+    EDITTEXT        IDC_WIDTH,260,91,32,14,ES_AUTOHSCROLL | ES_NUMBER
+    CTEXT           "X",IDC_STATIC,295,94,8,8
+    EDITTEXT        IDC_HEIGHT,306,91,32,14,ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "&Keep aspect ratio",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,78,72,10
+    LTEXT           "BGM source:",IDC_STATIC,115,133,41,8
+    CONTROL         "&Full screen",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,348,62,50,10
+    EDITTEXT        IDC_MSGFILE,59,91,131,14,ES_AUTOHSCROLL
+    GROUPBOX        "Audio",IDC_STATIC,7,118,391,78
+    COMBOBOX        IDC_CD,54,132,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    COMBOBOX        IDC_BGM,157,132,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    LTEXT           "OPL type:",IDC_STATIC,14,153,33,8
+    COMBOBOX        IDC_OPL,54,151,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "S&tereo",IDC_STEREO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,133,37,10
+    PUSHBUTTON      "&Default",IDC_DEFAULT,348,204,50,14
+    CONTROL         "Surround &OPL",IDC_SURROUNDOPL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,245,153,55,10
+    LTEXT           "Audio sample rate:                      Hz",IDC_STATIC,280,133,113,8
+    EDITTEXT        IDC_SAMPLERATE,342,130,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "OPL sample rate:                       Hz",IDC_STATIC,115,153,110,8
+    EDITTEXT        IDC_OPLSR,172,150,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "Audio quality:",IDC_STATIC,14,173,45,8
+    LTEXT           "Low                                   High",IDC_STATIC,47,182,98,8
+    CONTROL         "",IDC_QUALITY,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,171,70,15
+    LTEXT           "Volume:",IDC_STATIC,145,173,26,8
+    CONTROL         "",IDC_VOLUME,"msctls_trackbar32",WS_TABSTOP,170,171,100,15
+    LTEXT           "Low                                              High",IDC_STATIC,163,182,120,8
+    LTEXT           "parameter:                       Hz",IDC_STATIC,304,153,94,8
+    EDITTEXT        IDC_OPLOFFSET,343,150,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "Audio buffer:",IDC_STATIC,299,173,44,8
+    EDITTEXT        IDC_AUDIOBUFFER,343,170,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "Game save path:",IDC_STATIC,26,28,56,8
+    EDITTEXT        IDC_SAVEPATH,84,25,256,14,ES_AUTOHSCROLL | ES_READONLY
+    PUSHBUTTON      "&Browse",IDC_BRGAME,348,7,50,14
+    PUSHBUTTON      "&Browse",IDC_BRSAVE,348,25,50,14
+    LTEXT           "Message file:",IDC_STATIC,14,94,43,8
 END
 
 
@@ -91,7 +93,7 @@ BEGIN
         LEFTMARGIN, 7
         RIGHTMARGIN, 398
         TOPMARGIN, 7
-        BOTTOMMARGIN, 204
+        BOTTOMMARGIN, 218
     END
 END
 #endif    // APSTUDIO_INVOKED
@@ -127,13 +129,25 @@ BEGIN
 0x504d, 0x0033, 
     IDC_BGM, 0x403, 4, 0
 0x474f, 0x0047, 
-    IDC_OPL, 0x403, 5, 0
-0x414d, 0x454d, "\000" 
     IDC_OPL, 0x403, 7, 0
 0x4f44, 0x4253, 0x584f, "\000" 
+    IDC_OPL, 0x403, 5, 0
+0x414d, 0x454d, "\000" 
     0
 END
 
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+    IDS_LAUNCHSETTING       "Launch setting dialog on next game start?"
+    IDS_CONFIRM             "Confirm"
+END
+
 #endif    // 非特定语言 resources
 /////////////////////////////////////////////////////////////////////////////
 
@@ -150,56 +164,59 @@ LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
 // Dialog
 //
 
-IDD_LAUNCHER DIALOGEX 0, 0, 405, 211
+IDD_LAUNCHER DIALOGEX 0, 0, 405, 225
 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
 EXSTYLE WS_EX_APPWINDOW
 CAPTION "SDL PAL 启动器"
 FONT 8, "MS Shell Dlg", 400, 0, 0x1
 BEGIN
-    DEFPUSHBUTTON   "启动游戏(&L)",IDOK,348,190,50,14
-    PUSHBUTTON      "退出(&X)",IDCANCEL,7,190,50,14
+    DEFPUSHBUTTON   "启动游戏(&L)",IDOK,177,204,50,14
+    PUSHBUTTON      "退出(&X)",IDCANCEL,7,204,50,14
     LTEXT           "游戏资源目录:",IDC_STATIC,14,10,57,8
-    EDITTEXT        IDC_GAMEPATH,71,7,327,14,ES_AUTOHSCROLL | ES_READONLY
-    LTEXT           "CD 音源:",IDC_STATIC,14,117,36,8
-    GROUPBOX        "游戏资源版本及语言设置",IDC_STATIC,7,31,192,64
-    CONTROL         "&DOS 版",IDC_DOS,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,46,40,10
-    CONTROL         "&WIN95 版",IDC_WIN95,"Button",BS_AUTORADIOBUTTON,102,46,48,10
-    CONTROL         "繁体中文(&T)",IDC_CHT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,62,57,10
-    CONTROL         "简体中文(&C)",IDC_CHS,"Button",BS_AUTORADIOBUTTON,102,62,58,10
-    CONTROL         "自定义(&U)",IDC_CUSTOM,"Button",BS_AUTORADIOBUTTON,14,78,48,10
-    CONTROL         "使用游戏内字体(&E)",IDC_EMBEDFONT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,46,81,10
-    GROUPBOX        "显示设置",IDC_STATIC,206,31,192,64
-    LTEXT           "窗口尺寸:",IDC_STATIC,215,78,41,8
-    EDITTEXT        IDC_WIDTH,260,75,32,14,ES_AUTOHSCROLL | ES_NUMBER
-    CTEXT           "X",IDC_STATIC,295,78,8,8
-    EDITTEXT        IDC_HEIGHT,306,75,32,14,ES_AUTOHSCROLL | ES_NUMBER
-    CONTROL         "保持纵横比(&K)",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,62,65,10
-    LTEXT           "BGM 音源:",IDC_STATIC,115,117,41,8
-    CONTROL         "全屏启动游戏(&F)",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,325,46,73,10
-    EDITTEXT        IDC_MSGFILE,72,75,118,14,ES_AUTOHSCROLL
-    GROUPBOX        "音频设置",IDC_STATIC,7,102,391,78
-    COMBOBOX        IDC_CD,54,115,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    COMBOBOX        IDC_BGM,157,115,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    LTEXT           "OPL 类型:",IDC_STATIC,14,137,40,8
-    COMBOBOX        IDC_OPL,54,135,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    CONTROL         "立体声(&T)",IDC_STEREO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,116,49,10
-    PUSHBUTTON      "保存设置(&A)",IDC_SAVE,118,190,50,14
-    PUSHBUTTON      "默认设置(&D)",IDC_DEFAULT,239,190,50,14
-    CONTROL         "环绕声 &OPL",IDC_SURROUNDOPL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,137,55,10
-    LTEXT           "音频采样率:                      Hz",IDC_STATIC,285,117,101,8
-    EDITTEXT        IDC_SAMPLERATE,335,114,40,14,ES_AUTOHSCROLL | ES_NUMBER
-    LTEXT           "OPL 采样率:                       Hz",IDC_STATIC,115,137,102,8
-    EDITTEXT        IDC_OPLSR,165,134,40,14,ES_AUTOHSCROLL | ES_NUMBER
-    LTEXT           "音频质量:",IDC_STATIC,14,157,41,8
-    LTEXT           "低                                   高",IDC_STATIC,47,166,87,8
-    CONTROL         "",IDC_QUALITY,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,55,155,70,15
-    LTEXT           "音量:",IDC_STATIC,145,157,25,8
-    CONTROL         "",IDC_VOLUME,"msctls_trackbar32",WS_TABSTOP,170,155,100,15
-    LTEXT           "低                                                  高",IDC_STATIC,163,166,117,8
-    LTEXT           "参数:                     Hz",IDC_STATIC,285,137,75,8
-    EDITTEXT        IDC_OPLOFFSET,308,134,40,14,ES_AUTOHSCROLL | ES_NUMBER
-    LTEXT           "音频缓冲区:",IDC_STATIC,291,157,49,8
-    EDITTEXT        IDC_AUDIOBUFFER,341,154,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    EDITTEXT        IDC_GAMEPATH,71,7,270,14,ES_AUTOHSCROLL | ES_READONLY
+    LTEXT           "CD 音源:",IDC_STATIC,14,133,36,8
+    GROUPBOX        "游戏资源版本及语言设置",IDC_STATIC,7,47,192,64
+    CONTROL         "&DOS 版",IDC_DOS,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,62,40,10
+    CONTROL         "&WIN95 版",IDC_WIN95,"Button",BS_AUTORADIOBUTTON,102,62,48,10
+    CONTROL         "繁体中文(&T)",IDC_CHT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,78,57,10
+    CONTROL         "简体中文(&C)",IDC_CHS,"Button",BS_AUTORADIOBUTTON,102,78,58,10
+    CONTROL         "使用游戏内字体(&E)",IDC_EMBEDFONT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,62,81,10
+    GROUPBOX        "显示设置",IDC_STATIC,206,47,192,64
+    LTEXT           "窗口尺寸:",IDC_STATIC,215,94,41,8
+    EDITTEXT        IDC_WIDTH,260,91,32,14,ES_AUTOHSCROLL | ES_NUMBER
+    CTEXT           "X",IDC_STATIC,295,94,8,8
+    EDITTEXT        IDC_HEIGHT,306,91,32,14,ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "保持纵横比(&K)",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,78,65,10
+    LTEXT           "BGM 音源:",IDC_STATIC,115,133,41,8
+    CONTROL         "全屏启动游戏(&F)",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,325,62,73,10
+    EDITTEXT        IDC_MSGFILE,57,91,133,14,ES_AUTOHSCROLL
+    GROUPBOX        "音频设置",IDC_STATIC,7,118,391,78
+    COMBOBOX        IDC_CD,54,131,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    COMBOBOX        IDC_BGM,157,131,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    LTEXT           "OPL 类型:",IDC_STATIC,14,153,40,8
+    COMBOBOX        IDC_OPL,54,151,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "立体声(&T)",IDC_STEREO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,132,49,10
+    PUSHBUTTON      "默认设置(&D)",IDC_DEFAULT,348,204,50,14
+    CONTROL         "环绕声 &OPL",IDC_SURROUNDOPL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,153,55,10
+    LTEXT           "音频采样率:                      Hz",IDC_STATIC,285,133,101,8
+    EDITTEXT        IDC_SAMPLERATE,335,130,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "OPL 采样率:                       Hz",IDC_STATIC,115,153,102,8
+    EDITTEXT        IDC_OPLSR,165,150,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "音频质量:",IDC_STATIC,14,173,41,8
+    LTEXT           "低                                   高",IDC_STATIC,47,182,87,8
+    CONTROL         "",IDC_QUALITY,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,55,171,70,15
+    LTEXT           "音量:",IDC_STATIC,145,173,25,8
+    CONTROL         "",IDC_VOLUME,"msctls_trackbar32",WS_TABSTOP,170,171,100,15
+    LTEXT           "低                                                  高",IDC_STATIC,163,182,117,8
+    LTEXT           "参数:                     Hz",IDC_STATIC,285,153,75,8
+    EDITTEXT        IDC_OPLOFFSET,308,150,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "音频缓冲区:",IDC_STATIC,291,173,49,8
+    EDITTEXT        IDC_AUDIOBUFFER,341,170,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "游戏存档目录:",IDC_STATIC,14,28,57,8
+    EDITTEXT        IDC_SAVEPATH,71,25,270,14,ES_AUTOHSCROLL | ES_READONLY
+    PUSHBUTTON      "浏览(&B)",IDC_BRGAME,348,7,50,14
+    PUSHBUTTON      "浏览(&B)",IDC_BRSAVE,348,25,50,14
+    LTEXT           "语言文件:",IDC_STATIC,14,94,41,8
 END
 
 
@@ -216,7 +233,7 @@ BEGIN
         LEFTMARGIN, 7
         RIGHTMARGIN, 398
         TOPMARGIN, 7
-        BOTTOMMARGIN, 204
+        BOTTOMMARGIN, 218
     END
 END
 #endif    // APSTUDIO_INVOKED
@@ -252,10 +269,10 @@ BEGIN
 0x504d, 0x0033, 
     IDC_BGM, 0x403, 4, 0
 0x474f, 0x0047, 
-    IDC_OPL, 0x403, 5, 0
-0x414d, 0x454d, "\000" 
     IDC_OPL, 0x403, 7, 0
 0x4f44, 0x4253, 0x584f, "\000" 
+    IDC_OPL, 0x403, 5, 0
+0x414d, 0x454d, "\000" 
     0
 END
 
@@ -334,6 +351,24 @@ BEGIN
     END
 END
 
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+    IDS_LAUNCHSETTING       "下次启动时还要进入设置界面吗?"
+    IDS_CONFIRM             "确认"
+END
+
+STRINGTABLE
+BEGIN
+    IDC_BRGAME              "打开游戏资源目录"
+    IDC_BRSAVE              "打开游戏存档目录"
+END
+
 #endif    // 中文(简体,中国) resources
 /////////////////////////////////////////////////////////////////////////////
 
@@ -350,56 +385,59 @@ LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL
 // Dialog
 //
 
-IDD_LAUNCHER DIALOGEX 0, 0, 405, 211
+IDD_LAUNCHER DIALOGEX 0, 0, 405, 225
 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
 EXSTYLE WS_EX_APPWINDOW
 CAPTION "SDL PAL 币笆竟"
 FONT 8, "MS Shell Dlg", 400, 0, 0x1
 BEGIN
-    DEFPUSHBUTTON   "币笆笴栏(&L)",IDOK,348,190,50,14
-    PUSHBUTTON      "癶�(&X)",IDCANCEL,7,190,50,14
+    DEFPUSHBUTTON   "币笆笴栏(&L)",IDOK,177,204,50,14
+    PUSHBUTTON      "癶�(&X)",IDCANCEL,7,204,50,14
     LTEXT           "笴栏戈方郎Ж�",IDC_STATIC,14,10,57,8
-    EDITTEXT        IDC_GAMEPATH,71,7,327,14,ES_AUTOHSCROLL | ES_READONLY
-    LTEXT           "CD �方�",IDC_STATIC,14,117,36,8
-    GROUPBOX        "笴栏戈方�セの粂ē砞﹚",IDC_STATIC,7,31,192,64
-    CONTROL         "&DOS �",IDC_DOS,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,46,40,10
-    CONTROL         "&WIN95 �",IDC_WIN95,"Button",BS_AUTORADIOBUTTON,102,46,48,10
-    CONTROL         "羉砰いゅ(&T)",IDC_CHT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,62,57,10
-    CONTROL         "虏砰いゅ(&C)",IDC_CHS,"Button",BS_AUTORADIOBUTTON,102,62,58,10
-    CONTROL         "�﹚?(&U)",IDC_CUSTOM,"Button",BS_AUTORADIOBUTTON,14,78,48,10
-    CONTROL         "ㄏノ笴栏ず�砰(&E)",IDC_EMBEDFONT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,46,81,10
-    GROUPBOX        "陪ボ砞﹚",IDC_STATIC,206,31,192,64
-    LTEXT           "跌怠へ��",IDC_STATIC,215,78,41,8
-    EDITTEXT        IDC_WIDTH,260,75,32,14,ES_AUTOHSCROLL | ES_NUMBER
-    CTEXT           "X",IDC_STATIC,295,78,8,8
-    EDITTEXT        IDC_HEIGHT,306,75,32,14,ES_AUTOHSCROLL | ES_NUMBER
-    CONTROL         "玂�羇绢ゑ(&K)",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,62,65,10
-    LTEXT           "BGM �方�",IDC_STATIC,115,117,41,8
-    CONTROL         "��币笆笴栏(&F)",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,325,46,73,10
-    EDITTEXT        IDC_MSGFILE,72,75,118,14,ES_AUTOHSCROLL
-    GROUPBOX        "�癟砞﹚",IDC_STATIC,7,102,391,78
-    COMBOBOX        IDC_CD,54,115,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    COMBOBOX        IDC_BGM,157,115,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    LTEXT           "OPL 摸��",IDC_STATIC,14,137,40,8
-    COMBOBOX        IDC_OPL,54,135,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    CONTROL         "ō菌羘(&T)",IDC_STEREO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,116,49,10
-    PUSHBUTTON      "玂�砞﹚(&A)",IDC_SAVE,118,190,50,14
-    PUSHBUTTON      "纐粄砞﹚(&D)",IDC_DEFAULT,239,190,50,14
-    CONTROL         "吏露羘 &OPL",IDC_SURROUNDOPL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,235,137,55,10
-    LTEXT           "�癟�妓硉瞯�                      Hz",IDC_STATIC,285,117,109,8
-    EDITTEXT        IDC_SAMPLERATE,342,114,40,14,ES_AUTOHSCROLL | ES_NUMBER
-    LTEXT           "OPL �妓硉瞯�                       Hz",IDC_STATIC,115,137,110,8
-    EDITTEXT        IDC_OPLSR,172,134,40,14,ES_AUTOHSCROLL | ES_NUMBER
-    LTEXT           "�癟珇借�",IDC_STATIC,14,157,41,8
-    LTEXT           "�                                   蔼",IDC_STATIC,47,166,87,8
-    CONTROL         "",IDC_QUALITY,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,55,155,70,15
-    LTEXT           "�秖�",IDC_STATIC,145,157,25,8
-    CONTROL         "",IDC_VOLUME,"msctls_trackbar32",WS_TABSTOP,170,155,100,15
-    LTEXT           "�                                                  蔼",IDC_STATIC,163,166,117,8
-    LTEXT           "把计�                     Hz",IDC_STATIC,297,137,75,8
-    EDITTEXT        IDC_OPLOFFSET,320,134,40,14,ES_AUTOHSCROLL | ES_NUMBER
-    LTEXT           "�癟絯侥跋�",IDC_STATIC,291,157,49,8
-    EDITTEXT        IDC_AUDIOBUFFER,341,154,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    EDITTEXT        IDC_GAMEPATH,71,7,270,14,ES_AUTOHSCROLL | ES_READONLY
+    LTEXT           "CD �方�",IDC_STATIC,14,133,36,8
+    GROUPBOX        "笴栏戈方�セの粂ē砞﹚",IDC_STATIC,7,47,192,64
+    CONTROL         "&DOS �",IDC_DOS,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,62,40,10
+    CONTROL         "&WIN95 �",IDC_WIN95,"Button",BS_AUTORADIOBUTTON,102,62,48,10
+    CONTROL         "羉砰いゅ(&T)",IDC_CHT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,78,57,10
+    CONTROL         "虏砰いゅ(&C)",IDC_CHS,"Button",BS_AUTORADIOBUTTON,102,78,58,10
+    CONTROL         "ㄏノ笴栏ず�砰(&E)",IDC_EMBEDFONT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,62,81,10
+    GROUPBOX        "陪ボ砞﹚",IDC_STATIC,206,47,192,64
+    LTEXT           "跌怠へ��",IDC_STATIC,215,94,41,8
+    EDITTEXT        IDC_WIDTH,260,91,32,14,ES_AUTOHSCROLL | ES_NUMBER
+    CTEXT           "X",IDC_STATIC,295,94,8,8
+    EDITTEXT        IDC_HEIGHT,306,91,32,14,ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "玂�羇绢ゑ(&K)",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,215,78,65,10
+    LTEXT           "BGM �方�",IDC_STATIC,115,133,41,8
+    CONTROL         "��币笆笴栏(&F)",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,325,62,73,10
+    EDITTEXT        IDC_MSGFILE,57,91,133,14,ES_AUTOHSCROLL
+    GROUPBOX        "�癟砞﹚",IDC_STATIC,7,118,391,78
+    COMBOBOX        IDC_CD,54,132,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    COMBOBOX        IDC_BGM,157,132,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    LTEXT           "OPL 摸��",IDC_STATIC,14,153,40,8
+    COMBOBOX        IDC_OPL,54,151,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "ミ砰羘(&T)",IDC_STEREO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,132,49,10
+    PUSHBUTTON      "纐粄砞﹚(&D)",IDC_DEFAULT,348,204,50,14
+    CONTROL         "吏露羘 &OPL",IDC_SURROUNDOPL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,235,153,55,10
+    LTEXT           "�癟�妓硉瞯�                      Hz",IDC_STATIC,285,133,109,8
+    EDITTEXT        IDC_SAMPLERATE,342,130,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "OPL �妓硉瞯�                       Hz",IDC_STATIC,115,153,110,8
+    EDITTEXT        IDC_OPLSR,172,150,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "�癟珇借�",IDC_STATIC,14,173,41,8
+    LTEXT           "�                                   蔼",IDC_STATIC,47,182,87,8
+    CONTROL         "",IDC_QUALITY,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,55,171,70,15
+    LTEXT           "�秖�",IDC_STATIC,145,173,25,8
+    CONTROL         "",IDC_VOLUME,"msctls_trackbar32",WS_TABSTOP,170,171,100,15
+    LTEXT           "�                                                  蔼",IDC_STATIC,163,182,117,8
+    LTEXT           "把计�                     Hz",IDC_STATIC,297,153,75,8
+    EDITTEXT        IDC_OPLOFFSET,320,150,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "�癟絯侥跋�",IDC_STATIC,291,173,49,8
+    EDITTEXT        IDC_AUDIOBUFFER,341,170,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "笴栏秈�郎Ж�",IDC_STATIC,15,27,57,8
+    EDITTEXT        IDC_SAVEPATH,71,25,270,14,ES_AUTOHSCROLL | ES_READONLY
+    PUSHBUTTON      "聅凝(&B)",IDC_BRGAME,348,7,50,14
+    PUSHBUTTON      "聅凝(&B)",IDC_BRSAVE,348,25,50,14
+    LTEXT           "粂ē郎�",IDC_STATIC,14,94,33,8
 END
 
 
@@ -416,7 +454,7 @@ BEGIN
         LEFTMARGIN, 7
         RIGHTMARGIN, 398
         TOPMARGIN, 7
-        BOTTOMMARGIN, 204
+        BOTTOMMARGIN, 218
     END
 END
 #endif    // APSTUDIO_INVOKED
@@ -452,13 +490,25 @@ BEGIN
 0x504d, 0x0033, 
     IDC_BGM, 0x403, 4, 0
 0x474f, 0x0047, 
-    IDC_OPL, 0x403, 5, 0
-0x414d, 0x454d, "\000" 
     IDC_OPL, 0x403, 7, 0
 0x4f44, 0x4253, 0x584f, "\000" 
+    IDC_OPL, 0x403, 5, 0
+0x414d, 0x454d, "\000" 
     0
 END
 
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+    IDS_LAUNCHSETTING       "�Ω币笆�临璶秈�砞﹚ざ�盾�"
+    IDS_CONFIRM             "絋粄"
+END
+
 #endif    // 中文(繁体,台湾) resources
 /////////////////////////////////////////////////////////////////////////////
 

+ 2 - 0
win32/sdlpal.vcxproj

@@ -166,6 +166,7 @@
     <ClCompile Include="..\mp3play.c" />
     <ClCompile Include="..\oggplay.c" />
     <ClCompile Include="..\overlay.c" />
+    <ClCompile Include="..\palcfg.c" />
     <ClCompile Include="..\palcommon.c" />
     <ClCompile Include="..\palette.c" />
     <ClCompile Include="..\play.c" />
@@ -277,6 +278,7 @@
     <ClInclude Include="..\main.h" />
     <ClInclude Include="..\map.h" />
     <ClInclude Include="..\midi.h" />
+    <ClInclude Include="..\palcfg.h" />
     <ClInclude Include="..\palcommon.h" />
     <ClInclude Include="..\palette.h" />
     <ClInclude Include="..\play.h" />

+ 6 - 0
win32/sdlpal.vcxproj.filters

@@ -308,6 +308,9 @@
     <ClCompile Include="..\overlay.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\palcfg.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\ascii.h">
@@ -649,6 +652,9 @@
     <ClInclude Include="resource.h">
       <Filter>platform</Filter>
     </ClInclude>
+    <ClInclude Include="..\palcfg.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\sdlpal.ico">

+ 129 - 91
win32/win32.cpp

@@ -5,6 +5,7 @@
 #include <tchar.h>
 #include <Windows.h>
 #include <CommCtrl.h>
+#include <ShlObj.h>
 #include <string>
 #include "resource.h"
 #include "../global.h"
@@ -30,98 +31,118 @@ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
 #define EnableDlgItem(hwnd, nIDControl, bEnable) \
 			EnableWindow(GetDlgItem((hwnd), (nIDControl)), (bEnable))
 
-void SaveSettings(HWND hwndDlg, BOOL fWriteFile)
+HINSTANCE g_hInstance;
+WORD g_wLanguage;
+
+int WINAPI LoadStringEx(
+	HINSTANCE hInstance,
+	UINT      uID,
+	LANGID    wLang,
+	LPTSTR    lpBuffer,
+	int       nBufferMax
+)
 {
-	if (fWriteFile)
+	auto hrc = FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE((uID >> 4) + 1), wLang);
+	if (nullptr == hrc) return 0;
+
+	auto begin = (LPCWSTR)LockResource(LoadResource(hInstance, hrc));
+	for (int idx = 0; idx < (int)(uID & 0xf); idx++)
+		begin += *begin + 1;
+	if (nBufferMax == 0)
 	{
-		std::string msgfile;
-		char buffer[40];
-		int len = GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_MSGFILE)) + 1;
-		msgfile.assign(len, ' ');
-		GetDlgItemTextA(hwndDlg, IDC_MSGFILE, (char*)msgfile.data(), len);
-
-		FILE *fp = UTIL_OpenFileForMode("sdlpal.cfg", "w");
-		fprintf(fp, "DOS=%d\n", IsDlgButtonChecked(hwndDlg, IDC_DOS));
-		if (IsDlgButtonChecked(hwndDlg, IDC_DOS))
-			fprintf(fp, "UseEmbeddedFonts=%d\n", IsDlgButtonChecked(hwndDlg, IDC_EMBEDFONT));
-		else if (!IsDlgButtonChecked(hwndDlg, IDC_CUSTOM))
-			fprintf(fp, "CodePage=%d\n", IsDlgButtonChecked(hwndDlg, IDC_CHS));
-		if (IsDlgButtonChecked(hwndDlg, IDC_CUSTOM))
-			fprintf(fp, "MessageFileName=%s\n", msgfile.c_str());
-		GetDlgItemTextA(hwndDlg, IDC_CD, buffer, 40); fprintf(fp, "CD=%s\n", buffer);
-		GetDlgItemTextA(hwndDlg, IDC_BGM, buffer, 40); fprintf(fp, "MUSIC=%s\n", buffer);
-		if (ComboBox_GetCurSel(hwndDlg, IDC_BGM) == MUSIC_RIX)
-		{
-			GetDlgItemTextA(hwndDlg, IDC_OPL, buffer, 40); fprintf(fp, "OPL=%s\n", buffer);
-			fprintf(fp, "UseSurroundOPL=%d\n", IsDlgButtonChecked(hwndDlg, IDC_SURROUNDOPL));
-			fprintf(fp, "OPLSampleRate=%u\n", GetDlgItemInt(hwndDlg, IDC_OPLSR, nullptr, FALSE));
-			fprintf(fp, "SurroundOPLOffset=%d\n", GetDlgItemInt(hwndDlg, IDC_OPLOFFSET, nullptr, TRUE));
-		}
-		fprintf(fp, "Stereo=%d\n", IsDlgButtonChecked(hwndDlg, IDC_STEREO));
-		fprintf(fp, "ResampleQuality=%d\n", TrackBar_GetPos(hwndDlg, IDC_QUALITY));
-		fprintf(fp, "Volume=%d\n", TrackBar_GetPos(hwndDlg, IDC_VOLUME));
-		fprintf(fp, "AudioBufferSize=%u\n", GetDlgItemInt(hwndDlg, IDC_AUDIOBUFFER, nullptr, FALSE));
-		fprintf(fp, "SampleRate=%u\n", GetDlgItemInt(hwndDlg, IDC_SAMPLERATE, nullptr, FALSE));
-		fprintf(fp, "WindowWidth=%u\n", GetDlgItemInt(hwndDlg, IDC_WIDTH, nullptr, FALSE));
-		fprintf(fp, "WindowHeight=%u\n", GetDlgItemInt(hwndDlg, IDC_HEIGHT, nullptr, FALSE));
-		fprintf(fp, "KeepAspectRatio=%d\n", IsDlgButtonChecked(hwndDlg, IDC_ASPECTRATIO));
-		fclose(fp);
+		*((LPCWSTR*)lpBuffer) = begin;
+		return sizeof(LPCWSTR);
 	}
 	else
 	{
-		PAL_LoadConfig(FALSE);
-
-		gConfig.fIsWIN95 = !IsDlgButtonChecked(hwndDlg, IDC_DOS);
-		gConfig.fUseEmbeddedFonts = !gConfig.fIsWIN95 && IsDlgButtonChecked(hwndDlg, IDC_EMBEDFONT);
-		if (!IsDlgButtonChecked(hwndDlg, IDC_CUSTOM))
+		wcsncpy(lpBuffer, begin + 1, min(nBufferMax, *begin));
+		if (nBufferMax <= *begin)
 		{
-			if (IsDlgButtonChecked(hwndDlg, IDC_CHS) && gConfig.fIsWIN95)
-				gConfig.iCodePage = CP_GBK;
-			else
-				gConfig.iCodePage = CP_BIG5;
-			free(gConfig.pszMsgName);
-			gConfig.pszMsgName = nullptr;
+			lpBuffer[nBufferMax - 1] = '\0';
+			return nBufferMax - 1;
 		}
 		else
 		{
-			int length = GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_MSGFILE));
-			gConfig.pszMsgName = (char*)realloc(gConfig.pszMsgName, length + 1);
-			GetDlgItemTextA(hwndDlg, IDC_MSGFILE, gConfig.pszMsgName, length + 1);
-		}
-		gConfig.fKeepAspectRatio = IsDlgButtonChecked(hwndDlg, IDC_ASPECTRATIO);
-		gConfig.dwScreenWidth = GetDlgItemInt(hwndDlg, IDC_WIDTH, nullptr, FALSE);
-		gConfig.dwScreenHeight = GetDlgItemInt(hwndDlg, IDC_HEIGHT, nullptr, FALSE);
-		gConfig.eCDType = (MUSICTYPE)(ComboBox_GetCurSel(hwndDlg, IDC_CD) + MUSIC_MP3);
-		gConfig.eMusicType = (MUSICTYPE)ComboBox_GetCurSel(hwndDlg, IDC_BGM);
-		gConfig.eOPLType = (OPLTYPE)(ComboBox_GetCurSel(hwndDlg, IDC_OPL) + OPL_DOSBOX_OLD);
-		gConfig.iAudioChannels = IsDlgButtonChecked(hwndDlg, IDC_STEREO) ? 2 : 1;
-		gConfig.iSampleRate = GetDlgItemInt(hwndDlg, IDC_SAMPLERATE, nullptr, FALSE);
-		gConfig.wAudioBufferSize = GetDlgItemInt(hwndDlg, IDC_AUDIOBUFFER, nullptr, FALSE);
-		gConfig.iVolume = TrackBar_GetPos(hwndDlg, IDC_VOLUME);
-		gConfig.iResampleQuality = TrackBar_GetPos(hwndDlg, IDC_QUALITY);
-		if (gConfig.eMusicType == MUSIC_RIX)
-		{
-			gConfig.fUseSurroundOPL = IsDlgButtonChecked(hwndDlg, IDC_SURROUNDOPL);
-			gConfig.iOPLSampleRate = GetDlgItemInt(hwndDlg, IDC_OPLSR, nullptr, FALSE);
-			gConfig.dSurroundOPLOffset = GetDlgItemInt(hwndDlg, IDC_OPLOFFSET, nullptr, TRUE);
+			lpBuffer[*begin] = '\0';
+			return *begin;
 		}
 	}
 }
 
+void SaveSettings(HWND hwndDlg, BOOL fWriteFile)
+{
+	int textLen;
+
+	if (IsDlgButtonChecked(hwndDlg, IDC_CHS) && gConfig.fIsWIN95)
+		gConfig.uCodePage = CP_GBK;
+	else
+		gConfig.uCodePage = CP_BIG5;
+
+	if ((textLen = GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_MSGFILE))) > 0)
+	{
+		gConfig.pszMsgFile = (char*)realloc(gConfig.pszMsgFile, textLen + 1);
+		GetDlgItemTextA(hwndDlg, IDC_MSGFILE, gConfig.pszMsgFile, textLen + 1);
+	}
+	else
+	{
+		free(gConfig.pszMsgFile); gConfig.pszMsgFile = nullptr;
+	}
+	if ((textLen = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_GAMEPATH))) > 0)
+	{
+		gConfig.pszGamePath = (char*)realloc(gConfig.pszGamePath, textLen + 1);
+		GetDlgItemTextA(hwndDlg, IDC_GAMEPATH, gConfig.pszGamePath, textLen + 1);
+	}
+	else
+	{
+		free(gConfig.pszGamePath); gConfig.pszGamePath = nullptr;
+	}
+	if ((textLen = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_SAVEPATH))) > 0)
+	{
+		gConfig.pszSavePath = (char*)realloc(gConfig.pszSavePath, textLen + 1);
+		GetDlgItemTextA(hwndDlg, IDC_SAVEPATH, gConfig.pszSavePath, textLen + 1);
+	}
+	else
+	{
+		free(gConfig.pszSavePath); gConfig.pszSavePath = nullptr;
+	}
+
+	gConfig.fIsWIN95 = !IsDlgButtonChecked(hwndDlg, IDC_DOS);
+	gConfig.fUseEmbeddedFonts = !gConfig.fIsWIN95 && IsDlgButtonChecked(hwndDlg, IDC_EMBEDFONT);
+	gConfig.fKeepAspectRatio = IsDlgButtonChecked(hwndDlg, IDC_ASPECTRATIO);
+	gConfig.dwScreenWidth = GetDlgItemInt(hwndDlg, IDC_WIDTH, nullptr, FALSE);
+	gConfig.dwScreenHeight = GetDlgItemInt(hwndDlg, IDC_HEIGHT, nullptr, FALSE);
+	gConfig.eCDType = (MUSICTYPE)(ComboBox_GetCurSel(hwndDlg, IDC_CD) + MUSIC_MP3);
+	gConfig.eMusicType = (MUSICTYPE)ComboBox_GetCurSel(hwndDlg, IDC_BGM);
+	gConfig.eOPLType = (OPLTYPE)(ComboBox_GetCurSel(hwndDlg, IDC_OPL));
+	gConfig.iAudioChannels = IsDlgButtonChecked(hwndDlg, IDC_STEREO) ? 2 : 1;
+	gConfig.iSampleRate = GetDlgItemInt(hwndDlg, IDC_SAMPLERATE, nullptr, FALSE);
+	gConfig.wAudioBufferSize = GetDlgItemInt(hwndDlg, IDC_AUDIOBUFFER, nullptr, FALSE);
+	gConfig.iVolume = TrackBar_GetPos(hwndDlg, IDC_VOLUME);
+	gConfig.iResampleQuality = TrackBar_GetPos(hwndDlg, IDC_QUALITY);
+	if (gConfig.eMusicType == MUSIC_RIX)
+	{
+		gConfig.fUseSurroundOPL = IsDlgButtonChecked(hwndDlg, IDC_SURROUNDOPL);
+		gConfig.iOPLSampleRate = GetDlgItemInt(hwndDlg, IDC_OPLSR, nullptr, FALSE);
+		gConfig.iSurroundOPLOffset = GetDlgItemInt(hwndDlg, IDC_OPLOFFSET, nullptr, TRUE);
+	}
+
+	if (fWriteFile) PAL_SaveConfig();
+}
+
 void ResetControls(HWND hwndDlg)
 {
 	TCHAR buffer[100];
 
 	EnableDlgItem(hwndDlg, IDC_CHS, gConfig.fIsWIN95);
 	EnableDlgItem(hwndDlg, IDC_EMBEDFONT, !gConfig.fIsWIN95);
-	EnableDlgItem(hwndDlg, IDC_MSGFILE, gConfig.pszMsgName ? TRUE : FALSE);
+	EnableDlgItem(hwndDlg, IDC_MSGFILE, gConfig.pszMsgFile ? TRUE : FALSE);
 	EnableDlgItem(hwndDlg, IDC_OPL, gConfig.eMusicType == MUSIC_RIX);
 	EnableDlgItem(hwndDlg, IDC_SURROUNDOPL, gConfig.eMusicType == MUSIC_RIX);
 	EnableDlgItem(hwndDlg, IDC_OPLOFFSET, gConfig.eMusicType == MUSIC_RIX);
 	EnableDlgItem(hwndDlg, IDC_OPLSR, gConfig.eMusicType == MUSIC_RIX);
 
 	CheckRadioButton(hwndDlg, IDC_DOS, IDC_WIN95, gConfig.fIsWIN95 ? IDC_WIN95 : IDC_DOS);
-	CheckRadioButton(hwndDlg, IDC_CHT, IDC_CUSTOM, gConfig.pszMsgName ? IDC_CUSTOM : IDC_CHT + gConfig.iCodePage);
+	CheckRadioButton(hwndDlg, IDC_CHT, IDC_CHS, IDC_CHT + gConfig.uCodePage);
 
 	CheckDlgButton(hwndDlg, IDC_EMBEDFONT, gConfig.fUseEmbeddedFonts);
 	CheckDlgButton(hwndDlg, IDC_ASPECTRATIO, gConfig.fKeepAspectRatio);
@@ -136,10 +157,12 @@ void ResetControls(HWND hwndDlg)
 	SetDlgItemText(hwndDlg, IDC_HEIGHT, _ultot(gConfig.dwScreenHeight, buffer, 10));
 	SetDlgItemText(hwndDlg, IDC_SAMPLERATE, _itot(gConfig.iSampleRate, buffer, 10));
 	SetDlgItemText(hwndDlg, IDC_OPLSR, _itot(gConfig.iOPLSampleRate, buffer, 10));
-	SetDlgItemText(hwndDlg, IDC_OPLOFFSET, _itot((int)gConfig.dSurroundOPLOffset, buffer, 10));
+	SetDlgItemText(hwndDlg, IDC_OPLOFFSET, _itot((int)gConfig.iSurroundOPLOffset, buffer, 10));
 	SetDlgItemText(hwndDlg, IDC_AUDIOBUFFER, _itot(gConfig.wAudioBufferSize, buffer, 10));
 
-	SetDlgItemTextA(hwndDlg, IDC_MSGFILE, gConfig.pszMsgName);
+	if (gConfig.pszGamePath) SetDlgItemTextA(hwndDlg, IDC_GAMEPATH, gConfig.pszGamePath);
+	if (gConfig.pszSavePath) SetDlgItemTextA(hwndDlg, IDC_SAVEPATH, gConfig.pszSavePath);
+	if (gConfig.pszMsgFile) SetDlgItemTextA(hwndDlg, IDC_MSGFILE, gConfig.pszMsgFile);
 
 	TrackBar_SetRange(hwndDlg, IDC_QUALITY, 0, 4, FALSE);
 	TrackBar_SetPos(hwndDlg, IDC_QUALITY, gConfig.iResampleQuality, TRUE);
@@ -149,9 +172,6 @@ void ResetControls(HWND hwndDlg)
 
 INT_PTR InitProc(HWND hwndDlg, HWND hwndCtrl, LPARAM lParam)
 {
-	TCHAR curdir[MAX_PATH * 2];
-	GetCurrentDirectory(MAX_PATH * 2, curdir);
-
 	InitCommonControls();
 
 	ComboBox_AddString(hwndDlg, IDC_CD, TEXT("MP3"));
@@ -165,8 +185,6 @@ INT_PTR InitProc(HWND hwndDlg, HWND hwndCtrl, LPARAM lParam)
 	ComboBox_AddString(hwndDlg, IDC_OPL, TEXT("DOSBOX"));
 	ComboBox_AddString(hwndDlg, IDC_OPL, TEXT("MAME"));
 
-	SetDlgItemText(hwndDlg, IDC_GAMEPATH, curdir);
-
 	ResetControls(hwndDlg);
 
 	WINDOWINFO wi = { sizeof(WINDOWINFO) };
@@ -186,16 +204,25 @@ INT_PTR ButtonProc(HWND hwndDlg, WORD idControl, HWND hwndCtrl)
 	switch (idControl)
 	{
 	case IDOK:
-		SaveSettings(hwndDlg, FALSE);
+	{
+		TCHAR title[40], message[160];
+
+		LoadStringEx(g_hInstance, IDS_CONFIRM, g_wLanguage, title, 40);
+		LoadStringEx(g_hInstance, IDS_LAUNCHSETTING, g_wLanguage, message, 160);
+
+		if (MessageBox(hwndDlg, message, title, MB_YESNO) == IDYES)
+			gConfig.fLaunchSetting = TRUE;
+		else
+			gConfig.fLaunchSetting = FALSE;
+
+		SaveSettings(hwndDlg, TRUE);
 		EndDialog(hwndDlg, IDOK);
 		return TRUE;
+	}
 	case IDCANCEL:
 		EndDialog(hwndDlg, IDCANCEL);
 		return TRUE;
 
-	case IDC_SAVE:
-		SaveSettings(hwndDlg, TRUE);
-		return TRUE;
 	case IDC_DEFAULT:
 		PAL_LoadConfig(FALSE);
 		ResetControls(hwndDlg);
@@ -215,11 +242,22 @@ INT_PTR ButtonProc(HWND hwndDlg, WORD idControl, HWND hwndCtrl)
 		}
 		return TRUE;
 
-	case IDC_CHT:
-	case IDC_CHS:
-	case IDC_CUSTOM:
-		EnableDlgItem(hwndDlg, IDC_MSGFILE, IsDlgButtonChecked(hwndDlg, IDC_CUSTOM));
+	case IDC_BRGAME:
+	case IDC_BRSAVE:
+	{
+		TCHAR szName[MAX_PATH * 2], szTitle[200];
+		BROWSEINFO bi = { hwndDlg, nullptr, szName, szTitle, BIF_USENEWUI, nullptr, NULL, 0 };
+		LoadStringEx(g_hInstance, idControl, g_wLanguage, szTitle, 200);
+		auto pidl = SHBrowseForFolder(&bi);
+		if (pidl)
+		{
+			SHGetPathFromIDList(pidl, szName);
+			int n = _tcslen(szName);
+			if (szName[n - 1] != '\\') _tcscat(szName, L"\\");
+			SetDlgItemText(hwndDlg, idControl - IDC_BRGAME + IDC_GAMEPATH, szName);
+		}
 		return TRUE;
+	}
 
 	default: return FALSE;
 	}
@@ -264,19 +302,19 @@ INT_PTR CALLBACK LauncherDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPAR
 
 extern "C" int UTIL_Platform_Init(int argc, char* argv[])
 {
-	auto module = GetModuleHandle(nullptr);
-	auto lang = GetThreadUILanguage();
-	if (PRIMARYLANGID(lang) == LANG_CHINESE)
+	g_hInstance = GetModuleHandle(nullptr);
+	g_wLanguage = GetThreadUILanguage();
+	if (PRIMARYLANGID(g_wLanguage) == LANG_CHINESE)
 	{
-		if (SUBLANGID(lang) == SUBLANG_CHINESE_SIMPLIFIED || SUBLANGID(lang) == SUBLANG_CHINESE_SINGAPORE)
-			lang = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
+		if (SUBLANGID(g_wLanguage) == SUBLANG_CHINESE_SIMPLIFIED || SUBLANGID(g_wLanguage) == SUBLANG_CHINESE_SINGAPORE)
+			g_wLanguage = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
 		else
-			lang = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL);
+			g_wLanguage = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL);
 	}
 	else
-		lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
-	auto dlg = (LPCDLGTEMPLATE)LockResource(LoadResource(module, FindResourceEx(module, RT_DIALOG, MAKEINTRESOURCE(IDD_LAUNCHER), lang)));
-	if (DialogBoxIndirect(GetModuleHandle(nullptr), dlg, nullptr, LauncherDialogProc) != IDOK)
+		g_wLanguage = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
+	auto dlg = (LPCDLGTEMPLATE)LockResource(LoadResource(g_hInstance, FindResourceEx(g_hInstance, RT_DIALOG, MAKEINTRESOURCE(IDD_LAUNCHER), g_wLanguage)));
+	if (gConfig.fLaunchSetting && DialogBoxIndirect(GetModuleHandle(nullptr), dlg, nullptr, LauncherDialogProc) != IDOK)
 		return -1;
 	else
 		return 0;

winrt/SDLPal.WindowsPhone/SDLPal.Common.def → winrt/SDLPal.Common/SDLPal.Common.def


+ 35 - 0
winrt/SDLPal.Common/SDLPal.cpp

@@ -0,0 +1,35 @@
+#include "pch.h"
+
+#include <wrl.h>
+#include <windows.h>
+#include "../SDLPal.Common/AsyncHelper.h"
+#include "../../global.h"
+#include "App.xaml.h"
+
+HANDLE g_eventHandle;
+
+int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
+{
+	if (FAILED(Windows::Foundation::Initialize(RO_INIT_MULTITHREADED))) {
+		return 1;
+	}
+
+	g_eventHandle = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
+
+	PAL_LoadConfig(TRUE);
+
+	if (gConfig.fLaunchSetting)
+	{
+		Windows::UI::Xaml::Application::Start(
+			ref new Windows::UI::Xaml::ApplicationInitializationCallback(
+				[](Windows::UI::Xaml::ApplicationInitializationCallbackParams^ p) {
+					auto app = ref new SDLPal::App();
+				}
+			)
+		);
+	}
+	else
+		SDL_WinRTRunApp(SDL_main, NULL);
+
+	return 0;
+}

+ 54 - 0
winrt/SDLPal.Common/StringHelper.h

@@ -0,0 +1,54 @@
+#pragma once
+
+#include <wrl.h>
+#include <string>
+
+static void ConvertString(Platform::String^ src, std::string& dst)
+{
+	int len = WideCharToMultiByte(CP_ACP, 0, src->Begin(), -1, nullptr, 0, nullptr, nullptr);
+	dst.resize(len - 1);
+	WideCharToMultiByte(CP_ACP, 0, src->Begin(), -1, (char*)dst.data(), len, nullptr, nullptr);
+}
+
+static std::string ConvertString(Platform::String^ src)
+{
+	int len = WideCharToMultiByte(CP_ACP, 0, src->Begin(), -1, nullptr, 0, nullptr, nullptr);
+	std::string dst(len - 1, ' ');
+	WideCharToMultiByte(CP_ACP, 0, src->Begin(), -1, (char*)dst.data(), len, nullptr, nullptr);
+	return dst;
+}
+
+static std::string ConvertString(const wchar_t* src)
+{
+	int len = WideCharToMultiByte(CP_ACP, 0, src, -1, nullptr, 0, nullptr, nullptr);
+	std::string dst(len - 1, ' ');
+	WideCharToMultiByte(CP_ACP, 0, src, -1, (char*)dst.data(), len, nullptr, nullptr);
+	return dst;
+}
+
+static std::string ConvertString(const std::wstring& src)
+{
+	return ConvertString(src.c_str());
+}
+
+static void ConvertString(const std::string& src, std::wstring& dst)
+{
+	int len = MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, nullptr, 0);
+	dst.resize(len - 1);
+	MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, (wchar_t*)dst.data(), len);
+}
+
+static Platform::String^ ConvertString(const char* src)
+{
+	int len = MultiByteToWideChar(CP_ACP, 0, src, -1, nullptr, 0);
+	auto wc = new wchar_t[len];
+	MultiByteToWideChar(CP_ACP, 0, src, -1, wc, len);
+	auto dst = ref new Platform::String(wc);
+	delete[] wc;
+	return dst;
+}
+
+static Platform::String^ ConvertString(const std::string& src)
+{
+	return ConvertString(src.c_str());
+}

+ 28 - 10
winrt/SDLPal.Common/WinRTIO.cpp

@@ -2,9 +2,10 @@
 #include <string>
 #include <DXGI.h>
 #include <ppltasks.h>
-#include <AsyncHelper.h>
 #include <shcore.h>
 #include <unordered_set>
+#include "AsyncHelper.h"
+#include "StringHelper.h"
 
 #pragma comment(lib, "ShCore.lib")
 
@@ -13,13 +14,6 @@
 static const LARGE_INTEGER liZero = { 0 };
 static const void* const _SIGNATURE = &liZero;
 
-static void ConvertString(const std::string& src, std::wstring& dst)
-{
-	int len = MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, nullptr, 0);
-	dst.resize(len - 1);
-	MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, (wchar_t*)dst.data(), len);
-}
-
 /*========================*/
 
 typedef struct WRT_FILE WRT_FILE;
@@ -297,13 +291,37 @@ int WRT_fputc(int _Ch, WRT_FILE * _File)
 {
 	if (!_File || _File->sig != _SIGNATURE) return fputc(_Ch, (FILE*)_File);
 
-	return EOF;
+	CriticalSection cs(_File->cs);
+	unsigned long cbWrite;
+	return _File->writable && _File->stream->Write(&_Ch, 1, &cbWrite) == S_OK ? cbWrite : EOF;
 }
 
 extern "C"
 int WRT_fputs(const char * _Str, WRT_FILE * _File)
 {
 	if (!_File || _File->sig != _SIGNATURE) return fputs(_Str, (FILE*)_File);
+	if (!_File->writable || !_Str) return EOF;
 
-	return EOF;
+	const char *start = _Str;
+	int length = strlen(_Str);
+	unsigned long cbWrite;
+	const char crlf[] = { '\r', '\n' };
+	CriticalSection cs(_File->cs);
+	while (true)
+	{
+		const char *ptr = start;
+		while (*ptr && *ptr != '\n') ptr++;
+		if (_File->stream->Write(start, ptr - start, &cbWrite) != S_OK)
+			return EOF;
+		if (*ptr == '\n')
+		{
+			if (_File->stream->Write(crlf, 2, &cbWrite) != S_OK)
+				return EOF;
+			else
+				start = ptr + 1;
+		}
+		else
+			break;
+	}
+	return length;
 }

+ 79 - 96
winrt/SDLPal.WindowsPhone/WinRTUtil.cpp

@@ -1,47 +1,18 @@
+#include "pch.h"
+
 #include <wrl.h>
 #include <string>
 #include <DXGI.h>
 #include <ppltasks.h>
 #include "../SDLPal.Common/AsyncHelper.h"
+#include "../SDLPal.Common/StringHelper.h"
 
 extern "C" void TerminateOnError(const char *fmt, ...);
 
 #define PAL_PATH_NAME	"SDLPAL"
 
-static std::string g_savepath, g_basepath;
-static Windows::Storage::StorageFolder ^g_basefolder, ^g_savefolder;
-
-static void ConvertString(Platform::String^ src, std::string& dst)
-{
-	int len = WideCharToMultiByte(CP_ACP, 0, src->Begin(), -1, nullptr, 0, nullptr, nullptr);
-	dst.resize(len - 1);
-	WideCharToMultiByte(CP_ACP, 0, src->Begin(), -1, (char*)dst.data(), len, nullptr, nullptr);
-}
-
-static std::string ConvertString(Platform::String^ src)
-{
-	int len = WideCharToMultiByte(CP_ACP, 0, src->Begin(), -1, nullptr, 0, nullptr, nullptr);
-	std::string dst(len - 1, ' ');
-	WideCharToMultiByte(CP_ACP, 0, src->Begin(), -1, (char*)dst.data(), len, nullptr, nullptr);
-	return dst;
-}
-
-static void ConvertString(const std::string& src, std::wstring& dst)
-{
-	int len = MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, nullptr, 0);
-	dst.resize(len - 1);
-	MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, (wchar_t*)dst.data(), len);
-}
-
-static Platform::String^ ConvertString(const std::string& src)
-{
-	int len = MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, nullptr, 0);
-	auto wc = new wchar_t[len];
-	MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, wc, len);
-	auto dst = ref new Platform::String(wc);
-	delete[] wc;
-	return dst;
-}
+static std::string g_savepath, g_basepath, g_configpath;
+static Windows::Storage::StorageFolder ^g_basefolder, ^g_savefolder, ^g_configfolder;
 
 static Windows::Storage::StorageFolder^ CheckGamePath(Windows::Storage::StorageFolder^ root, HANDLE eventHandle)
 {
@@ -85,58 +56,65 @@ static Windows::Storage::StorageFolder^ CheckGamePath(Windows::Storage::StorageF
 extern "C"
 LPCSTR UTIL_BasePath(VOID)
 {
+	//if (g_basepath.empty())
+	//{
+	//	Windows::Storage::StorageFolder^ folder = nullptr;
+	//	HANDLE eventHandle = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
+	//	auto folders = AWait(Windows::Storage::KnownFolders::RemovableDevices->GetFoldersAsync())->First();
+	//	while (folders->HasCurrent)
+	//	{
+	//		if (folder = CheckGamePath(folders->Current, eventHandle))
+	//			break;
+	//		else
+	//			folders->MoveNext();
+	//	}
+
+	//	if (!folder)
+	//	{
+	//		Windows::Storage::StorageFolder^ search_folders[] = {
+	//			Windows::Storage::KnownFolders::PicturesLibrary,
+	//			Windows::Storage::KnownFolders::SavedPictures,
+	//			//Windows::Storage::KnownFolders::MusicLibrary,
+	//			//Windows::Storage::KnownFolders::VideosLibrary,
+	//		};
+	//		for (int i = 0; i < sizeof(search_folders) / sizeof(search_folders[0]); i++)
+	//		{
+	//			if (folder = CheckGamePath(search_folders[i], eventHandle))
+	//				break;
+	//		}
+	//	}
+
+	//	CloseHandle(eventHandle);
+
+	//	if (folder)
+	//	{
+	//		/* Folder examination succeeded */
+	//		auto path = folder->Path;
+	//		if (path->End()[-1] != L'\\') path += "\\";
+	//		ConvertString(path, g_basepath);
+	//		g_basefolder = folder;
+
+	//		/* Check whether the folder is writable */
+	//		FILE* fp = fopen(ConvertString(path + "sdlpal.rpg").c_str(), "wb");
+	//		if (fp)
+	//		{
+	//			g_savepath = g_basepath;
+	//			g_savefolder = g_basefolder;
+	//			fclose(fp);
+	//		}
+	//	}
+	//	else
+	//	{
+	//		TerminateOnError("Could not find PAL folder.\n");
+	//		//ConvertString(Windows::ApplicationModel::Package::Current->InstalledLocation->Path + "\\Assets\\Data\\", g_basepath);
+	//	}
+	//}
 	if (g_basepath.empty())
 	{
-		Windows::Storage::StorageFolder^ folder = nullptr;
-		HANDLE eventHandle = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
-		auto folders = AWait(Windows::Storage::KnownFolders::RemovableDevices->GetFoldersAsync())->First();
-		while (folders->HasCurrent)
-		{
-			if (folder = CheckGamePath(folders->Current, eventHandle))
-				break;
-			else
-				folders->MoveNext();
-		}
-
-		if (!folder)
-		{
-			Windows::Storage::StorageFolder^ search_folders[] = {
-				Windows::Storage::KnownFolders::PicturesLibrary,
-				Windows::Storage::KnownFolders::SavedPictures,
-				//Windows::Storage::KnownFolders::MusicLibrary,
-				//Windows::Storage::KnownFolders::VideosLibrary,
-			};
-			for (int i = 0; i < sizeof(search_folders) / sizeof(search_folders[0]); i++)
-			{
-				if (folder = CheckGamePath(search_folders[i], eventHandle))
-					break;
-			}
-		}
-
-		CloseHandle(eventHandle);
-
-		if (folder)
-		{
-			/* Folder examination succeeded */
-			auto path = folder->Path;
-			if (path->End()[-1] != L'\\') path += "\\";
-			ConvertString(path, g_basepath);
-			g_basefolder = folder;
-
-			/* Check whether the folder is writable */
-			FILE* fp = fopen(ConvertString(path + "sdlpal.rpg").c_str(), "wb");
-			if (fp)
-			{
-				g_savepath = g_basepath;
-				g_savefolder = g_basefolder;
-				fclose(fp);
-			}
-		}
-		else
-		{
-			TerminateOnError("Could not find PAL folder.\n");
-			//ConvertString(Windows::ApplicationModel::Package::Current->InstalledLocation->Path + "\\Assets\\Data\\", g_basepath);
-		}
+		g_basefolder = Windows::Storage::ApplicationData::Current->LocalFolder;
+		auto localfolder = g_basefolder->Path;
+		if (localfolder->End()[-1] != L'\\') localfolder += "\\";
+		ConvertString(localfolder, g_basepath);
 	}
 	return g_basepath.c_str();
 }
@@ -146,14 +124,27 @@ LPCSTR UTIL_SavePath(VOID)
 {
 	if (g_savepath.empty())
 	{
-		auto localfolder = Windows::Storage::ApplicationData::Current->LocalFolder->Path;
+		g_savefolder = Windows::Storage::ApplicationData::Current->LocalFolder;
+		auto localfolder = g_savefolder->Path;
 		if (localfolder->End()[-1] != L'\\') localfolder += "\\";
 		ConvertString(localfolder, g_savepath);
-		g_savefolder = Windows::Storage::ApplicationData::Current->LocalFolder;
 	}
 	return g_savepath.c_str();
 }
 
+extern "C"
+LPCSTR UTIL_ConfigPath(VOID)
+{
+	if (g_configpath.empty())
+	{
+		g_configfolder = Windows::Storage::ApplicationData::Current->LocalFolder;
+		auto localfolder = g_configfolder->Path;
+		if (localfolder->End()[-1] != L'\\') localfolder += "\\";
+		ConvertString(localfolder, g_configpath);
+	}
+	return g_configpath.c_str();
+}
+
 static BOOL UTIL_IsMobile(VOID)
 {
 	auto rc = Windows::ApplicationModel::Resources::Core::ResourceContext::GetForCurrentView();
@@ -170,7 +161,9 @@ BOOL UTIL_GetScreenSize(DWORD *pdwScreenWidth, DWORD *pdwScreenHeight)
 	IDXGIOutput* pOutput = nullptr;
 	DWORD retval = FALSE;
 
+#if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
 	if (!UTIL_IsMobile()) return FALSE;
+#endif
 
 	if (!pdwScreenWidth || !pdwScreenHeight) return FALSE;
 
@@ -232,13 +225,3 @@ VOID UTIL_Platform_Quit(VOID)
 {
 	longjmp(g_exit_jmp_env, LONGJMP_EXIT_CODE);
 }
-
-int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
-{
-	if (FAILED(Windows::Foundation::Initialize(RO_INIT_MULTITHREADED))) {
-		return 1;
-	}
-
-	SDL_WinRTRunApp(SDL_main, NULL);
-	return 0;
-}

+ 7 - 0
winrt/SDLPal.WindowsPhone/App.xaml

@@ -0,0 +1,7 @@
+<Application
+    x:Class="SDLPal.App"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:SDLPal">
+
+</Application>

+ 174 - 0
winrt/SDLPal.WindowsPhone/App.xaml.cpp

@@ -0,0 +1,174 @@
+//
+// App.xaml.cpp
+// App 类的实现。
+//
+
+#include "pch.h"
+#include "MainPage.xaml.h"
+
+using namespace SDLPal;
+
+using namespace Platform;
+using namespace Windows::ApplicationModel;
+using namespace Windows::ApplicationModel::Activation;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Interop;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Media::Animation;
+using namespace Windows::UI::Xaml::Navigation;
+
+// “空白应用程序”模板在 http://go.microsoft.com/fwlink/?LinkID=391641 上有介绍
+
+/// <summary>
+/// 初始化单一实例应用程序对象。这是执行的创作代码的第一行,
+/// 已执行,逻辑上等同于 main() 或 WinMain()。
+/// </summary>
+App::App()
+{
+	InitializeComponent();
+	Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);
+}
+
+/// <summary>
+/// 在最终用户正常启动应用程序时调用。将在启动应用程序
+/// 当启动应用程序以打开特定的文件或显示时使用
+/// 搜索结果等
+/// </summary>
+/// <param name="e">有关启动请求和过程的详细信息。</param>
+void App::OnLaunched(LaunchActivatedEventArgs^ e)
+{
+#if _DEBUG
+	if (IsDebuggerPresent())
+	{
+		DebugSettings->EnableFrameRateCounter = true;
+	}
+#endif
+
+	auto rootFrame = dynamic_cast<Frame^>(Window::Current->Content);
+
+	// 不要在窗口已包含内容时重复应用程序初始化,
+	// 只需确保窗口处于活动状态。
+	if (rootFrame == nullptr)
+	{
+		// 创建一个 Frame 以用作导航上下文并将其与
+		// SuspensionManager 键关联
+		rootFrame = ref new Frame();
+
+		// TODO: 将此值更改为适合您的应用程序的缓存大小。
+		rootFrame->CacheSize = 1;
+
+		//设置默认语言
+		rootFrame->Language = Windows::Globalization::ApplicationLanguages::Languages->GetAt(0);
+
+		if (e->PreviousExecutionState == ApplicationExecutionState::Terminated)
+		{
+			// TODO: 仅当适用时还原保存的会话状态,并安排
+			// 还原完成后的最终启动步骤。
+		}
+
+		// 将框架放在当前窗口中
+		Window::Current->Content = rootFrame;
+	}
+
+	if (rootFrame->Content == nullptr)
+	{
+		// 删除用于启动的旋转门导航。
+		if (rootFrame->ContentTransitions != nullptr)
+		{
+			_transitions = ref new TransitionCollection();
+			for (auto transition : rootFrame->ContentTransitions)
+			{
+				_transitions->Append(transition);
+			}
+		}
+
+		rootFrame->ContentTransitions = nullptr;
+		_firstNavigatedToken = rootFrame->Navigated += ref new NavigatedEventHandler(this, &App::RootFrame_FirstNavigated);
+
+		// 当导航堆栈尚未还原时,导航到第一页,
+		// 并通过将所需信息作为导航参数传入来配置
+		// 新页面。
+		if (!rootFrame->Navigate(MainPage::typeid, e->Arguments))
+		{
+			throw ref new FailureException("Failed to create initial page");
+		}
+	}
+
+	// 确保当前窗口处于活动状态
+	Window::Current->Activate();
+}
+
+/// <summary>
+/// 启动应用程序后还原内容转换。
+/// </summary>
+void App::RootFrame_FirstNavigated(Object^ sender, NavigationEventArgs^ e)
+{
+	auto rootFrame = safe_cast<Frame^>(sender);
+
+	TransitionCollection^ newTransitions;
+	if (_transitions == nullptr)
+	{
+		newTransitions = ref new TransitionCollection();
+		newTransitions->Append(ref new NavigationThemeTransition());
+	}
+	else
+	{
+		newTransitions = _transitions;
+	}
+
+	rootFrame->ContentTransitions = newTransitions;
+	rootFrame->Navigated -= _firstNavigatedToken;
+}
+
+void SDLPal::App::OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs ^ args)
+{
+	auto main_page = static_cast<MainPage^>(_main_page);
+	switch (args->Kind)
+	{
+	case ActivationKind::PickFolderContinuation:
+	{
+		auto folder = safe_cast<IFolderPickerContinuationEventArgs^>(args)->Folder;
+		if (folder) main_page->SetPath(folder);
+		break;
+	}
+	case ActivationKind::PickSaveFileContinuation:
+	{
+		auto save_args = safe_cast<IFileSavePickerContinuationEventArgs^>(args);
+		if (save_args->File && save_args->ContinuationData->HasKey("Slot"))
+			main_page->Export(save_args->File, safe_cast<Platform::String^>(save_args->ContinuationData->Lookup("Slot")));
+		break;
+	}
+	case ActivationKind::PickFileContinuation:
+	{
+		auto open_args = safe_cast<IFileOpenPickerContinuationEventArgs^>(args);
+		if (open_args->Files->Size > 0 && open_args->ContinuationData->HasKey("Slot"))
+			main_page->Import(open_args->Files->First()->Current, safe_cast<Platform::String^>(open_args->ContinuationData->Lookup("Slot")));
+		break;
+	}
+	}
+	Application::OnActivated(args);
+}
+
+/// <summary>
+/// 在将要挂起应用程序执行时调用。将保存应用程序状态
+/// 无需知道应用程序会被终止还是会恢复,
+/// 并让内存内容保持不变。
+/// </summary>
+void App::OnSuspending(Object^ sender, SuspendingEventArgs^ e)
+{
+	(void) sender;	// 未使用的参数
+	(void) e;		// 未使用的参数
+
+	// TODO: 保存应用程序状态并停止任何后台活动
+}
+
+void App::SetMainPage(Windows::UI::Xaml::Controls::Page^ page)
+{
+	_main_page = page;
+}

+ 38 - 0
winrt/SDLPal.WindowsPhone/App.xaml.h

@@ -0,0 +1,38 @@
+//
+// App.xaml.h
+// App 类的声明。
+//
+
+#pragma once
+
+#include "App.g.h"
+
+namespace SDLPal
+{
+	/// <summary>
+	/// 提供特定于应用程序的行为,以补充默认的应用程序类。
+	/// </summary>
+	ref class App sealed
+	{
+	public:
+		App();
+
+		virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e) override;
+
+		void SetMainPage(Windows::UI::Xaml::Controls::Page^ page);
+
+	protected:
+		virtual void OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs^ args) override;
+
+	private:
+		Windows::UI::Xaml::Media::Animation::TransitionCollection^ _transitions;
+		Windows::Foundation::EventRegistrationToken _firstNavigatedToken;
+		Windows::UI::Xaml::Controls::Page^ _main_page;
+
+		void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ e);
+		void RootFrame_FirstNavigated(Platform::Object^ sender, Windows::UI::Xaml::Navigation::NavigationEventArgs^ e);
+	};
+
+}
+
+extern HANDLE g_eventHandle;

+ 99 - 0
winrt/SDLPal.WindowsPhone/MainPage.xaml

@@ -0,0 +1,99 @@
+<Page
+    x:Class="SDLPal.MainPage"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:SDLPal"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d">
+
+    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
+        <ScrollViewer HorizontalScrollMode="Disabled">
+            <StackPanel VerticalAlignment="Top" Margin="10,0,10,0">
+                <TextBox x:Name="tbGamePath" x:Uid="GamePath" TextWrapping="Wrap" VerticalAlignment="Top" Header="游戏资源文件夹" IsReadOnly="True" PlaceholderText="游戏资源文件夹"/>
+                <Grid VerticalAlignment="Top">
+                    <Button x:Name="btnDefaultGame" x:Uid="ButtonRestore" Content="默认文件夹" HorizontalAlignment="Left" VerticalAlignment="Top" Click="btnDefault_Click" />
+                    <Button x:Name="btnBrowseGame" x:Uid="ButtonBrowse" Content="浏览文件夹" HorizontalAlignment="Right" VerticalAlignment="Top" Click="btnBrowse_Click" />
+                </Grid>
+                <ToggleSwitch x:Name="tsIsDOS" x:Uid="DOS" Header="游戏资源版本" OffContent="WIN95" OnContent="DOS" Toggled="tsIsDOS_Toggled" />
+                <ToggleSwitch x:Name="tsUseEmbedFont" x:Uid="EmbeddedFont" Header="游戏内嵌字体" OffContent="否" OnContent="是" />
+                <ToggleSwitch x:Name="tsLanguage" x:Uid="Language" Header="游戏资源语言" OffContent="繁体中文" OnContent="简体中文" />
+                <TextBox x:Name="tbMsgFile" x:Uid="MessageFile" Header="自定义语言文件名" TextWrapping="Wrap" VerticalAlignment="Top" PlaceholderText="自定义语言文件名"/>
+                <ToggleSwitch x:Name="tsKeepAspect" x:Uid="AspectRatio" Header="保持纵横比" OffContent="否" OnContent="是" />
+                <ToggleSwitch x:Name="tsStereo" x:Uid="Stereo" Header="立体声" OffContent="否" OnContent="是" />
+                <Slider x:Name="slVolume" x:Uid="Volume" Header="音量" TickPlacement="Inline" TickFrequency="10" />
+                <Slider x:Name="slQuality" x:Uid="Quality" Header="音频质量" Maximum="4" LargeChange="1" TickFrequency="1" />
+                <ComboBox x:Name="cbSampleRate" x:Uid="Samplerate" Header="音频输出采样率" PlaceholderText="音频输出采样率">
+                    <ComboBoxItem Content="11025"/>
+                    <ComboBoxItem Content="22050"/>
+                    <ComboBoxItem Content="44100"/>
+                </ComboBox>
+                <ComboBox x:Name="cbAudioBuffer" x:Uid="AudioBuffer" Header="音频缓冲区大小" PlaceholderText="音频缓冲区大小">
+                    <ComboBoxItem Content="512"/>
+                    <ComboBoxItem Content="1024"/>
+                    <ComboBoxItem Content="2048"/>
+                    <ComboBoxItem Content="4096"/>
+                </ComboBox>
+                <ComboBox x:Name="cbCD" x:Uid="CD" Header="CD 音轨格式" PlaceholderText="CD 音轨格式">
+                    <ComboBoxItem Content="MP3"/>
+                    <ComboBoxItem Content="OGG"/>
+                </ComboBox>
+                <ComboBox x:Name="cbBGM" x:Uid="BGM" Header="背景音乐格式" PlaceholderText="背景音乐格式" SelectionChanged="cbBGM_SelectionChanged">
+                    <ComboBoxItem Content="RIX"/>
+                    <ComboBoxItem Content="MP3"/>
+                    <ComboBoxItem Content="OGG"/>
+                </ComboBox>
+                <ComboBox x:Name="cbOPL" x:Uid="OPL" Header="OPL 模拟器" PlaceholderText="OPL 模拟器">
+                    <ComboBoxItem Content="DOSBOX"/>
+                    <ComboBoxItem Content="MAME"/>
+                </ComboBox>
+                <ComboBox x:Name="cbOPLSR" x:Uid="OPLSR" Header="OPL 模拟器采样率" PlaceholderText="OPL 模拟器采样率">
+                    <ComboBoxItem Content="12429"/>
+                    <ComboBoxItem Content="24858"/>
+                    <ComboBoxItem Content="49716"/>
+                </ComboBox>
+                <ToggleSwitch x:Name="tsSurroundOPL" x:Uid="SurroundOPL" Header="环绕声 OPL" OffContent="否" OnContent="是" />
+                <Grid VerticalAlignment="Top">
+                    <Button x:Name="btnReset" x:Uid="ButtonDefault" Content="默认设置" HorizontalAlignment="Left" Click="btnReset_Click" />
+                    <Button x:Name="btnFinish" x:Uid="ButtonFinish" Content="完成设置" HorizontalAlignment="Right" Click="btnFinish_Click" />
+                </Grid>
+                <Border HorizontalAlignment="Stretch" BorderBrush="LightGray" BorderThickness="2" />
+                <Grid VerticalAlignment="Top">
+                    <TextBlock x:Uid="SaveSlot1" Text="进度一" HorizontalAlignment="Left" Margin="0,14" FontSize="24" />
+                    <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
+                        <Button x:Name="btnImport1" x:Uid="ButtonImport" Content="导入" Margin="0,0,10,0" Tag="1.rpg" Click="btnImport_Click" />
+                        <Button x:Name="btnExport1" x:Uid="ButtonExport" Content="导出" Margin="10,0,0,0" Tag="1.rpg" Click="btnExport_Click" />
+                    </StackPanel>
+                </Grid>
+                <Grid VerticalAlignment="Top">
+                    <TextBlock x:Uid="SaveSlot2" Text="进度二" HorizontalAlignment="Left" Margin="0,14" FontSize="24" />
+                    <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
+                        <Button x:Name="btnImport2" x:Uid="ButtonImport" Content="导入" Margin="0,0,10,0" Tag="2.rpg" Click="btnImport_Click" />
+                        <Button x:Name="btnExport2" x:Uid="ButtonExport" Content="导出" Margin="10,0,0,0" Tag="2.rpg" Click="btnExport_Click" />
+                    </StackPanel>
+                </Grid>
+                <Grid VerticalAlignment="Top">
+                    <TextBlock x:Uid="SaveSlot3" Text="进度三" HorizontalAlignment="Left" Margin="0,14" FontSize="24" />
+                    <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
+                        <Button x:Name="btnImport3" x:Uid="ButtonImport" Content="导入" Margin="0,0,10,0" Tag="3.rpg" Click="btnImport_Click" />
+                        <Button x:Name="btnExport3" x:Uid="ButtonExport" Content="导出" Margin="10,0,0,0" Tag="3.rpg" Click="btnExport_Click" />
+                    </StackPanel>
+                </Grid>
+                <Grid VerticalAlignment="Top">
+                    <TextBlock x:Uid="SaveSlot4" Text="进度四" HorizontalAlignment="Left" Margin="0,14" FontSize="24" />
+                    <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
+                        <Button x:Name="btnImport4" x:Uid="ButtonImport" Content="导入" Margin="0,0,10,0" Tag="4.rpg" Click="btnImport_Click" />
+                        <Button x:Name="btnExport4" x:Uid="ButtonExport" Content="导出" Margin="10,0,0,0" Tag="4.rpg" Click="btnExport_Click" />
+                    </StackPanel>
+                </Grid>
+                <Grid VerticalAlignment="Top">
+                    <TextBlock x:Uid="SaveSlot5" Text="进度五" HorizontalAlignment="Left" Margin="0,14" FontSize="24" />
+                    <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
+                        <Button x:Name="btnImport5" x:Uid="ButtonImport" Content="导入" Margin="0,0,10,0" Tag="5.rpg" Click="btnImport_Click" />
+                        <Button x:Name="btnExport5" x:Uid="ButtonExport" Content="导出" Margin="10,0,0,0" Tag="5.rpg" Click="btnExport_Click" />
+                    </StackPanel>
+                </Grid>
+            </StackPanel>
+        </ScrollViewer>
+    </Grid>
+</Page>

+ 263 - 0
winrt/SDLPal.WindowsPhone/MainPage.xaml.cpp

@@ -0,0 +1,263 @@
+//
+// MainPage.xaml.cpp
+// MainPage 类的实现。
+//
+
+#include "pch.h"
+#include "MainPage.xaml.h"
+#include "../SDLPal.Common/StringHelper.h"
+#include "../SDLPal.Common/AsyncHelper.h"
+#include "../../global.h"
+
+using namespace SDLPal;
+
+using namespace Platform;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+
+MainPage::MainPage()
+{
+	InitializeComponent();
+	static_cast<App^>(Application::Current)->SetMainPage(this);
+	LoadControlContents();
+
+	Windows::UI::Xaml::Controls::Button^ buttons[] = { btnExport1, btnExport2, btnExport3, btnExport4, btnExport5 };
+	for (int i = 0; i < 5; i++) m_buttons[i] = buttons[i];
+	CheckSaveSlots();
+}
+
+void SDLPal::MainPage::SetPath(Windows::Storage::StorageFolder^ folder)
+{
+	tbGamePath->Text = folder->Path;
+	tbGamePath->Tag = folder;
+}
+
+void SDLPal::MainPage::Export(Windows::Storage::StorageFile^ file, Platform::String^ slot)
+{
+	auto src = AWait(Windows::Storage::ApplicationData::Current->LocalFolder->GetFileAsync(slot), g_eventHandle);
+	try
+	{
+		AWait(src->CopyAndReplaceAsync(file), g_eventHandle);
+	}
+	catch (Platform::Exception^ e)
+	{
+		(ref new Windows::UI::Popups::MessageDialog(e->Message))->ShowAsync();
+	}
+}
+
+void SDLPal::MainPage::Import(Windows::Storage::StorageFile^ file, Platform::String^ slot)
+{
+	try
+	{
+		AWait(file->CopyAsync(Windows::Storage::ApplicationData::Current->LocalFolder, slot, Windows::Storage::NameCollisionOption::ReplaceExisting), g_eventHandle);
+		m_buttons[_wtoi(slot->Data()) - 1]->IsEnabled = true;
+	}
+	catch (Platform::Exception^ e)
+	{
+		(ref new Windows::UI::Popups::MessageDialog(e->Message))->ShowAsync();
+	}
+}
+
+void SDLPal::MainPage::LoadControlContents()
+{
+	if (gConfig.pszGamePath)
+	{
+		tbGamePath->Text = ConvertString(gConfig.pszGamePath);
+		try { tbGamePath->Tag = AWait(Windows::Storage::StorageFolder::GetFolderFromPathAsync(tbGamePath->Text), g_eventHandle); }
+		catch (Platform::Exception^) {}
+	}
+	else
+	{
+		tbGamePath->Tag = Windows::Storage::ApplicationData::Current->LocalFolder;
+		tbGamePath->Text = Windows::Storage::ApplicationData::Current->LocalFolder->Path;
+	}
+
+	if (gConfig.pszMsgFile) tbMsgFile->Text = ConvertString(gConfig.pszMsgFile);
+
+	tsLanguage->IsOn = (gConfig.uCodePage == CP_GBK);
+	tsIsDOS->IsOn = (gConfig.fIsWIN95 == FALSE);
+	tsUseEmbedFont->IsOn = (gConfig.fUseEmbeddedFonts == TRUE);
+	tsKeepAspect->IsOn = (gConfig.fKeepAspectRatio == TRUE);
+	tsStereo->IsOn = (gConfig.iAudioChannels == 2);
+	tsSurroundOPL->IsOn = (gConfig.fUseSurroundOPL == TRUE);
+
+	slVolume->Value = gConfig.iVolume;
+	slQuality->Value = gConfig.iResampleQuality;
+
+	cbCD->SelectedIndex = (gConfig.eCDType == MUSIC_MP3) ? 0 : 1;
+	if (gConfig.eMusicType == MUSIC_OGG)
+		cbBGM->SelectedIndex = 2;
+	else if (gConfig.eMusicType == MUSIC_MP3)
+		cbBGM->SelectedIndex = 1;
+	else
+		cbBGM->SelectedIndex = 0;
+	cbOPL->SelectedIndex = (int)gConfig.eOPLType;
+
+	if (gConfig.iSampleRate <= 11025)
+		cbSampleRate->SelectedIndex = 0;
+	else if (gConfig.iSampleRate <= 22050)
+		cbSampleRate->SelectedIndex = 1;
+	else
+		cbSampleRate->SelectedIndex = 2;
+
+	if (gConfig.wAudioBufferSize <= 512)
+		cbAudioBuffer->SelectedIndex = 0;
+	else if (gConfig.wAudioBufferSize == 1024)
+		cbAudioBuffer->SelectedIndex = 1;
+	else if (gConfig.wAudioBufferSize == 2048)
+		cbAudioBuffer->SelectedIndex = 2;
+	else
+		cbAudioBuffer->SelectedIndex = 3;
+
+	if (gConfig.iOPLSampleRate <= 12429)
+		cbOPLSR->SelectedIndex = 0;
+	else if (gConfig.iSampleRate <= 24858)
+		cbOPLSR->SelectedIndex = 1;
+	else
+		cbOPLSR->SelectedIndex = 2;
+
+	tsUseEmbedFont->Visibility = tsIsDOS->IsOn ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed;
+}
+
+void SDLPal::MainPage::SaveControlContents()
+{
+	std::wstring path;
+
+	if (gConfig.pszGamePath) free(gConfig.pszGamePath);
+	path.assign(tbGamePath->Text->Data());
+	if (path.back() != '\\') path.append(L"\\");
+	gConfig.pszGamePath = strdup(ConvertString(path).c_str());
+
+	if (gConfig.pszMsgFile) { free(gConfig.pszMsgFile); gConfig.pszMsgFile = NULL; }
+	gConfig.pszMsgFile = (tbMsgFile->Text->Length() > 0) ? strdup(ConvertString(tbMsgFile->Text).c_str()) : nullptr;
+
+	gConfig.fIsWIN95 = tsIsDOS->IsOn ? FALSE : TRUE;
+	gConfig.fUseEmbeddedFonts = tsUseEmbedFont->IsOn ? TRUE : FALSE;
+	gConfig.fKeepAspectRatio = tsKeepAspect->IsOn ? TRUE : FALSE;
+	gConfig.iAudioChannels = tsStereo->IsOn ? 2 : 1;
+	gConfig.fUseSurroundOPL = tsSurroundOPL->IsOn ? TRUE : FALSE;
+
+	gConfig.iVolume = (int)slVolume->Value;
+	gConfig.iResampleQuality = (int)slQuality->Value;
+	gConfig.uCodePage = tsLanguage->IsOn ? CP_GBK : CP_BIG5;
+
+	gConfig.eCDType = (MUSICTYPE)(MUSIC_MP3 + cbCD->SelectedIndex);
+	gConfig.eMusicType = (cbBGM->SelectedIndex >= 1) ? (MUSICTYPE)(MUSIC_MP3 + cbBGM->SelectedIndex) : MUSIC_RIX;
+	gConfig.eOPLType = (OPLTYPE)cbOPL->SelectedIndex;
+
+	gConfig.iSampleRate = wcstoul(static_cast<Platform::String^>(static_cast<ComboBoxItem^>(cbSampleRate->SelectedItem)->Content)->Data(), nullptr, 10);
+	gConfig.iOPLSampleRate = wcstoul(static_cast<Platform::String^>(static_cast<ComboBoxItem^>(cbOPLSR->SelectedItem)->Content)->Data(), nullptr, 10);
+	gConfig.wAudioBufferSize = wcstoul(static_cast<Platform::String^>(static_cast<ComboBoxItem^>(cbAudioBuffer->SelectedItem)->Content)->Data(), nullptr, 10);
+}
+
+void SDLPal::MainPage::CheckSaveSlots()
+{
+	static Platform::String^ slots[] = { "1.rpg", "2.rpg", "3.rpg", "4.rpg", "5.rpg" };
+	auto folder = Windows::Storage::ApplicationData::Current->LocalFolder;
+	for (int i = 0; i < 5; i++)
+	{
+		try
+		{
+			AWait(folder->GetFileAsync(slots[i]), g_eventHandle);
+			m_buttons[i]->IsEnabled = true;
+		}
+		catch (Platform::Exception^)
+		{
+			m_buttons[i]->IsEnabled = false;
+		}
+	}
+}
+
+void SDLPal::MainPage::btnBrowse_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
+{
+	auto picker = ref new Windows::Storage::Pickers::FolderPicker();
+	picker->PickFolderAndContinue();
+}
+
+void SDLPal::MainPage::btnDefault_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
+{
+	tbGamePath->Tag = Windows::Storage::ApplicationData::Current->LocalFolder;
+	tbGamePath->Text = Windows::Storage::ApplicationData::Current->LocalFolder->Path;
+}
+
+void SDLPal::MainPage::tsIsDOS_Toggled(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
+{
+	if (tsIsDOS->IsOn)
+	{
+		tsLanguage->IsOn = false;
+		tsLanguage->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+		tsUseEmbedFont->Visibility = Windows::UI::Xaml::Visibility::Visible;
+	}
+	else
+	{
+		tsLanguage->IsOn = (gConfig.uCodePage == CP_GBK);
+		tsLanguage->Visibility = Windows::UI::Xaml::Visibility::Visible;
+		tsUseEmbedFont->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+	}
+}
+
+void SDLPal::MainPage::cbBGM_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e)
+{
+	auto visibility = (cbBGM->SelectedIndex == 0) ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed;
+	cbOPL->Visibility = visibility;
+	cbOPLSR->Visibility = visibility;
+	tsSurroundOPL->Visibility = visibility;
+}
+
+
+void SDLPal::MainPage::btnReset_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
+{
+	PAL_LoadConfig(FALSE);
+	LoadControlContents();
+}
+
+
+void SDLPal::MainPage::btnFinish_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
+{
+	SaveControlContents();
+	auto loader = ref new Windows::ApplicationModel::Resources::ResourceLoader();
+	auto handler = ref new Windows::UI::Popups::UICommandInvokedHandler(this, &MainPage::CloseUICommandHandler);
+	auto dlg = ref new Windows::UI::Popups::MessageDialog(loader->GetString("MBContent"));
+	dlg->Commands->Append(ref new Windows::UI::Popups::UICommand(loader->GetString("MBYes"), handler, safe_cast<Platform::Object^>(0)));
+	dlg->Commands->Append(ref new Windows::UI::Popups::UICommand(loader->GetString("MBNo"), handler, safe_cast<Platform::Object^>(1)));
+	dlg->ShowAsync();
+}
+
+void SDLPal::MainPage::btnImport_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
+{
+	auto loader = ref new Windows::ApplicationModel::Resources::ResourceLoader();
+	auto picker = ref new Windows::Storage::Pickers::FileOpenPicker();
+	picker->SettingsIdentifier = safe_cast<Platform::String^>(static_cast<Windows::UI::Xaml::Controls::Button^>(sender)->Tag);
+	picker->ContinuationData->Insert("Slot", picker->SettingsIdentifier);
+	picker->FileTypeFilter->Append(".rpg");
+	picker->PickSingleFileAndContinue();
+}
+
+
+void SDLPal::MainPage::btnExport_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
+{
+	auto loader = ref new Windows::ApplicationModel::Resources::ResourceLoader();
+	auto picker = ref new Windows::Storage::Pickers::FileSavePicker();
+	picker->FileTypeChoices->Insert(loader->GetString("SaveSlotType"), ref new Platform::Collections::Vector<Platform::String^>(1, { ".rpg" }));
+	picker->SettingsIdentifier = safe_cast<Platform::String^>(static_cast<Windows::UI::Xaml::Controls::Button^>(sender)->Tag);
+	picker->SuggestedFileName = picker->SettingsIdentifier;
+	picker->DefaultFileExtension = ".rpg";
+	picker->ContinuationData->Insert("Slot", picker->SettingsIdentifier);
+	picker->PickSaveFileAndContinue();
+}
+
+void SDLPal::MainPage::CloseUICommandHandler(Windows::UI::Popups::IUICommand^ command)
+{
+	gConfig.fLaunchSetting = ((int)command->Id == 0);
+	PAL_SaveConfig();
+	for (int i = 0; i < 5; i++) m_buttons[i] = nullptr;
+	Application::Current->Exit();
+}

+ 43 - 0
winrt/SDLPal.WindowsPhone/MainPage.xaml.h

@@ -0,0 +1,43 @@
+//
+// MainPage.xaml.h
+// MainPage 类的声明。
+//
+
+#pragma once
+
+#include "MainPage.g.h"
+
+namespace SDLPal
+{
+	/// <summary>
+	/// 可用于自身或导航至 Frame 内部的空白页。
+	/// </summary>
+	public ref class MainPage sealed
+	{
+	public:
+		MainPage();
+
+		void SetPath(Windows::Storage::StorageFolder^ folder);
+		void Export(Windows::Storage::StorageFile^ file, Platform::String^ slot);
+		void Import(Windows::Storage::StorageFile^ file, Platform::String^ slot);
+
+	protected:
+		void LoadControlContents();
+		void SaveControlContents();
+		void CheckSaveSlots();
+
+	private:
+		Windows::UI::Xaml::Controls::Button^ m_buttons[5];
+
+		void CloseUICommandHandler(Windows::UI::Popups::IUICommand^ command);
+
+		void btnBrowse_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+		void btnDefault_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+		void tsIsDOS_Toggled(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+		void cbBGM_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e);
+		void btnReset_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+		void btnFinish_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+		void btnImport_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+		void btnExport_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+	};
+}

+ 15 - 0
winrt/SDLPal.WindowsPhone/Package.appxmanifest

@@ -37,10 +37,25 @@
             </SupportedFileTypes>
           </FileTypeAssociation>
         </Extension>
+        <Extension Category="windows.fileSavePicker">
+          <FileSavePicker>
+            <SupportedFileTypes>
+              <FileType>.rpg</FileType>
+            </SupportedFileTypes>
+          </FileSavePicker>
+        </Extension>
+        <Extension Category="windows.fileOpenPicker">
+          <FileOpenPicker>
+            <SupportedFileTypes>
+              <FileType>.rpg</FileType>
+            </SupportedFileTypes>
+          </FileOpenPicker>
+        </Extension>
       </Extensions>
     </Application>
   </Applications>
   <Capabilities>
     <Capability Name="removableStorage" />
+    <Capability Name="picturesLibrary" />
   </Capabilities>
 </Package>

+ 16 - 7
winrt/SDLPal.WindowsPhone/SDLPal.Common.vcxproj

@@ -69,38 +69,44 @@
   <PropertyGroup />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <IgnoreImportLibrary>false</IgnoreImportLibrary>
+    <IntDir>$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <IgnoreImportLibrary>false</IgnoreImportLibrary>
+    <IntDir>$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
     <IgnoreImportLibrary>false</IgnoreImportLibrary>
+    <IntDir>$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
     <IgnoreImportLibrary>false</IgnoreImportLibrary>
+    <IntDir>$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <SDLCheck>true</SDLCheck>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Common.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
       <GenerateWindowsMetadata>false</GenerateWindowsMetadata>
-      <ModuleDefinitionFile>SDLPal.Common.def</ModuleDefinitionFile>
+      <ModuleDefinitionFile>../SDLPal.Common/SDLPal.Common.def</ModuleDefinitionFile>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <SDLCheck>true</SDLCheck>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Common.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
       <GenerateWindowsMetadata>false</GenerateWindowsMetadata>
-      <ModuleDefinitionFile>SDLPal.Common.def</ModuleDefinitionFile>
+      <ModuleDefinitionFile>../SDLPal.Common/SDLPal.Common.def</ModuleDefinitionFile>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
@@ -108,12 +114,13 @@
       <PrecompiledHeader>Use</PrecompiledHeader>
       <CompileAsWinRT>false</CompileAsWinRT>
       <SDLCheck>true</SDLCheck>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Common.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
       <GenerateWindowsMetadata>false</GenerateWindowsMetadata>
-      <ModuleDefinitionFile>SDLPal.Common.def</ModuleDefinitionFile>
+      <ModuleDefinitionFile>../SDLPal.Common/SDLPal.Common.def</ModuleDefinitionFile>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
@@ -121,22 +128,24 @@
       <PrecompiledHeader>Use</PrecompiledHeader>
       <CompileAsWinRT>false</CompileAsWinRT>
       <SDLCheck>true</SDLCheck>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Common.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
       <GenerateWindowsMetadata>false</GenerateWindowsMetadata>
-      <ModuleDefinitionFile>SDLPal.Common.def</ModuleDefinitionFile>
+      <ModuleDefinitionFile>../SDLPal.Common/SDLPal.Common.def</ModuleDefinitionFile>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
-    <ClCompile Include="WinRTIO.cpp" />
+    <ClCompile Include="..\SDLPal.Common\WinRTIO.cpp" />
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="AsyncHelper.h" />
+    <ClInclude Include="..\SDLPal.Common\AsyncHelper.h" />
+    <ClInclude Include="..\SDLPal.Common\StringHelper.h" />
   </ItemGroup>
   <ItemGroup>
-    <None Include="SDLPal.Common.def" />
+    <None Include="..\SDLPal.Common\SDLPal.Common.def" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">

+ 4 - 3
winrt/SDLPal.WindowsPhone/SDLPal.Common.vcxproj.filters

@@ -7,12 +7,13 @@
     </Filter>
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="WinRTIO.cpp" />
+    <ClInclude Include="..\SDLPal.Common\AsyncHelper.h" />
+    <ClInclude Include="..\SDLPal.Common\StringHelper.h" />
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="AsyncHelper.h" />
+    <ClCompile Include="..\SDLPal.Common\WinRTIO.cpp" />
   </ItemGroup>
   <ItemGroup>
-    <None Include="SDLPal.Common.def" />
+    <None Include="..\SDLPal.Common\SDLPal.Common.def" />
   </ItemGroup>
 </Project>

+ 10 - 4
winrt/SDLPal.WindowsPhone/SDLPal.Core.vcxproj

@@ -63,25 +63,29 @@
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
     <IncludePath>..\SDL2\include;$(IncludePath)</IncludePath>
+    <IntDir>$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
     <IncludePath>..\SDL2\include;$(IncludePath)</IncludePath>
+    <IntDir>$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <IncludePath>..\SDL2\include;$(IncludePath)</IncludePath>
+    <IntDir>$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <IncludePath>..\SDL2\include;$(IncludePath)</IncludePath>
+    <IntDir>$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;__WINPHONE__=1;PAL_HAS_TOUCH=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
       <AdditionalIncludeDirectories>..\..\liboggvorbis\include;$(IntermediateOutputPath);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <CompileAsWinRT>false</CompileAsWinRT>
       <DisableSpecificWarnings>
       </DisableSpecificWarnings>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Core.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
       <GenerateWindowsMetadata>false</GenerateWindowsMetadata>
@@ -93,11 +97,11 @@
     <ClCompile>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;__WINPHONE__=1;PAL_HAS_TOUCH=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
       <AdditionalIncludeDirectories>..\..\liboggvorbis\include;$(IntermediateOutputPath);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <CompileAsWinRT>false</CompileAsWinRT>
       <DisableSpecificWarnings>
       </DisableSpecificWarnings>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Core.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
       <GenerateWindowsMetadata>false</GenerateWindowsMetadata>
@@ -109,11 +113,11 @@
     <ClCompile>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;__WINPHONE__=1;PAL_HAS_TOUCH=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
       <AdditionalIncludeDirectories>..\..\liboggvorbis\include;$(IntermediateOutputPath);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <CompileAsWinRT>false</CompileAsWinRT>
       <DisableSpecificWarnings>
       </DisableSpecificWarnings>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Core.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
       <GenerateWindowsMetadata>false</GenerateWindowsMetadata>
@@ -125,11 +129,11 @@
     <ClCompile>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;__WINPHONE__=1;PAL_HAS_TOUCH=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
       <AdditionalIncludeDirectories>..\..\liboggvorbis\include;$(IntermediateOutputPath);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <CompileAsWinRT>false</CompileAsWinRT>
       <DisableSpecificWarnings>
       </DisableSpecificWarnings>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Core.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
       <GenerateWindowsMetadata>false</GenerateWindowsMetadata>
@@ -231,6 +235,7 @@
     <ClInclude Include="..\..\main.h" />
     <ClInclude Include="..\..\map.h" />
     <ClInclude Include="..\..\midi.h" />
+    <ClInclude Include="..\..\palcfg.h" />
     <ClInclude Include="..\..\palcommon.h" />
     <ClInclude Include="..\..\palette.h" />
     <ClInclude Include="..\..\play.h" />
@@ -312,6 +317,7 @@
     <ClCompile Include="..\..\mp3play.c" />
     <ClCompile Include="..\..\oggplay.c" />
     <ClCompile Include="..\..\overlay.c" />
+    <ClCompile Include="..\..\palcfg.c" />
     <ClCompile Include="..\..\palcommon.c" />
     <ClCompile Include="..\..\palette.c" />
     <ClCompile Include="..\..\play.c" />

+ 6 - 0
winrt/SDLPal.WindowsPhone/SDLPal.Core.vcxproj.filters

@@ -374,6 +374,9 @@
     <ClInclude Include="..\..\resampler.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\palcfg.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\adplug\binfile.cpp">
@@ -616,6 +619,9 @@
     <ClCompile Include="..\..\overlay.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\palcfg.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\libmad\D.dat">

+ 53 - 14
winrt/SDLPal.WindowsPhone/SDLPal.vcxproj

@@ -22,7 +22,7 @@
     <ProjectGuid>{4c240e16-f6f6-4d60-b29b-7f7acb4815d7}</ProjectGuid>
     <Keyword>BlankApp</Keyword>
     <RootNamespace>SDLPal</RootNamespace>
-    <DefaultLanguage>zh-CN</DefaultLanguage>
+    <DefaultLanguage>en</DefaultLanguage>
     <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
     <AppContainerApplication>true</AppContainerApplication>
     <ApplicationType>Windows Phone</ApplicationType>
@@ -72,66 +72,70 @@
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <IncludePath>..\SDL2\include;$(IncludePath)</IncludePath>
+    <IntDir>$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <IncludePath>..\SDL2\include;$(IncludePath)</IncludePath>
+    <IntDir>$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
     <IncludePath>..\SDL2\include;$(IncludePath)</IncludePath>
+    <IntDir>$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
     <IncludePath>..\SDL2\include;$(IncludePath)</IncludePath>
+    <IntDir>$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
     <ClCompile>
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Main.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
-      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;vccorlibd.lib;msvcrtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;sdlpal.core.lib;vccorlibd.lib;msvcrtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>vccorlibd.lib;msvcrtd.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
-      <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common;$(OutDir)../SDLPal.Core</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
     <ClCompile>
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Main.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
-      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;vccorlib.lib;msvcrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;sdlpal.core.lib;vccorlib.lib;msvcrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>vccorlib.lib;msvcrt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
-      <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common;$(OutDir)../SDLPal.Core</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Main.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
-      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;vccorlibd.lib;msvcrtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;sdlpal.core.lib;vccorlibd.lib;msvcrtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>vccorlibd.lib;msvcrtd.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
-      <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common;$(OutDir)../SDLPal.Core</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).Main.pdb</ProgramDataBaseFileName>
     </ClCompile>
     <Link>
-      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;vccorlib.lib;msvcrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;sdlpal.core.lib;vccorlib.lib;msvcrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>vccorlib.lib;msvcrt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
-      <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common;$(OutDir)../SDLPal.Core</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
@@ -186,7 +190,20 @@
     <Image Include="Assets\WideLogo.scale-240.png" />
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="WinRTUtil.cpp" />
+    <ClCompile Include="App.xaml.cpp">
+      <DependentUpon>App.xaml</DependentUpon>
+    </ClCompile>
+    <ClCompile Include="MainPage.xaml.cpp">
+      <DependentUpon>MainPage.xaml</DependentUpon>
+    </ClCompile>
+    <ClCompile Include="pch.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="..\SDLPal.Common\SDLPal.cpp" />
+    <ClCompile Include="..\SDLPal.Common\WinRTUtil.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\SDL2\VisualC-WinRT\WinPhone81_VS2013\SDL-WinPhone81.vcxproj">
@@ -199,6 +216,28 @@
       <Project>{704d3871-2e86-42ef-a607-cbcfb7a7ebab}</Project>
     </ProjectReference>
   </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="App.xaml">
+      <FileType>Document</FileType>
+    </ApplicationDefinition>
+    <Page Include="MainPage.xaml">
+      <SubType>Designer</SubType>
+    </Page>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="App.xaml.h">
+      <DependentUpon>App.xaml</DependentUpon>
+    </ClInclude>
+    <ClInclude Include="MainPage.xaml.h">
+      <DependentUpon>MainPage.xaml</DependentUpon>
+    </ClInclude>
+    <ClInclude Include="pch.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <PRIResource Include="Strings\en\Resources.resw" />
+    <PRIResource Include="Strings\zh-hans\Resources.resw" />
+    <PRIResource Include="Strings\zh-hant\Resources.resw" />
+  </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>

+ 39 - 1
winrt/SDLPal.WindowsPhone/SDLPal.vcxproj.filters

@@ -23,9 +23,25 @@
     <Image Include="Assets\WideLogo.scale-240.png">
       <Filter>资产</Filter>
     </Image>
+    <Filter Include="Strings">
+      <UniqueIdentifier>{904ef133-c7fd-4fd8-b204-d06e98a03142}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Strings\en">
+      <UniqueIdentifier>{87067f57-ef24-4cb9-8bc2-b81fb6c9a5db}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Strings\zh-hans">
+      <UniqueIdentifier>{ae0fd064-ab20-47bc-9334-a8cd7fd7153c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Strings\zh-hant">
+      <UniqueIdentifier>{1a1bb392-d3b3-4c82-a811-f6cfabd43f9a}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="WinRTUtil.cpp" />
+    <ClCompile Include="..\SDLPal.Common\WinRTUtil.cpp" />
+    <ClCompile Include="..\SDLPal.Common\SDLPal.cpp" />
+    <ClCompile Include="pch.cpp" />
+    <ClCompile Include="App.xaml.cpp" />
+    <ClCompile Include="MainPage.xaml.cpp" />
   </ItemGroup>
   <ItemGroup>
     <AppxManifest Include="Package.appxmanifest" />
@@ -68,4 +84,26 @@
       <Filter>资产</Filter>
     </Image>
   </ItemGroup>
+  <ItemGroup>
+    <Page Include="MainPage.xaml" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="pch.h" />
+    <ClInclude Include="App.xaml.h" />
+    <ClInclude Include="MainPage.xaml.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="App.xaml" />
+  </ItemGroup>
+  <ItemGroup>
+    <PRIResource Include="Strings\en\Resources.resw">
+      <Filter>Strings\en</Filter>
+    </PRIResource>
+    <PRIResource Include="Strings\zh-hans\Resources.resw">
+      <Filter>Strings\zh-hans</Filter>
+    </PRIResource>
+    <PRIResource Include="Strings\zh-hant\Resources.resw">
+      <Filter>Strings\zh-hant</Filter>
+    </PRIResource>
+  </ItemGroup>
 </Project>

+ 273 - 0
winrt/SDLPal.WindowsPhone/Strings/en/Resources.resw

@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="AspectRatio.Header" xml:space="preserve">
+    <value>Keep aspect ratio</value>
+  </data>
+  <data name="AspectRatio.OffContent" xml:space="preserve">
+    <value>No</value>
+  </data>
+  <data name="AspectRatio.OnContent" xml:space="preserve">
+    <value>Yes</value>
+  </data>
+  <data name="AudioBuffer.Header" xml:space="preserve">
+    <value>Audio buffer size</value>
+  </data>
+  <data name="AudioBuffer.PlaceholderText" xml:space="preserve">
+    <value>Audio buffer size</value>
+  </data>
+  <data name="BGM.Header" xml:space="preserve">
+    <value>Format of BGM</value>
+  </data>
+  <data name="BGM.PlaceholderText" xml:space="preserve">
+    <value>Format of BGM</value>
+  </data>
+  <data name="ButtonBrowse.Content" xml:space="preserve">
+    <value>Browse folder</value>
+  </data>
+  <data name="ButtonDefault.Content" xml:space="preserve">
+    <value>Default setting</value>
+  </data>
+  <data name="ButtonExport.Content" xml:space="preserve">
+    <value>Export</value>
+  </data>
+  <data name="ButtonFinish.Content" xml:space="preserve">
+    <value>Finish setting</value>
+  </data>
+  <data name="ButtonImport.Content" xml:space="preserve">
+    <value>Import</value>
+  </data>
+  <data name="ButtonRestore.Content" xml:space="preserve">
+    <value>Default folder</value>
+  </data>
+  <data name="CD.Header" xml:space="preserve">
+    <value>Format of CD track</value>
+  </data>
+  <data name="CD.PlaceholderText" xml:space="preserve">
+    <value>Format of CD track</value>
+  </data>
+  <data name="DOS.Header" xml:space="preserve">
+    <value>Game resource version</value>
+  </data>
+  <data name="DOS.OffContent" xml:space="preserve">
+    <value>WIN95</value>
+  </data>
+  <data name="DOS.OnContent" xml:space="preserve">
+    <value>DOS</value>
+  </data>
+  <data name="EmbeddedFont.Header" xml:space="preserve">
+    <value>Use the font within game resource</value>
+  </data>
+  <data name="EmbeddedFont.OffContent" xml:space="preserve">
+    <value>No</value>
+  </data>
+  <data name="EmbeddedFont.OnContent" xml:space="preserve">
+    <value>Yes</value>
+  </data>
+  <data name="GamePath.Header" xml:space="preserve">
+    <value>Folder of game resource</value>
+  </data>
+  <data name="GamePath.PlaceholderText" xml:space="preserve">
+    <value>Folder of game resource</value>
+  </data>
+  <data name="Language.Header" xml:space="preserve">
+    <value>Game resource language</value>
+  </data>
+  <data name="Language.OffContent" xml:space="preserve">
+    <value>Traditional Chinese</value>
+  </data>
+  <data name="Language.OnContent" xml:space="preserve">
+    <value>Simplified Chinese</value>
+  </data>
+  <data name="MBContent" xml:space="preserve">
+    <value>Launch the setting interface on next start?</value>
+  </data>
+  <data name="MBNo" xml:space="preserve">
+    <value>No</value>
+  </data>
+  <data name="MBYes" xml:space="preserve">
+    <value>Yes</value>
+  </data>
+  <data name="MessageFile.Header" xml:space="preserve">
+    <value>Customized message file</value>
+  </data>
+  <data name="MessageFile.PlaceholderText" xml:space="preserve">
+    <value>Customized message file</value>
+  </data>
+  <data name="OPL.Header" xml:space="preserve">
+    <value>Type of OPL simulator</value>
+  </data>
+  <data name="OPL.PlaceholderText" xml:space="preserve">
+    <value>Type of OPL simulator</value>
+  </data>
+  <data name="OPLSR.Header" xml:space="preserve">
+    <value>Sample rate of OPL simulator</value>
+  </data>
+  <data name="OPLSR.PlaceholderText" xml:space="preserve">
+    <value>Sample rate of OPL simulator</value>
+  </data>
+  <data name="Quality.Header" xml:space="preserve">
+    <value>Audio quality</value>
+  </data>
+  <data name="Samplerate.Header" xml:space="preserve">
+    <value>Sample rate of audio output</value>
+  </data>
+  <data name="Samplerate.PlaceholderText" xml:space="preserve">
+    <value>Sample rate of audio output</value>
+  </data>
+  <data name="SaveSlot1.Text" xml:space="preserve">
+    <value>Save slot 1</value>
+  </data>
+  <data name="SaveSlot2.Text" xml:space="preserve">
+    <value>Save slot 2</value>
+  </data>
+  <data name="SaveSlot3.Text" xml:space="preserve">
+    <value>Save slot 3</value>
+  </data>
+  <data name="SaveSlot4.Text" xml:space="preserve">
+    <value>Save slot 4</value>
+  </data>
+  <data name="SaveSlot5.Text" xml:space="preserve">
+    <value>Save slot 5</value>
+  </data>
+  <data name="SaveSlotType" xml:space="preserve">
+    <value>Save slot file</value>
+  </data>
+  <data name="Stereo.Header" xml:space="preserve">
+    <value>Stereo</value>
+  </data>
+  <data name="Stereo.OffContent" xml:space="preserve">
+    <value>No</value>
+  </data>
+  <data name="Stereo.OnContent" xml:space="preserve">
+    <value>Yes</value>
+  </data>
+  <data name="SurroundOPL.Header" xml:space="preserve">
+    <value>Use surround OPL</value>
+  </data>
+  <data name="SurroundOPL.OffContent" xml:space="preserve">
+    <value>No</value>
+  </data>
+  <data name="SurroundOPL.OnContent" xml:space="preserve">
+    <value>Yes</value>
+  </data>
+  <data name="Volume.Header" xml:space="preserve">
+    <value>Volume</value>
+  </data>
+</root>

+ 273 - 0
winrt/SDLPal.WindowsPhone/Strings/zh-hans/Resources.resw

@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="AspectRatio.Header" xml:space="preserve">
+    <value>保持纵横比</value>
+  </data>
+  <data name="AspectRatio.OffContent" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="AspectRatio.OnContent" xml:space="preserve">
+    <value>是</value>
+  </data>
+  <data name="AudioBuffer.Header" xml:space="preserve">
+    <value>音频缓冲区大小</value>
+  </data>
+  <data name="AudioBuffer.PlaceholderText" xml:space="preserve">
+    <value>音频缓冲区大小</value>
+  </data>
+  <data name="BGM.Header" xml:space="preserve">
+    <value>背景音乐格式</value>
+  </data>
+  <data name="BGM.PlaceholderText" xml:space="preserve">
+    <value>背景音乐格式</value>
+  </data>
+  <data name="ButtonBrowse.Content" xml:space="preserve">
+    <value>浏览文件夹</value>
+  </data>
+  <data name="ButtonDefault.Content" xml:space="preserve">
+    <value>默认设置</value>
+  </data>
+  <data name="ButtonExport.Content" xml:space="preserve">
+    <value>导出</value>
+  </data>
+  <data name="ButtonFinish.Content" xml:space="preserve">
+    <value>完成设置</value>
+  </data>
+  <data name="ButtonImport.Content" xml:space="preserve">
+    <value>导入</value>
+  </data>
+  <data name="ButtonRestore.Content" xml:space="preserve">
+    <value>默认文件夹</value>
+  </data>
+  <data name="CD.Header" xml:space="preserve">
+    <value>CD 音轨格式</value>
+  </data>
+  <data name="CD.PlaceholderText" xml:space="preserve">
+    <value>CD 音轨格式</value>
+  </data>
+  <data name="DOS.Header" xml:space="preserve">
+    <value>游戏资源版本</value>
+  </data>
+  <data name="DOS.OffContent" xml:space="preserve">
+    <value>WIN95</value>
+  </data>
+  <data name="DOS.OnContent" xml:space="preserve">
+    <value>DOS</value>
+  </data>
+  <data name="EmbeddedFont.Header" xml:space="preserve">
+    <value>使用游戏资源内置字体</value>
+  </data>
+  <data name="EmbeddedFont.OffContent" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="EmbeddedFont.OnContent" xml:space="preserve">
+    <value>是</value>
+  </data>
+  <data name="GamePath.Header" xml:space="preserve">
+    <value>游戏资源文件夹</value>
+  </data>
+  <data name="GamePath.PlaceholderText" xml:space="preserve">
+    <value>游戏资源文件夹</value>
+  </data>
+  <data name="Language.Header" xml:space="preserve">
+    <value>游戏资源语言</value>
+  </data>
+  <data name="Language.OffContent" xml:space="preserve">
+    <value>繁体中文</value>
+  </data>
+  <data name="Language.OnContent" xml:space="preserve">
+    <value>简体中文</value>
+  </data>
+  <data name="MBContent" xml:space="preserve">
+    <value>下次启动时要进入配置界面吗?</value>
+  </data>
+  <data name="MBNo" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="MBYes" xml:space="preserve">
+    <value>是</value>
+  </data>
+  <data name="MessageFile.Header" xml:space="preserve">
+    <value>自定义语言文件</value>
+  </data>
+  <data name="MessageFile.PlaceholderText" xml:space="preserve">
+    <value>自定义语言文件</value>
+  </data>
+  <data name="OPL.Header" xml:space="preserve">
+    <value>OPL 模拟器</value>
+  </data>
+  <data name="OPL.PlaceholderText" xml:space="preserve">
+    <value>OPL 模拟器</value>
+  </data>
+  <data name="OPLSR.Header" xml:space="preserve">
+    <value>OPL 模拟器采样率</value>
+  </data>
+  <data name="OPLSR.PlaceholderText" xml:space="preserve">
+    <value>OPL 模拟器采样率</value>
+  </data>
+  <data name="Quality.Header" xml:space="preserve">
+    <value>音频质量</value>
+  </data>
+  <data name="Samplerate.Header" xml:space="preserve">
+    <value>音频输出采样率</value>
+  </data>
+  <data name="Samplerate.PlaceholderText" xml:space="preserve">
+    <value>音频输出采样率</value>
+  </data>
+  <data name="SaveSlot1.Text" xml:space="preserve">
+    <value>进度一</value>
+  </data>
+  <data name="SaveSlot2.Text" xml:space="preserve">
+    <value>进度二</value>
+  </data>
+  <data name="SaveSlot3.Text" xml:space="preserve">
+    <value>进度三</value>
+  </data>
+  <data name="SaveSlot4.Text" xml:space="preserve">
+    <value>进度四</value>
+  </data>
+  <data name="SaveSlot5.Text" xml:space="preserve">
+    <value>进度五</value>
+  </data>
+  <data name="SaveSlotType" xml:space="preserve">
+    <value>存档文件夹</value>
+  </data>
+  <data name="Stereo.Header" xml:space="preserve">
+    <value>立体声</value>
+  </data>
+  <data name="Stereo.OffContent" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="Stereo.OnContent" xml:space="preserve">
+    <value>是</value>
+  </data>
+  <data name="SurroundOPL.Header" xml:space="preserve">
+    <value>环绕声 OPL</value>
+  </data>
+  <data name="SurroundOPL.OffContent" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="SurroundOPL.OnContent" xml:space="preserve">
+    <value>是</value>
+  </data>
+  <data name="Volume.Header" xml:space="preserve">
+    <value>音量</value>
+  </data>
+</root>

+ 273 - 0
winrt/SDLPal.WindowsPhone/Strings/zh-hant/Resources.resw

@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="AspectRatio.Header" xml:space="preserve">
+    <value>保持縱橫比</value>
+  </data>
+  <data name="AspectRatio.OffContent" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="AspectRatio.OnContent" xml:space="preserve">
+    <value>是</value>
+  </data>
+  <data name="AudioBuffer.Header" xml:space="preserve">
+    <value>音訊緩衝區大小</value>
+  </data>
+  <data name="AudioBuffer.PlaceholderText" xml:space="preserve">
+    <value>音訊緩衝區大小</value>
+  </data>
+  <data name="BGM.Header" xml:space="preserve">
+    <value>背景音樂格式</value>
+  </data>
+  <data name="BGM.PlaceholderText" xml:space="preserve">
+    <value>背景音樂格式</value>
+  </data>
+  <data name="ButtonBrowse.Content" xml:space="preserve">
+    <value>瀏覽資料夾</value>
+  </data>
+  <data name="ButtonDefault.Content" xml:space="preserve">
+    <value>默認設定</value>
+  </data>
+  <data name="ButtonExport.Content" xml:space="preserve">
+    <value>導出</value>
+  </data>
+  <data name="ButtonFinish.Content" xml:space="preserve">
+    <value>完成設定</value>
+  </data>
+  <data name="ButtonImport.Content" xml:space="preserve">
+    <value>導入</value>
+  </data>
+  <data name="ButtonRestore.Content" xml:space="preserve">
+    <value>預設資料夾</value>
+  </data>
+  <data name="CD.Header" xml:space="preserve">
+    <value>CD 音軌格式</value>
+  </data>
+  <data name="CD.PlaceholderText" xml:space="preserve">
+    <value>CD 音軌格式</value>
+  </data>
+  <data name="DOS.Header" xml:space="preserve">
+    <value>遊戲資源版本</value>
+  </data>
+  <data name="DOS.OffContent" xml:space="preserve">
+    <value>WIN95</value>
+  </data>
+  <data name="DOS.OnContent" xml:space="preserve">
+    <value>DOS</value>
+  </data>
+  <data name="EmbeddedFont.Header" xml:space="preserve">
+    <value>使用遊戲資源內置字體</value>
+  </data>
+  <data name="EmbeddedFont.OffContent" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="EmbeddedFont.OnContent" xml:space="preserve">
+    <value>是</value>
+  </data>
+  <data name="GamePath.Header" xml:space="preserve">
+    <value>遊戲資源檔夾</value>
+  </data>
+  <data name="GamePath.PlaceholderText" xml:space="preserve">
+    <value>遊戲資源檔夾</value>
+  </data>
+  <data name="Language.Header" xml:space="preserve">
+    <value>遊戲資源語言</value>
+  </data>
+  <data name="Language.OffContent" xml:space="preserve">
+    <value>繁體中文</value>
+  </data>
+  <data name="Language.OnContent" xml:space="preserve">
+    <value>簡體中文</value>
+  </data>
+  <data name="MBContent" xml:space="preserve">
+    <value>下次啟動時要進入設定介面嗎?</value>
+  </data>
+  <data name="MBNo" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="MBYes" xml:space="preserve">
+    <value>是</value>
+  </data>
+  <data name="MessageFile.Header" xml:space="preserve">
+    <value>自訂語言檔</value>
+  </data>
+  <data name="MessageFile.PlaceholderText" xml:space="preserve">
+    <value>自訂語言檔</value>
+  </data>
+  <data name="OPL.Header" xml:space="preserve">
+    <value>OPL 模擬器</value>
+  </data>
+  <data name="OPL.PlaceholderText" xml:space="preserve">
+    <value>OPL 模擬器</value>
+  </data>
+  <data name="OPLSR.Header" xml:space="preserve">
+    <value>OPL 模擬器取樣速率</value>
+  </data>
+  <data name="OPLSR.PlaceholderText" xml:space="preserve">
+    <value>OPL 模擬器取樣速率</value>
+  </data>
+  <data name="Quality.Header" xml:space="preserve">
+    <value>音訊品質</value>
+  </data>
+  <data name="Samplerate.Header" xml:space="preserve">
+    <value>音訊輸出取樣速率</value>
+  </data>
+  <data name="Samplerate.PlaceholderText" xml:space="preserve">
+    <value>音訊輸出取樣速率</value>
+  </data>
+  <data name="SaveSlot1.Text" xml:space="preserve">
+    <value>進度一</value>
+  </data>
+  <data name="SaveSlot2.Text" xml:space="preserve">
+    <value>進度二</value>
+  </data>
+  <data name="SaveSlot3.Text" xml:space="preserve">
+    <value>進度三</value>
+  </data>
+  <data name="SaveSlot4.Text" xml:space="preserve">
+    <value>進度四</value>
+  </data>
+  <data name="SaveSlot5.Text" xml:space="preserve">
+    <value>進度五</value>
+  </data>
+  <data name="SaveSlotType" xml:space="preserve">
+    <value>進度檔</value>
+  </data>
+  <data name="Stereo.Header" xml:space="preserve">
+    <value>立體聲</value>
+  </data>
+  <data name="Stereo.OffContent" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="Stereo.OnContent" xml:space="preserve">
+    <value>是</value>
+  </data>
+  <data name="SurroundOPL.Header" xml:space="preserve">
+    <value>環繞聲 OPL</value>
+  </data>
+  <data name="SurroundOPL.OffContent" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="SurroundOPL.OnContent" xml:space="preserve">
+    <value>是</value>
+  </data>
+  <data name="Volume.Header" xml:space="preserve">
+    <value>音量</value>
+  </data>
+</root>

+ 6 - 0
winrt/SDLPal.WindowsPhone/pch.cpp

@@ -0,0 +1,6 @@
+//
+// pch.cpp
+// 包含标准头并生成预编译头。
+//
+
+#include "pch.h"

+ 10 - 0
winrt/SDLPal.WindowsPhone/pch.h

@@ -0,0 +1,10 @@
+//
+// pch.h
+// 标准系统包含文件的头。
+//
+
+#pragma once
+
+#include <collection.h>
+#include <ppltasks.h>
+#include "App.xaml.h"