From 3963a7a53cb74ea3c75a3b9f7d97ee505c618359 Mon Sep 17 00:00:00 2001
From: John Peterson <john.s.peterson@live.com>
Date: Thu, 12 Jul 2012 05:28:08 +0000
Subject: [PATCH] Applied a series of patches by John Peterson:

- Fixed a printf statement in ConsoleDebugger.cpp.
- Fixed memory watches not breaking in CPUDebug.h.
- Added some helpful batch files to the VS2008 directory.
- Added support for automatic loading of parent ROM sets.
---
 Docs/README.txt                     | 56 ++++--------------
 Src/Debugger/CPUDebug.h             |  9 ++-
 Src/Debugger/ConsoleDebugger.cpp    |  2 +-
 Src/Games.cpp                       | 41 ++++++++++++++
 Src/Games.h                         |  1 +
 Src/ROMLoad.cpp                     | 88 +++++++++++++++++++++++++++--
 VS2008/Build.Release.Debugger.bat   |  2 +
 VS2008/Build.Release.bat            |  4 ++
 VS2008/Rebuild.Release.Debugger.bat |  1 +
 VS2008/Rebuild.Release.bat          |  1 +
 10 files changed, 154 insertions(+), 51 deletions(-)
 create mode 100644 VS2008/Build.Release.Debugger.bat
 create mode 100644 VS2008/Build.Release.bat
 create mode 100644 VS2008/Rebuild.Release.Debugger.bat
 create mode 100644 VS2008/Rebuild.Release.bat

diff --git a/Docs/README.txt b/Docs/README.txt
index 167d4ad..9c83841 100644
--- a/Docs/README.txt
+++ b/Docs/README.txt
@@ -64,13 +64,12 @@ still low.
      8. Force Feedback
      9. Save States and NVRAM
     10. Game-Specific Comments and Tips
-    11. Merging Split ROM Sets
-    12. The Configuration File
-    13. Index of Command Line Options
-    14. Index of Configuration File Settings
-    15. Compiling the Source Code
-    16. Contact Information
-    17. Acknowledgments
+    11. The Configuration File
+    12. Index of Command Line Options
+    13. Index of Configuration File Settings
+    14. Compiling the Source Code
+    15. Contact Information
+    16. Acknowledgments
     
 
 =======================
@@ -807,38 +806,7 @@ and, in the 'Game Assignments' menu, performing the following sequence:
 
 
 ==============================
-  11. Merging Split ROM Sets
-==============================
-
-ROMs that are split into parent and child sets (eg., 'Scud Race Plus', whose
-parent ROM set is 'Scud Race') must be combined into a single ZIP file.  ROM
-files from the parent set that have the same IC numbers (usually the file 
-extension but sometimes the number in the file name itself) as child ROMs
-should be deleted, otherwise Supermodel may choose to load the parent game. 
-
-For example, 'Scud Race Plus' is normally distributed containing only the
-following files:
-
-    epr-20092a.17
-    epr-20093a.18
-    epr-20094a.19
-    epr-20095a.20
-    epr-20096a.21
-    mpr-20097.13
-    mpr-20098.14
-    mpr-20099.15
-    mpr-20100.16
-    mpr-20101.24
-    
-To merge with the parent ROM set, copy over all files from 'Scud Race' except
-those with extension numbers 17-21, 13-16, and 24.  Some 'Scud Race Plus' ROM
-sets may have 'mpr-20101.23' instead of 'mpr-20101.24'.  They are the same file
-and in both cases should replace the file with extension 24 from 'Scud Race'
-('mpr-19671.24').
-
-
-==============================
-  12. The Configuration File
+  11. The Configuration File
 ==============================
 
 Supermodel reads configuration settings from 'Supermodel.ini' located in the
@@ -1116,7 +1084,7 @@ option '-input-system=rawinput' and the input mappings configured for each gun.
 
 
 =====================================
-  13. Index of Command Line Options
+  12. Index of Command Line Options
 =====================================
 
 All valid command line settings are listed here, ordered by category.  Defaults
@@ -1290,7 +1258,7 @@ not.  All options are case sensitive.
 
 
 ============================================
-  14. Index of Configuration File Settings
+  13. Index of Configuration File Settings
 ============================================
 
 All valid configuration file settings are listed here, ordered by category.
@@ -1860,7 +1828,7 @@ All settings are case sensitive.
     
 
 =================================
-  15. Compiling the Source Code
+  14. Compiling the Source Code
 =================================
 
 First, ensure that OpenGL, SDL (http://www.libsdl.org), and zlib
@@ -1894,7 +1862,7 @@ When everything is ready, rename the appropriate Makefile to 'Makefile' and run
 
 
 ===========================
-  16. Contact Information
+  15. Contact Information
 ===========================
 
 The official Supermodel web site is:
@@ -1915,7 +1883,7 @@ We ask that you remain mindful of the following courtesies:
 
 
 =======================
-  17. Acknowledgments
+  16. Acknowledgments
 =======================
 
 Numerous people contributed their precious time and energy to this project.
diff --git a/Src/Debugger/CPUDebug.h b/Src/Debugger/CPUDebug.h
index bfdcbdb..b5a3848 100644
--- a/Src/Debugger/CPUDebug.h
+++ b/Src/Debugger/CPUDebug.h
@@ -689,6 +689,8 @@ namespace Debugger
 			else if (mappedIO == NULL)
 				return;
 		}
+		if (m_break)
+			WaitCommand(HaltUntil);
 	}
 
 	inline void CCPUDebug::CheckWrite(UINT32 addr, unsigned dataSize, UINT64 data)
@@ -735,6 +737,8 @@ namespace Debugger
 			}
 			else if (mappedIO == NULL)
 				return;
+			if (m_break)
+				WaitCommand(HaltUntil);
 		}
 	}
 
@@ -777,6 +781,8 @@ namespace Debugger
 		CWatch *watch = (CWatch*)m_memWatchTable->Get(addr);
 		if (watch != NULL && watch->CheckRead(addr, dataSize, data))
 			MemWatchTriggered(watch, addr, dataSize, data, true);
+		if (m_break)
+			WaitCommand(HaltUntil);
 	}
 
 	inline void CCPUDebug::CheckRead16(UINT32 addr, UINT16 data)
@@ -820,9 +826,10 @@ namespace Debugger
 		CWatch *watch = (CWatch*)m_memWatchTable->Get(addr);
 		if (watch != NULL && watch->CheckWrite(addr, dataSize, data))
 			MemWatchTriggered(watch, addr, dataSize, data, false);
+		if (m_break)
+			WaitCommand(HaltUntil);
 	}
 
-	inline void CCPUDebug::CheckWrite16(UINT32 addr, UINT16 data)
 	{
 		if ((addr&m_mem16AndMask) == m_mem16AndMask && (addr&m_mem16OrMask) == 0)
 			CheckWrite(addr, 2, data);
diff --git a/Src/Debugger/ConsoleDebugger.cpp b/Src/Debugger/ConsoleDebugger.cpp
index dc80b79..b92b84c 100644
--- a/Src/Debugger/ConsoleDebugger.cpp
+++ b/Src/Debugger/ConsoleDebugger.cpp
@@ -1030,7 +1030,7 @@ namespace Debugger
 			m_cpu->WriteMem(addr, size, data);
 			m_cpu->FormatData(dataStr, size, data);
 			m_cpu->FormatAddress(addrStr, addr);
-			Print("Set %s data at %s to %s.\n", addrStr, dataStr);
+			Print("Set %s data at %s to %s.\n", uSizeStr, addrStr, dataStr);
 		}
 		else if (CheckToken(token, "lo", "listios"))				// listios
 		{
diff --git a/Src/Games.cpp b/Src/Games.cpp
index 428ab07..790bad4 100644
--- a/Src/Games.cpp
+++ b/Src/Games.cpp
@@ -38,6 +38,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Sega Bass Fishing
 	{
 		"bass",
+		NULL,
 		"Sega Bass Fishing",
 		"Sega",
 		1997,
@@ -105,6 +106,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Daytona USA 2 (Revision A)
 	{
 		"daytona2",
+		NULL,
 		"Daytona USA 2 Battle on the Edge",
 		"Sega",
 		1998,
@@ -188,6 +190,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Daytona USA 2 Power Edition
 	{
 		"dayto2pe",
+		NULL,
 		"Daytona USA 2 Power Edition",
 		"Sega",
 		1998,
@@ -271,6 +274,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Dirt Devils (Revision A)
 	{
 		"dirtdvls",
+		NULL,
 		"Dirt Devils",
 		"Sega",
 		1998,
@@ -333,6 +337,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Dirt Devils (Alt)(Revision A)
 	{
 		"dirtdvlsa",
+		"dirtdvls",
 		"Dirt Devils (Alt.)",
 		"Sega",
 		1998,
@@ -395,6 +400,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Emergency Call Ambulance
 	{
 		"eca",
+		NULL,
 		"Emergency Call Ambulance",
 		"Sega",
 		1999,
@@ -464,6 +470,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Emergency Call Ambulance (Export)
 	{
 		"ecax",
+		"eca",
 		"Emergency Call Ambulance (Export)",
 		"Sega",
 		1999,
@@ -533,6 +540,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Fighting Vipers 2 (Revision A)
 	{
 		"fvipers2",
+		NULL,
 		"Fighting Vipers 2",
 		"Sega",
 		1998,
@@ -608,6 +616,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Get Bass
 	{
 		"getbass",
+		NULL,
 		"Get Bass",
 		"Sega",
 		1997,
@@ -675,6 +684,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Harley-Davidson & L.A. Riders (Revision A)
 	{
 		"harley",
+		NULL,
 		"Harley-Davidson & L.A. Riders",
 		"Sega",
 		1997,
@@ -744,6 +754,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Harley-Davidson & L.A. Riders (Revision B)
 	{
 		"harleyb",
+		"harley",
 		"Harley-Davidson & L.A. Riders (Revision B)",
 		"Sega",
 		1997,
@@ -813,6 +824,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// L.A. Machineguns
 	{
 		"lamachin",
+		NULL,
 		"L.A. Machineguns",
 		"Sega",
 		1998,
@@ -882,6 +894,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Le Mans 24
 	{
 		"lemans24",
+		NULL,
 		"Le Mans 24",
 		"Sega",
 		1997,
@@ -949,6 +962,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// The Lost World
 	{
 		"lostwsga",
+		NULL,
 		"The Lost World",
 		"Sega",
 		1997,
@@ -1022,6 +1036,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Magical Truck Adventure
 	{
 		"magtruck",
+		NULL,
 		"Magical Truck Adventure",
 		"Sega",
 		1998,
@@ -1079,6 +1094,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// The Ocean Hunter
 	{
 		"oceanhun",
+		NULL,
 		"The Ocean Hunter",
 		"Sega",
 		1998,
@@ -1148,6 +1164,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Scud Race (Australia)
 	{
 		"scud",
+		NULL,
 		"Scud Race (Australia)",
 		"Sega",
 		1996,
@@ -1223,6 +1240,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Scud Race (Export)
 	{
 		"scuda",
+		"scud",
 		"Scud Race (Export)",
 		"Sega",
 		1996,
@@ -1298,6 +1316,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Scud Race (Japan)
 	{
 		"scudj",
+		"scud",
 		"Scud Race (Japan)",
 		"Sega",
 		1996,
@@ -1373,6 +1392,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Scud Race Plus (Revision A)
 	{
 		"scudp",
+		"scud",
 		"Scud Race Plus",
 		"Sega",
 		1997,
@@ -1454,6 +1474,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Ski Champ
 	{
 		"skichamp",
+		NULL,
 		"Ski Champ",
 		"Sega",
 		1998,
@@ -1527,6 +1548,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Spikeout Final Edition (disabled because this is a bad dump according to MAME)
 	{
 		"spikeofe",
+		NULL,
 		"Spikeout Final Edition",
 		"Sega",
 		1999,
@@ -1607,6 +1629,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Spikeout (Revision C)
 	{
 		"spikeout",
+		NULL,
 		"Spikeout",
 		"Sega",
 		1998,
@@ -1687,6 +1710,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Sega Rally 2
 	{
 		"srally2",
+		NULL,
 		"Sega Rally 2",
 		"Sega",
 		1998,
@@ -1762,6 +1786,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Sega Rally 2 DX
 	{
 		"srally2x",
+		NULL,
 		"Sega Rally 2 DX",
 		"Sega",
 		1998,
@@ -1832,6 +1857,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Star Wars Trilogy (Revision A)
 	{
 		"swtrilgy",
+		NULL,
 		"Star Wars Trilogy (Revision A)",
 		"Sega, LucasArts",
 		1998,
@@ -1904,6 +1930,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Star Wars Trilogy
 	{
 		"swtrilgya",
+		"swtrilgy",
 		"Star Wars Trilogy",
 		"Sega, LucasArts",
 		1998,
@@ -1976,6 +2003,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtua Fighter 3 (Revision C)
 	{
 		"vf3",
+		NULL,
 		"Virtua Fighter 3",
 		"Sega",
 		1996,
@@ -2049,6 +2077,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtua Fighter 3 (Revision A)
 	{
 		"vf3a",
+		"vf3",
 		"Virtua Fighter 3 (Revision A)",
 		"Sega",
 		1996,
@@ -2122,6 +2151,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtua Fighter 3 Team Battle
 	{
 		"vf3tb",
+		"vf3",
 		"Virtua Fighter 3 Team Battle",
 		"Sega",
 		1996,
@@ -2195,6 +2225,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtual On: Oratorio Tangram (Revision B)
 	{
 		"von2",
+		NULL,
 		"Virtual On: Oratorio Tangram",
 		"Sega",
 		1998,
@@ -2270,6 +2301,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtual On: Oratorio Tangram (Version 5.4g)
 	{
 		"von254g",
+		"von2",
 		"Virtual On: Oratorio Tangram (Version 5.4g)",
 		"Sega",
 		1998,
@@ -2345,6 +2377,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtua Striker 2 (Step 2.0)
 	{
 		"vs2",
+		NULL,
 		"Virtua Striker 2 (Step 2.0)",
 		"Sega",
 		1997,
@@ -2418,6 +2451,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtua Striker 2 (Step 1.5)
 	{
 		"vs215",
+		"vs2",
 		"Virtua Striker 2 (Step 1.5)",
 		"Sega",
 		1997,
@@ -2491,6 +2525,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtua Striker 2 '98 (Step 2.0)
 	{
 		"vs298",
+		NULL,
 		"Virtua Striker 2 '98 (Step 2.0)",
 		"Sega",
 		1998,
@@ -2564,6 +2599,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtua Striker 2 '98 (Step 1.5)
 	{
 		"vs29815",
+		"vs298",
 		"Virtua Striker 2 '98 (Step 1.5)",
 		"Sega",
 		1998,
@@ -2637,6 +2673,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtua Striker 2 '99
 	{
 		"vs299",
+		"vs2v991",
 		"Virtua Striker 2 '99",
 		"Sega",
 		1999,
@@ -2710,6 +2747,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtua Striker 2 '99 (Revision A)
 	{
 		"vs299a",
+		"vs2v991",
 		"Virtua Striker 2 '99 (Revision A)",
 		"Sega",
 		1999,
@@ -2783,6 +2821,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtua Striker 2 '99 (Revision B)
 	{
 		"vs299b",
+		"vs2v991",
 		"Virtua Striker 2 '99 (Revision B)",
 		"Sega",
 		1999,
@@ -2856,6 +2895,7 @@ const struct GameInfo	g_Model3GameList[] =
 	// Virtua Striker 2 '99.1 (Revision B)
 	{
 		"vs2v991",
+		NULL,
 		"Virtua Striker 2 '99.1",
 		"Sega",
 		1999,
@@ -2931,6 +2971,7 @@ const struct GameInfo	g_Model3GameList[] =
 		"",
 		NULL,
 		NULL,
+		NULL,
 		0,
 		0,
 		0,
diff --git a/Src/Games.h b/Src/Games.h
index 5650c2c..db9469d 100644
--- a/Src/Games.h
+++ b/Src/Games.h
@@ -67,6 +67,7 @@ struct GameInfo
 {
 	// Game information
 	const char		id[10];			// 9-character game identifier (also serves as zip archive file name)
+	const char		*parent;		// parent game identifier
 	const char		*title;			// complete game title
 	const char		*mfgName;		// name of manufacturer
 	unsigned		year;			// year released (in decimal)
diff --git a/Src/ROMLoad.cpp b/Src/ROMLoad.cpp
index def9104..9e1dead 100644
--- a/Src/ROMLoad.cpp
+++ b/Src/ROMLoad.cpp
@@ -204,10 +204,11 @@ static bool LoadROM(UINT8 *buf, unsigned bufSize, const struct ROMMap *Map, cons
  */
 const struct GameInfo * LoadROMSetFromZIPFile(const struct ROMMap *Map, const struct GameInfo *GameList, const char *zipFile, bool loadAll)
 {
-	unzFile					zf;
+	unzFile					zf, zfp = NULL;
 	unz_file_info			fileInfo;
 	const struct GameInfo	*Game = NULL;
 	const struct GameInfo	*CurGame;	// this is the game to which the last ROM found is thought to belong
+	string					zipFileParent, zfpErr = "";
 	int						romIdx;		// index within Game->ROM
 	unsigned				romsFound[sizeof(Game->ROM)/sizeof(struct ROMInfo)], numROMs;
 	int						err;
@@ -263,6 +264,34 @@ const struct GameInfo * LoadROMSetFromZIPFile(const struct ROMMap *Map, const st
 		}
 	}
 	
+	if (CurGame->parent)
+	{
+		// Create parent zip file name
+		string path = "";
+		if (strstr(zipFile, "/"))
+		{
+			path = string(zipFile);
+			path = path.substr(0, path.find_last_of("/") + 1);
+		}
+		if (strstr(zipFile, "\\"))
+		{
+			path = string(zipFile);
+			path = path.substr(0, path.find_last_of("\\") + 1);
+		}
+		zipFileParent = path + CurGame->parent + ".zip";
+	
+		// Create error message
+		zfpErr = " or '" + string(zipFileParent) + "'";
+
+		// Try to open file
+		zfp = unzOpen(zipFileParent.c_str());
+		if (NULL == zfp)
+		{
+			ErrorLog("Parent ROM set '%s' is missing.", zipFileParent.c_str());
+			return NULL;
+		}
+	}
+
 	// Second pass: check if all ROM files for the identified game are present
 	err = unzGoToFirstFile(zf);
 	if (UNZ_OK != err)
@@ -285,7 +314,30 @@ const struct GameInfo * LoadROMSetFromZIPFile(const struct ROMMap *Map, const st
 		// If we have found a ROM for the correct game, mark its corresponding indicator
 		romsFound[romIdx] = 1;
 	}
-	
+	if (zfp)
+	{
+		err = unzGoToFirstFile(zfp);
+		if (UNZ_OK != err)
+		{
+			ErrorLog("Unable to read the contents of '%s' (code %X)", zipFileParent.c_str(), err);
+			return NULL;
+		}
+		for (; err != UNZ_END_OF_LIST_OF_FILE; err = unzGoToNextFile(zfp))
+		{
+			// Identify the file we're looking at
+			err = unzGetCurrentFileInfo(zfp, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
+			if (err != UNZ_OK)
+				continue;			
+			
+			// If it's not part of the game we've identified, skip it
+			if (OKAY != FindROMByCRCInGame(&CurGame, &romIdx, Game, fileInfo.crc))
+				continue;
+			
+			// If we have found a ROM for the correct game, mark its corresponding indicator
+			romsFound[romIdx] = 1;
+		}
+	}
+
 	// Compute how many ROM files this game has
 	for (numROMs = 0; Game->ROM[numROMs].region != NULL; numROMs++)
 		;
@@ -295,11 +347,12 @@ const struct GameInfo * LoadROMSetFromZIPFile(const struct ROMMap *Map, const st
 	for (i = 0; i < numROMs; i++)
 	{
 		if ((0 == romsFound[i]) && !Game->ROM[i].optional)	// if not found and also not optional
-			err |= (int) ErrorLog("'%s' (CRC=%08X) is missing from '%s'.", Game->ROM[i].fileName, Game->ROM[i].crc, zipFile);
+			err |= (int) ErrorLog("'%s' (CRC=%08X) is missing from '%s'%s.", Game->ROM[i].fileName, Game->ROM[i].crc, zipFile, zfp ? zfpErr.c_str() : "");
 	}
 	if (err != OKAY)
 	{
 		unzClose(zf);
+		if (zfp) unzClose(zfp);
 		return NULL;
 	}
 		
@@ -314,6 +367,7 @@ const struct GameInfo * LoadROMSetFromZIPFile(const struct ROMMap *Map, const st
 	if (NULL == buf)
 	{
 		unzClose(zf);
+		if (zfp) unzClose(zfp);
 		ErrorLog("Insufficient memory to load ROM files (%d bytes).", maxSize);
 		return NULL;
 	}
@@ -341,7 +395,31 @@ const struct GameInfo * LoadROMSetFromZIPFile(const struct ROMMap *Map, const st
 		if (OKAY == LoadROM(buf, maxSize, Map, &Game->ROM[romIdx], zf, zipFile, loadAll))
 			romsFound[romIdx] = 1;	// success! mark as loaded
 	}
-	
+	if (zfp)
+	{
+		err = unzGoToFirstFile(zfp);
+		if (UNZ_OK != err)
+		{
+			ErrorLog("Unable to read the contents of '%s' (code %X).", zipFileParent.c_str(), err);
+			err = FAIL;
+			goto Quit;
+		}
+		for (; err != UNZ_END_OF_LIST_OF_FILE; err = unzGoToNextFile(zfp))
+		{
+			err = unzGetCurrentFileInfo(zfp, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
+			if (err != UNZ_OK)
+				continue;			
+			
+			// If this ROM is not part of the game we're loading, skip it
+			if (OKAY != FindROMByCRCInGame(&CurGame, &romIdx, Game, fileInfo.crc))
+				continue;
+
+			// Load the ROM and mark that we did so successfully
+			if (OKAY == LoadROM(buf, maxSize, Map, &Game->ROM[romIdx], zfp, zipFileParent.c_str(), loadAll))
+				romsFound[romIdx] = 1;	// success! mark as loaded
+		}
+	}
+
 	// Ensure all ROMs were loaded
 	if (loadAll)
 	{
@@ -350,7 +428,7 @@ const struct GameInfo * LoadROMSetFromZIPFile(const struct ROMMap *Map, const st
 		for (i = 0; i < numROMs; i++)
 		{
 			if (!(romsFound[i] || Game->ROM[i].optional))	// if ROM not found and also not optional
-				err = ErrorLog("Could not load '%s' (CRC=%08X) from '%s'.", Game->ROM[i].fileName, Game->ROM[i].crc, zipFile);
+				err = ErrorLog("Could not load '%s' (CRC=%08X) from '%s'%s.", Game->ROM[i].fileName, Game->ROM[i].crc, zipFile, zfp ? zfpErr.c_str() : "");
 		}
 	}
 	else
diff --git a/VS2008/Build.Release.Debugger.bat b/VS2008/Build.Release.Debugger.bat
new file mode 100644
index 0000000..a889616
--- /dev/null
+++ b/VS2008/Build.Release.Debugger.bat
@@ -0,0 +1,2 @@
+SET CL=/DSUPERMODEL_DEBUGGER
+call Build.Release.bat %1
diff --git a/VS2008/Build.Release.bat b/VS2008/Build.Release.bat
new file mode 100644
index 0000000..4513c09
--- /dev/null
+++ b/VS2008/Build.Release.bat
@@ -0,0 +1,4 @@
+call Build.bat Release x64 Supermodel %1
+call Build.bat Release Win32 Supermodel %1
+@echo All builds succeeded
+pause
diff --git a/VS2008/Rebuild.Release.Debugger.bat b/VS2008/Rebuild.Release.Debugger.bat
new file mode 100644
index 0000000..8cb54b5
--- /dev/null
+++ b/VS2008/Rebuild.Release.Debugger.bat
@@ -0,0 +1 @@
+call Build.Release.Debugger.bat Rebuild
diff --git a/VS2008/Rebuild.Release.bat b/VS2008/Rebuild.Release.bat
new file mode 100644
index 0000000..61865d2
--- /dev/null
+++ b/VS2008/Rebuild.Release.bat
@@ -0,0 +1 @@
+call Build.Release.bat Rebuild