shithub: cstory

Download patch

ref: 32a879ca58c048a3d09380d6d74d242ffe7bffd2
parent: 63dd995343579ef5663e5f4599dbac639b8fce08
author: Gabriel Ravier <gabravier@gmail.com>
date: Sun Jun 28 20:14:30 EDT 2020

src: Removed MAX_PATH and made the path/string handling better in general (ported over from supportPathsAboveFilenameMax)

Signed-off-by: Gabriel Ravier <gabravier@gmail.com>

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -171,6 +171,14 @@
 	"src/Backends/Controller.h"
 	"src/Backends/Misc.h"
 	"src/Backends/Rendering.h"
+	"src/Helpers/Asprintf.cpp"
+	"src/Helpers/Asprintf.h"
+	"src/Helpers/Vasprintf.cpp"
+	"src/Helpers/Vasprintf.h"
+	"src/Helpers/FopenFormatted.cpp"
+	"src/Helpers/FopenFormatted.h"
+	"src/Helpers/Strdup.cpp"
+	"src/Helpers/Strdup.h"
 )
 
 set(RESOURCES
--- a/src/ArmsItem.cpp
+++ b/src/ArmsItem.cpp
@@ -1,5 +1,6 @@
 #include "ArmsItem.h"
 
+#include <stdlib.h>
 #include <string.h>
 
 #include "WindowsWrapper.h"
@@ -414,12 +415,13 @@
 
 int CampLoop(void)
 {
-	char old_script_path[MAX_PATH];
-
 	RECT rcView = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
+	int ret = enum_ESCRETURN_continue;
 
 	// Save the current script path (to restore it when we get out of the inventory)
-	GetTextScriptPath(old_script_path);
+	char *old_script_path = GetTextScriptPath();
+	if (old_script_path == NULL)
+		return enum_ESCRETURN_exit;
 
 	// Load the inventory script
 	LoadTextScript2("ArmsItem.tsc");
@@ -450,10 +452,12 @@
 			switch (Call_Escape())
 			{
 				case enum_ESCRETURN_exit:
-					return enum_ESCRETURN_exit;	// Quit game
+					free(old_script_path);
+					return enum_ESCRETURN_exit;
 
 				case enum_ESCRETURN_restart:
-					return enum_ESCRETURN_restart;	// Go to game intro
+					free(old_script_path);
+					return enum_ESCRETURN_restart;
 			}
 		}
 
@@ -463,10 +467,12 @@
 		switch (TextScriptProc())
 		{
 			case enum_ESCRETURN_exit:
-				return enum_ESCRETURN_exit;	// Quit game
+				free(old_script_path);
+				return enum_ESCRETURN_exit;
 
 			case enum_ESCRETURN_restart:
-				return enum_ESCRETURN_restart;	// Go to game intro
+				free(old_script_path);
+				return enum_ESCRETURN_restart;
 		}
 
 		// Get currently displayed image
@@ -494,13 +500,17 @@
 		}
 
 		if (!Flip_SystemTask())
-			return enum_ESCRETURN_exit;	// Quit game
+		{
+			free(old_script_path);
+			return enum_ESCRETURN_exit;
+		}
 	}
 
 	// Resume original script
 	LoadTextScript_Stage(old_script_path);
 	gArmsEnergyX = 32; // Displays weapon rotation animation in case the weapon was changed
-	return enum_ESCRETURN_continue;	// Go to game
+	free(old_script_path);
+	return enum_ESCRETURN_continue;
 }
 
 BOOL CheckItem(long a)
--- a/src/Attributes.h
+++ b/src/Attributes.h
@@ -12,8 +12,11 @@
 
 #ifdef __GNUC__
 
+#define ATTRIBUTE_FORMAT(archetype, formatArgumentIndex, firstToCheck) __attribute__((format(archetype, formatArgumentIndex, firstToCheck)))
+#define ATTRIBUTE_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#define ATTRIBUTE_MALLOC __attribute__((malloc))
+#define ATTRIBUTE_NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
 #define ATTRIBUTE_HOT __attribute__((hot))
-#define ATTRIBUTE_OPTIMIZE(optString) __attribute__((optimize(optString)))
 #define LIKELY(condition) __builtin_expect((condition), 1)
 #define UNLIKELY(condition) __builtin_expect((condition), 0)
 #define PREFETCH(address, isWrite, locality) __builtin_prefetch((address), (isWrite), (locality))
@@ -20,8 +23,11 @@
 
 #else
 
+#define ATTRIBUTE_FORMAT(archetype, stringIndex, firstToCheck)
+#define ATTRIBUTE_WARN_UNUSED_RESULT
+#define ATTRIBUTE_MALLOC
+#define ATTRIBUTE_NONNULL
 #define ATTRIBUTE_HOT
-#define ATTRIBUTE_OPTIMIZE(optString)
 #define LIKELY(condition) condition
 #define UNLIKELY(condition) condition
 #define PREFETCH(address, isWrite, locality)
--- a/src/Back.cpp
+++ b/src/Back.cpp
@@ -13,6 +13,7 @@
 #include "Main.h"
 #include "Map.h"
 #include "Stage.h"
+#include "Helpers/FopenFormatted.h"
 
 BACK gBack;
 int gWaterY;
@@ -25,10 +26,7 @@
 	color_black = GetCortBoxColor(RGB(0, 0, 0x10));
 
 	// Get width and height
-	char path[MAX_PATH];
-	sprintf(path, "%s/%s.pbm", gDataPath, fName);
-
-	FILE *fp = fopen(path, "rb");
+	FILE *fp = fopenFormatted("rb", "%s/%s.pbm", gDataPath, fName);
 	if (fp == NULL)
 		return FALSE;
 
--- a/src/Backends/Misc.h
+++ b/src/Backends/Misc.h
@@ -86,7 +86,7 @@
 bool Backend_Init(void);
 void Backend_Deinit(void);
 void Backend_PostWindowCreation(void);
-bool Backend_GetBasePath(char *string_buffer);
+bool Backend_GetBasePath(char **string_buffer);
 void Backend_HideMouse(void);
 void Backend_SetWindowIcon(const unsigned char *rgb_pixels, unsigned int width, unsigned int height);
 void Backend_SetCursor(const unsigned char *rgb_pixels, unsigned int width, unsigned int height);
--- a/src/Backends/Platform/GLFW3.cpp
+++ b/src/Backends/Platform/GLFW3.cpp
@@ -181,7 +181,7 @@
 	glfwSetWindowSizeCallback(window, WindowSizeCallback);
 }
 
-bool Backend_GetBasePath(char *string_buffer)
+bool Backend_GetBasePath(char **string_buffer)
 {
 	(void)string_buffer;
 
--- a/src/Backends/Platform/Null.cpp
+++ b/src/Backends/Platform/Null.cpp
@@ -15,7 +15,7 @@
 	
 }
 
-bool Backend_GetBasePath(char *string_buffer)
+bool Backend_GetBasePath(char **string_buffer)
 {
 	(void)string_buffer;
 
--- a/src/Backends/Platform/SDL2.cpp
+++ b/src/Backends/Platform/SDL2.cpp
@@ -13,6 +13,7 @@
 #include "../../Organya.h"
 #include "../../Profile.h"
 #include "../../Resource.h"
+#include "../../Helpers/Strdup.h"
 
 #define DO_KEY(SDL_KEY, BACKEND_KEY) \
 	case SDL_KEY: \
@@ -84,7 +85,7 @@
 	
 }
 
-bool Backend_GetBasePath(char *string_buffer)
+bool Backend_GetBasePath(char **string_buffer)
 {
 #ifdef _WIN32
 	// SDL_GetBasePath returns a UTF-8 string, but Windows' fopen uses (extended?) ASCII.
@@ -100,7 +101,7 @@
 	// Trim the trailing '/'
 	size_t base_path_length = strlen(base_path);
 	base_path[base_path_length - 1] = '\0';
-	strcpy(string_buffer, base_path);
+	*string_buffer = strdup(base_path);
 	SDL_free(base_path);
 
 	return true;
--- a/src/Backends/Platform/WiiU.cpp
+++ b/src/Backends/Platform/WiiU.cpp
@@ -12,6 +12,8 @@
 #include <whb/proc.h>
 #include <whb/sdcard.h>
 
+#include "../../Helpers/Asprintf.h"
+
 static unsigned long ticks_per_second;
 
 bool Backend_Init(void)
@@ -54,15 +56,13 @@
 	
 }
 
-bool Backend_GetBasePath(char *string_buffer)
+bool Backend_GetBasePath(char **string_buffer)
 {
-	strcpy(string_buffer, WHBGetSdCardMountPath());
 #ifdef JAPANESE
-	strcat(string_buffer, "/CSE2-portable-jp");
+	*string_buffer = asprintf("%s/CSE-portable-jp", WHBGetSdCardMountPath());
 #else
-	strcat(string_buffer, "/CSE2-portable-en");
+	*string_buffer = asprintf("%s/CSE-portable-en", WHBGetSdCardMountPath());
 #endif
-
 	return true;
 }
 
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -7,6 +7,7 @@
 #include "Config.h"
 #include "File.h"
 #include "Main.h"
+#include "Helpers/FopenFormatted.h"
 
 const char* const gConfigName = "Config.dat";
 const char* const gProof = "DOUKUTSU20041206";
@@ -16,12 +17,8 @@
 	// Clear old configuration data
 	memset(conf, 0, sizeof(CONFIG));
 
-	// Get path
-	char path[MAX_PATH];
-	sprintf(path, "%s/%s", gModulePath, gConfigName);
-
 	// Open file
-	FILE *fp = fopen(path, "rb");
+	FILE *fp = fopenFormatted("rb", "%s/%s", gModulePath, gConfigName);
 	if (fp == NULL)
 		return FALSE;
 
--- a/src/Draw.cpp
+++ b/src/Draw.cpp
@@ -18,6 +18,7 @@
 #include "MapName.h"
 #include "Resource.h"
 #include "TextScr.h"
+#include "Helpers/Asprintf.h"
 
 typedef enum SurfaceType
 {
@@ -250,12 +251,15 @@
 // TODO - Inaccurate stack frame
 BOOL MakeSurface_File(const char *name, SurfaceID surf_no)
 {
-	char path[MAX_PATH];
-	sprintf(path, "%s/%s.pbm", gDataPath, name);
+	char *path;
 
+	if (asprintf(&path, "%s/%s.pbm", gDataPath, name) < 0)
+		return FALSE;
+
 	if (!IsEnableBitmap(path))
 	{
 		ErrorLog(path, 0);
+		free(path);
 		return FALSE;
 	}
 
@@ -266,6 +270,7 @@
 #endif
 	{
 		ErrorLog("surface no", surf_no);
+		free(path);
 		return FALSE;
 	}
 
@@ -272,6 +277,7 @@
 	if (surf[surf_no] != NULL)
 	{
 		ErrorLog("existing", surf_no);
+		free(path);
 		return FALSE;
 	}
 
@@ -281,8 +287,10 @@
 	if (image_buffer == NULL)
 	{
 		ErrorLog(path, 1);
+		free(path);
 		return FALSE;
 	}
+	free(path);
 
 	surf[surf_no] = RenderBackend_CreateSurface(width * mag, height * mag, false);
 
@@ -341,12 +349,14 @@
 // TODO - Inaccurate stack frame
 BOOL ReloadBitmap_File(const char *name, SurfaceID surf_no)
 {
-	char path[MAX_PATH];
-	sprintf(path, "%s/%s.pbm", gDataPath, name);
+	char *path;
+	if (asprintf(&path, "%s/%s.pbm", gDataPath, name) < 0)
+		return FALSE;
 
 	if (!IsEnableBitmap(path))
 	{
 		ErrorLog(path, 0);
+		free(path);
 		return FALSE;
 	}
 
@@ -357,6 +367,7 @@
 #endif
 	{
 		ErrorLog("surface no", surf_no);
+		free(path);
 		return FALSE;
 	}
 
@@ -366,8 +377,10 @@
 	if (image_buffer == NULL)
 	{
 		ErrorLog(path, 1);
+		free(path);
 		return FALSE;
 	}
+	free(path);
 
 	if (!ScaleAndUploadSurface(image_buffer, width, height, surf_no))
 	{
@@ -378,7 +391,6 @@
 	FreeBitmap(image_buffer);
 	surface_metadata[surf_no].type = SURFACE_SOURCE_FILE;
 	strcpy(surface_metadata[surf_no].name, name);
-
 	return TRUE;
 }
 
@@ -647,8 +659,9 @@
 {
 	(void)name;	// Unused in this branch
 
-	char path[MAX_PATH];
-	sprintf(path, "%s/Font/font", gDataPath);
+	char *path;
+	if (asprintf(&path, "%s/Font/font", gDataPath) < 0)
+		return;
 
 	// Get font size
 	unsigned int width, height;
--- a/src/Ending.cpp
+++ b/src/Ending.cpp
@@ -16,6 +16,7 @@
 #include "Organya.h"
 #include "Stage.h"
 #include "TextScr.h"
+#include "Helpers/Asprintf.h"
 
 struct CREDIT
 {
@@ -216,7 +217,6 @@
 BOOL StartCreditScript(void)
 {
 	FILE *fp;
-	char path[MAX_PATH];
 
 	// Clear previously existing credits data
 	if (Credit.pData != NULL)
@@ -226,18 +226,27 @@
 	}
 
 	// Open file
-	sprintf(path, "%s/%s", gDataPath, credit_script);
+	char *path;
+	if (asprintf(&path, "%s/%s", gDataPath, credit_script) < 0)
+		return FALSE;
 
 	Credit.size = GetFileSizeLong(path);
 	if (Credit.size == -1)
+	{
+		free(path);
 		return FALSE;
+	}
 
 	// Allocate buffer data
 	Credit.pData = (char*)malloc(Credit.size);
 	if (Credit.pData == NULL)
+	{
+		free(path);
 		return FALSE;
+	}
 
 	fp = fopen(path, "rb");
+	free(path);
 	if (fp == NULL)
 	{
 		free(Credit.pData);
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -44,6 +44,7 @@
 #include "Star.h"
 #include "TextScr.h"
 #include "ValueView.h"
+#include "Helpers/Asprintf.h"
 
 int g_GameFlags;
 int gCounter;
@@ -701,10 +702,13 @@
 
 	PlaySoundObject(7, -1);
 
-	char path[MAX_PATH];
-	sprintf(path, "%s/npc.tbl", gDataPath);
+	char *path;
+	if (asprintf(&path, "%s/npc.tbl", gDataPath) < 0)
+		return FALSE;
 
-	if (!LoadNpcTable(path))
+	BOOL load_npc_table_succeeded = LoadNpcTable(path);
+	free(path);
+	if (!load_npc_table_succeeded)
 	{
 #ifdef JAPANESE
 		Backend_ShowMessageBox("エラー", "NPCテーブルが読めない");
--- a/src/Generic.cpp
+++ b/src/Generic.cpp
@@ -2,11 +2,14 @@
 
 #include <stddef.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include "WindowsWrapper.h"
 
 #include "Main.h"
+#include "Helpers/Asprintf.h"
+#include "Helpers/FopenFormatted.h"
 
 void GetCompileDate(int *year, int *month, int *day)
 {
@@ -49,20 +52,19 @@
 
 void DeleteLog(void)
 {
-	char path[MAX_PATH];
+	char *path;
 
-	sprintf(path, "%s/debug.txt", gModulePath);
+	if (asprintf(&path, "%s/debug.txt", gModulePath) < 0)
+		return;
+
 	remove(path);
+	free(path);
 }
 
 BOOL WriteLog(const char *string, int value1, int value2, int value3)
 {
-	char path[MAX_PATH];
-	FILE *fp;
+	FILE *fp = fopenFormatted("a+", "%s/debug.txt", gModulePath);
 
-	sprintf(path, "%s/debug.txt", gModulePath);
-	fp = fopen(path, "a+");
-
 	if (fp == NULL)
 		return FALSE;
 
@@ -73,12 +75,8 @@
 
 BOOL IsKeyFile(const char *name)
 {
-	char path[MAX_PATH];
+	FILE *file = fopenFormatted("rb", "%s/%s", gModulePath, name);
 
-	sprintf(path, "%s/%s", gModulePath, name);
-
-	FILE *file = fopen(path, "rb");
-
 	if (file == NULL)
 		return FALSE;
 
@@ -86,6 +84,7 @@
 	return TRUE;
 }
 
+// Some code uses this as CheckFileExists, checking if the return value is -1
 long GetFileSizeLong(const char *path)
 {
 	long len;
@@ -105,15 +104,15 @@
 
 BOOL ErrorLog(const char *string, int value)
 {
-	char path[MAX_PATH];
-	FILE *fp;
+	char *path;
+	if (asprintf(&path, "%s/%s", gModulePath, "error.log") < 0)
+		return FALSE;
 
-	sprintf(path, "%s/%s", gModulePath, "error.log");
-
 	if (GetFileSizeLong(path) > 0x19000)	// Purge the error log if it gets too big, I guess
 		remove(path);
 
-	fp = fopen(path, "a+");
+	FILE *fp = fopen(path, "a+");
+	free(path);
 	if (fp == NULL)
 		return FALSE;
 
--- a/src/GenericLoad.cpp
+++ b/src/GenericLoad.cpp
@@ -293,8 +293,10 @@
 	pt_size += MakePixToneObject(&gPtpTable[137], 1, 6);
 	pt_size += MakePixToneObject(&gPtpTable[138], 1, 7);
 
-	char str[0x40];
-	sprintf(str, "PixTone = %d byte", pt_size);
-	// There must have been some kind of console print function here or something
+	/*
+	 * 	char str[0x40];
+	 *	sprintf(str, "PixTone = %d byte", pt_size);
+	 *  // There must have been some kind of console print function here or something
+	 */
 	return TRUE;
 }
--- /dev/null
+++ b/src/Helpers/Asprintf.cpp
@@ -1,0 +1,18 @@
+#ifndef _GNU_SOURCE
+
+#include "Asprintf.h"
+#include "Vasprintf.h"
+#include "../Attributes.h"
+#include <stdarg.h>
+
+ATTRIBUTE_FORMAT(printf, 2, 3) ATTRIBUTE_WARN_UNUSED_RESULT int asprintf(char **result_string, const char *format_string, ...)
+{
+	// Handle variadic arguments and redirect to vasprintf
+	va_list arguments_local;
+	va_start(arguments_local, format_string);
+	int retVal = vasprintf(result_string, format_string, arguments_local);
+	va_end(arguments_local);	// Destroy arguments_local
+	return retVal;
+}
+
+#endif
--- /dev/null
+++ b/src/Helpers/Asprintf.h
@@ -1,0 +1,15 @@
+#pragma once
+
+// If <stdio.h> defines asprintf for us, use its definition
+#ifdef _GNU_SOURCE
+
+#include <stdio.h>
+
+#else
+
+#include "../Attributes.h"
+#define asprintf Portable_asprintf
+
+ATTRIBUTE_FORMAT(printf, 2, 3) ATTRIBUTE_WARN_UNUSED_RESULT int asprintf(char **resultString, const char *formatString, ...);
+
+#endif
--- /dev/null
+++ b/src/Helpers/FopenFormatted.cpp
@@ -1,0 +1,23 @@
+#include "FopenFormatted.h"
+#include "../Attributes.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+ATTRIBUTE_FORMAT(printf, 2, 3) ATTRIBUTE_WARN_UNUSED_RESULT FILE *fopenFormatted(const char *mode, const char *format_string, ...)
+{
+	// Handle variadic arguments and redirect to vasprintf
+	va_list arguments_local;
+	va_start(arguments_local, format_string);
+	char *path;
+	int vasprintf_retval = vasprintf(&path, format_string, arguments_local);
+	va_end(arguments_local);	// Destroy arguments_local
+
+	if (vasprintf_retval < 0)
+		return NULL;
+
+	FILE *file = fopen(path, mode);
+	free(path);
+
+	return file;
+}
--- /dev/null
+++ b/src/Helpers/FopenFormatted.h
@@ -1,0 +1,5 @@
+#pragma once
+#include "../Attributes.h"
+#include <stdio.h>
+
+ATTRIBUTE_FORMAT(printf, 2, 3) ATTRIBUTE_WARN_UNUSED_RESULT FILE *fopenFormatted(const char *mode, const char *formatString, ...);
--- /dev/null
+++ b/src/Helpers/Strdup.cpp
@@ -1,0 +1,20 @@
+#include <assert.h>
+#if !(_XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L)
+
+#include "Strdup.h"
+#include "../Attributes.h"
+#include <string.h>
+#include <stdlib.h>
+
+ATTRIBUTE_MALLOC ATTRIBUTE_NONNULL((1)) char *strdup(const char *duplicated_string)
+{
+	size_t duplicatedStringLength = strlen(duplicated_string) + sizeof(char);	// Length of the whole string, plus the terminator
+	char *returnedString = (char *)malloc(duplicatedStringLength);	// Memory for the new string is obtained with malloc. malloc also sets errno to ENOMEM on failure, which is exactly what we want for conformance
+
+	if (returnedString)	// If the result of malloc was NULL, allocation failed copying into it would be UB
+		memcpy(returnedString, duplicated_string, duplicatedStringLength);	// Copy the entire duplicated string (including the null terminator)
+
+	return returnedString;	// Returns a pointer to the duplicated string on success, NULL if insufficient memory was available
+}
+
+#endif
--- /dev/null
+++ b/src/Helpers/Strdup.h
@@ -1,0 +1,16 @@
+#pragma once
+#include <assert.h>	// Probably the smallest header that will include <features.h> on systems with it
+
+// If <string.h> defines strdup for us, use its definition
+#if (_XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L)
+
+#include <string.h>
+
+#else
+
+#include "../Attributes.h"
+#define strdup Portable_strdup
+
+ATTRIBUTE_MALLOC ATTRIBUTE_NONNULL(1) char *strdup(const char *duplicatedString);
+
+#endif
--- /dev/null
+++ b/src/Helpers/Vasprintf.cpp
@@ -1,0 +1,32 @@
+#ifndef _GNU_SOURCE
+
+#include "Vasprintf.h"
+#include "../Attributes.h"
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+ATTRIBUTE_FORMAT(printf, 2, 0) ATTRIBUTE_WARN_UNUSED_RESULT int vasprintf(char **result_string, const char *format_string, va_list arguments)
+{
+	const int error_return_value = -1;	// According to the man page, this is returned on error
+	va_list arguments_local;
+	va_copy(arguments_local, arguments);	// Copy the arguments so we can actually use them
+
+	int size = vsnprintf(NULL, 0, format_string, arguments_local);	// Get the amount of bytes we need for the output buffer
+
+	va_end(arguments_local);	// Destroy arguments_local
+
+	if (size < 0)	// If an output error is encountered, vsnprintf returns a negative value
+		return error_return_value;
+
+	*result_string = (char *)malloc(size + 1);	// Allocate enough space for the string (need + 1 for the null terminator)
+
+	if (*result_string == NULL)	// On error, malloc returns NULL
+		return error_return_value;
+
+	// We know we have enough space, so we can use vsprintf safely
+	return vsprintf(*result_string, format_string, arguments);	// vsprintf returns the amount of characters that got printed. This is what we're supposed to return
+}
+
+#endif
--- /dev/null
+++ b/src/Helpers/Vasprintf.h
@@ -1,0 +1,16 @@
+#pragma once
+
+// If <stdio.h> defines asprintf for us, use its definition
+#ifdef _GNU_SOURCE
+
+#include <stdio.h>
+
+#else
+
+#include "../Attributes.h"
+#include <stdarg.h>
+#define vasprintf Portable_vasprintf
+
+ATTRIBUTE_FORMAT(printf, 2, 0) ATTRIBUTE_WARN_UNUSED_RESULT int vasprintf(char **resultString, const char *formatString, va_list arguments);
+
+#endif
--- a/src/Main.cpp
+++ b/src/Main.cpp
@@ -24,9 +24,11 @@
 #include "Resource.h"
 #include "Sound.h"
 #include "Triangle.h"
+#include "Helpers/Asprintf.h"
+#include "Helpers/Strdup.h"
 
-char gModulePath[MAX_PATH];
-char gDataPath[MAX_PATH];
+char *gModulePath;
+char *gDataPath;
 
 BOOL bFullscreen;
 BOOL gbUseJoystick = FALSE;
@@ -82,6 +84,24 @@
 	}
 }
 
+static int main_out_backend_deinit(int return_value)
+{
+	Backend_Deinit();
+	return return_value;
+}
+
+static int main_out_free_module_path(int return_value)
+{
+	free(gModulePath);
+	return main_out_backend_deinit(return_value);
+}
+
+static int main_out_free_data_path(int return_value)
+{
+	free(gDataPath);
+	return main_out_free_module_path(return_value);
+}
+
 // TODO - Inaccurate stack frame
 int main(int argc, char *argv[])
 {
@@ -93,10 +113,15 @@
 		return EXIT_FAILURE;
 
 	// Get executable's path
-	if (!Backend_GetBasePath(gModulePath))
+	if (!Backend_GetBasePath(&gModulePath))
 	{
 		// Fall back on argv[0] if the backend cannot provide a path
-		strcpy(gModulePath, argv[0]);
+		gModulePath = strdup(argv[0]);
+		if (!gModulePath)
+		{
+			Backend_PrintError("Could not get executable path (not enough memory)");
+			return main_out_backend_deinit(EXIT_FAILURE);
+		}
 
 		for (size_t i = strlen(gModulePath);; --i)
 		{
@@ -109,8 +134,8 @@
 	}
 
 	// Get path of the data folder
-	strcpy(gDataPath, gModulePath);
-	strcat(gDataPath, "/data");
+	if (asprintf(&gDataPath, "%s/data", gModulePath) < 0)
+		return main_out_free_module_path(EXIT_FAILURE);
 
 	CONFIG conf;
 	if (!LoadConfigData(&conf))
@@ -223,18 +248,12 @@
 			if (conf.display_mode == 1)
 			{
 				if (!StartDirectDraw(lpWindowName, windowWidth, windowHeight, 0))
-				{
-					Backend_Deinit();
-					return EXIT_FAILURE;
-				}
+					return main_out_free_data_path(EXIT_FAILURE);
 			}
 			else
 			{
 				if (!StartDirectDraw(lpWindowName, windowWidth, windowHeight, 1))
-				{
-					Backend_Deinit();
-					return EXIT_FAILURE;
-				}
+					return main_out_free_data_path(EXIT_FAILURE);
 			}
 		#else
 			// Doesn't handle StartDirectDraw failing
@@ -255,10 +274,7 @@
 
 		#ifdef FIX_BUGS
 			if (!StartDirectDraw(lpWindowName, windowWidth, windowHeight, 2))
-			{
-				Backend_Deinit();
-				return EXIT_FAILURE;
-			}
+				return main_out_free_data_path(EXIT_FAILURE);
 		#else
 			// Doesn't handle StartDirectDraw failing
 			StartDirectDraw(lpWindowName, windowWidth, windowHeight, 2);
@@ -326,10 +342,7 @@
 
 	// Draw to screen
 	if (!Flip_SystemTask())
-	{
-		Backend_Deinit();
-		return EXIT_SUCCESS;
-	}
+		return main_out_free_data_path(EXIT_SUCCESS);
 
 	// Initialize sound
 	InitDirectSound();
@@ -353,9 +366,7 @@
 	EndDirectSound();
 	EndDirectDraw();
 
-	Backend_Deinit();
-
-	return EXIT_SUCCESS;
+	return main_out_free_data_path(EXIT_SUCCESS);
 }
 
 void InactiveWindow(void)
--- a/src/Main.h
+++ b/src/Main.h
@@ -2,8 +2,8 @@
 
 #include "WindowsWrapper.h"
 
-extern char gModulePath[MAX_PATH];
-extern char gDataPath[MAX_PATH];
+extern char *gModulePath;
+extern char *gDataPath;
 
 extern BOOL bFullscreen;
 extern BOOL gbUseJoystick;
--- a/src/Map.cpp
+++ b/src/Map.cpp
@@ -12,6 +12,7 @@
 #include "File.h"
 #include "Main.h"
 #include "NpChar.h"
+#include "Helpers/FopenFormatted.h"
 
 #define PXM_BUFFER_SIZE 0x4B000
 
@@ -29,13 +30,9 @@
 {
 	FILE *fp;
 	char check[3];
-	char path[MAX_PATH];
 
-	// Get path
-	sprintf(path, "%s/%s", gDataPath, path_map);
-
 	// Open file
-	fp = fopen(path, "rb");
+	fp = fopenFormatted("rb", "%s/%s", gDataPath, path_map);
 	if (fp == NULL)
 		return FALSE;
 
@@ -69,12 +66,9 @@
 BOOL LoadAttributeData(const char *path_atrb)
 {
 	FILE *fp;
-	char path[MAX_PATH];
 
 	// Open file
-	sprintf(path, "%s/%s", gDataPath, path_atrb);
-
-	fp = fopen(path, "rb");
+	fp = fopenFormatted("rb", "%s/%s", gDataPath, path_atrb);
 	if (fp == NULL)
 		return FALSE;
 
--- a/src/MycParam.cpp
+++ b/src/MycParam.cpp
@@ -16,6 +16,8 @@
 #include "Sound.h"
 #include "TextScr.h"
 #include "ValueView.h"
+#include "Helpers/Asprintf.h"
+#include "Helpers/FopenFormatted.h"
 
 ARMS_LEVEL gArmsLevelTable[14] =
 {
@@ -436,7 +438,7 @@
 	unsigned char p[4];
 	REC rec;
 	FILE *fp;
-	char path[MAX_PATH];
+	char *path;
 
 	// Quit if player doesn't have the Nikumaru Counter
 	if (!(gMC.equip & EQUIP_NIKUMARU_COUNTER))
@@ -443,7 +445,8 @@
 		return TRUE;
 
 	// Get last time
-	sprintf(path, "%s/290.rec", gModulePath);
+	if (asprintf(&path, "%s/290.rec", gModulePath) < 0)
+		return FALSE;
 
 	fp = fopen(path, "rb");
 	if (fp != NULL)
@@ -508,12 +511,9 @@
 	unsigned char p[4];
 	REC rec;
 	FILE *fp;
-	char path[MAX_PATH];
 
 	// Open file
-	sprintf(path, "%s/290.rec", gModulePath);
-
-	fp = fopen(path, "rb");
+	fp = fopenFormatted("rb", "%s/290.rec", gModulePath);
 	if (fp == NULL)
 		return 0;
 
--- a/src/NpChar.cpp
+++ b/src/NpChar.cpp
@@ -17,6 +17,7 @@
 #include "NpcTbl.h"
 #include "Sound.h"
 #include "ValueView.h"
+#include "Helpers/FopenFormatted.h"
 
 NPCHAR gNPC[NPC_MAX];
 int gCurlyShoot_wait;
@@ -59,10 +60,7 @@
 	char code[4];
 	EVENT eve;
 
-	char path[MAX_PATH];
-	sprintf(path, "%s/%s", gDataPath, path_event);
-
-	fp = fopen(path, "rb");
+	fp = fopenFormatted("rb", "%s/%s", gDataPath, path_event);
 	if (fp == NULL)
 		return FALSE;
 
--- a/src/Profile.cpp
+++ b/src/Profile.cpp
@@ -22,6 +22,7 @@
 #include "Stage.h"
 #include "Star.h"
 #include "ValueView.h"
+#include "Helpers/FopenFormatted.h"
 
 const char* const gDefaultName = "Profile.dat";
 const char* const gProfileCode = "Do041220";
@@ -28,10 +29,7 @@
 
 BOOL IsProfile(void)
 {
-	char path[MAX_PATH];
-	sprintf(path, "%s/%s", gModulePath, gDefaultName);
-
-	FILE *file = fopen(path, "rb");
+	FILE *file = fopenFormatted("rb", "%s/%s", gModulePath, gDefaultName);
 	if (file == NULL)
 		return FALSE;
 
@@ -41,24 +39,15 @@
 
 BOOL SaveProfile(const char *name)
 {
-	FILE *fp;
-	PROFILE profile;
 	const char *FLAG = "FLAG";
 
-	char path[MAX_PATH];
-
-	// Get path
-	if (name != NULL)
-		sprintf(path, "%s/%s", gModulePath, name);
-	else
-		sprintf(path, "%s/%s", gModulePath, gDefaultName);
-
 	// Open file
-	fp = fopen(path, "wb");
+	FILE *fp = fopenFormatted("wb", "%s/%s", gModulePath, ((name != NULL) ? name : gDefaultName));
 	if (fp == NULL)
 		return FALSE;
 
 	// Set up profile
+	PROFILE profile;
 	memset(&profile, 0, sizeof(PROFILE));
 	memcpy(profile.code, gProfileCode, sizeof(profile.code));
 	memcpy(profile.FLAG, FLAG, sizeof(profile.FLAG));
@@ -124,16 +113,13 @@
 {
 	FILE *fp;
 	PROFILE profile;
-	char path[MAX_PATH];
 
-	// Get path
+	// Open file
 	if (name != NULL)
-		sprintf(path, "%s", name);
+		fp = fopenFormatted("rb", "%s", name);
 	else
-		sprintf(path, "%s/%s", gModulePath, gDefaultName);
+		fp = fopenFormatted("rb", "%s/%s", gModulePath, gDefaultName);
 
-	// Open file
-	fp = fopen(path, "rb");
 	if (fp == NULL)
 		return FALSE;
 
--- a/src/SelStage.cpp
+++ b/src/SelStage.cpp
@@ -1,5 +1,6 @@
 #include "SelStage.h"
 
+#include <stdlib.h>
 #include <string.h>
 
 #include "WindowsWrapper.h"
@@ -156,13 +157,13 @@
 
 int StageSelectLoop(int *p_event)
 {
-	char old_script_path[MAX_PATH];
-
 	RECT rcView = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
 
 	gSelectedStage = 0;
 	BackupSurface(SURFACE_ID_SCREEN_GRAB, &grcFull);
-	GetTextScriptPath(old_script_path);
+	char *old_script_path = GetTextScriptPath();
+	if (!old_script_path)
+		return enum_ESCRETURN_exit;
 	LoadTextScript2("StageSelect.tsc");
 	gStageSelectTitleY = (WINDOW_HEIGHT / 2) - 66;
 	StartTextScript(gPermitStage[gSelectedStage].index + 1000);
@@ -176,9 +177,11 @@
 			switch (Call_Escape())
 			{
 				case enum_ESCRETURN_exit:
+					free(old_script_path);
 					return enum_ESCRETURN_exit;
 
 				case enum_ESCRETURN_restart:
+					free(old_script_path);
 					return enum_ESCRETURN_restart;
 			}
 		}
@@ -188,9 +191,11 @@
 		switch (TextScriptProc())
 		{
 			case enum_ESCRETURN_exit:
+				free(old_script_path);
 				return enum_ESCRETURN_exit;
 
 			case enum_ESCRETURN_restart:
+				free(old_script_path);
 				return enum_ESCRETURN_restart;
 		}
 
@@ -212,6 +217,7 @@
 		{
 			StopTextScript();
 			LoadTextScript_Stage(old_script_path);
+			free(old_script_path);
 			*p_event = 0;
 			return enum_ESCRETURN_continue;
 		}
@@ -219,10 +225,14 @@
 		PutFramePerSecound();
 
 		if (!Flip_SystemTask())
+		{
+			free(old_script_path);
 			return enum_ESCRETURN_exit;
+		}
 	}
 
 	LoadTextScript_Stage(old_script_path);
+	free(old_script_path);
 	*p_event = gPermitStage[gSelectedStage].event;
 	return enum_ESCRETURN_continue;
 }
--- a/src/Stage.cpp
+++ b/src/Stage.cpp
@@ -131,8 +131,6 @@
 
 BOOL TransferStage(int no, int w, int x, int y)
 {
-	char path[MAX_PATH];
-	char path_dir[20];
 	BOOL bError;
 
 	// Move character
@@ -141,7 +139,15 @@
 	bError = FALSE;
 
 	// Get path
-	strcpy(path_dir, "Stage");
+	char path_dir[sizeof("Stage")] = "Stage";	// Can only be either "Stage" or "Npc", and sizeof("Stage") > sizeof("Npc")
+	/*
+	 *	The size of path should be size of the format string (without the operands) + size of first operand + size of second operand - 2 (no null terminator needed for the first two operands, and sizeof will include the size for the null terminators here)
+	 *	The size of the format string is sizeof("/.pxa") because that's the biggest raw format (omitting the operands) used here
+	 *	Size of first operand is sizeof(path_dir) because that's always the first operand
+	 *	Size of second operand is sizeof(gTMT[no].parts) because all the operands used here (gTMT[no].parts, gTMT[no].map, gTMT[no].npc, gTMT[no].boss) are of the same size
+	 *	sprintf(path, "%s", gTMT[no].back) is irrelevant here because sizeof(gTMT[no].back) is the same as the size of all the operands discussed for the size of the second operand, so gTMT[no].back always fits into the size allocated for the second operand alone
+	 */
+	char path[sizeof("/.pxa") + sizeof(path_dir) + sizeof(gTMT[no].parts) - 2];
 
 	// Load tileset
 	sprintf(path, "%s/Prt%s", path_dir, gTMT[no].parts);
--- a/src/TextScr.cpp
+++ b/src/TextScr.cpp
@@ -33,6 +33,8 @@
 #include "SelStage.h"
 #include "Sound.h"
 #include "Stage.h"
+#include "Helpers/Asprintf.h"
+#include "Helpers/Strdup.h"
 
 // This limits the size of a .tsc script to 0x5000 bytes (the game will crash above this)
 #define TSC_BUFFER_SIZE 0x5000
@@ -124,17 +126,22 @@
 BOOL LoadTextScript2(const char *name)
 {
 	FILE *fp;
-	char path[MAX_PATH];
+	char *path;
 
 	// Get path
-	sprintf(path, "%s/%s", gDataPath, name);
+	if (asprintf(&path, "%s/%s", gDataPath, name) < 0)
+		return FALSE;
 
 	gTS.size = GetFileSizeLong(path);
 	if (gTS.size == -1)
+	{
+		free(path);
 		return FALSE;
+	}
 
 	// Open file
 	fp = fopen(path, "rb");
+	free(path);
 	if (fp == NULL)
 		return FALSE;
 
@@ -144,7 +151,9 @@
 	fclose(fp);
 
 	// Set path
-	strcpy(gTS.path, name);
+	if (gTS.path != NULL)
+		free(gTS.path);
+	gTS.path = strdup(name);
 
 	// Decrypt data
 	EncryptionBinaryData2((unsigned char*)gTS.data, gTS.size);
@@ -156,18 +165,23 @@
 BOOL LoadTextScript_Stage(const char *name)
 {
 	FILE *fp;
-	char path[MAX_PATH];
 	long head_size;
 	long body_size;
 
 	// Open Head.tsc
-	sprintf(path, "%s/%s", gDataPath, "Head.tsc");
+	char *path;
+	if (asprintf(&path, "%s/%s", gDataPath, "Head.tsc") < 0)
+		return FALSE;
 
 	head_size = GetFileSizeLong(path);
 	if (head_size == -1)
+	{
+		free(path);
 		return FALSE;
+	}
 
 	fp = fopen(path, "rb");
+	free(path);
 	if (fp == NULL)
 		return FALSE;
 
@@ -178,13 +192,18 @@
 	fclose(fp);
 
 	// Open stage's .tsc
-	sprintf(path, "%s/%s", gDataPath, name);
+	if (asprintf(&path, "%s/%s", gDataPath, name) < 0)
+		return FALSE;
 
 	body_size = GetFileSizeLong(path);
 	if (body_size == -1)
+	{
+		free(path);
 		return FALSE;
+	}
 
 	fp = fopen(path, "rb");
+	free(path);
 	if (fp == NULL)
 		return FALSE;
 
@@ -196,15 +215,17 @@
 
 	// Set parameters
 	gTS.size = head_size + body_size;
-	strcpy(gTS.path, name);
+	if (gTS.path != NULL)
+		free(gTS.path);
+	gTS.path = strdup(name);
 
 	return TRUE;
 }
 
 // Get current path
-void GetTextScriptPath(char *path)
+char *GetTextScriptPath()
 {
-	strcpy(path, gTS.path);
+	return strdup(gTS.path);
 }
 
 // Get 4 digit number from TSC data
@@ -1281,11 +1302,13 @@
 					}
 					else
 					{
-						char str_0[0x40];
+						// The size of str_0 is the size of the format string (includes null terminator so that's the space we'll be using for ours) + the 3 chars from the format string
 						#ifdef JAPANESE
+						char str_0[sizeof("不明のコード:<") + (sizeof(char) * 3)];
 						sprintf(str_0, "不明のコード:<%c%c%c", gTS.data[gTS.p_read + 1], gTS.data[gTS.p_read + 2], gTS.data[gTS.p_read + 3]);
 						Backend_ShowMessageBox("エラー", str_0);
 						#else
+						char str_0[sizeof("Unknown code:<%c%c%c") + (sizeof(char) * 3)];
 						sprintf(str_0, "Unknown code:<%c%c%c", gTS.data[gTS.p_read + 1], gTS.data[gTS.p_read + 2], gTS.data[gTS.p_read + 3]);
 						Backend_ShowMessageBox("Error", str_0);
 						#endif
--- a/src/TextScr.h
+++ b/src/TextScr.h
@@ -5,7 +5,7 @@
 typedef struct TEXT_SCRIPT
 {
 	// Path (reload when exit teleporter menu/inventory)
-	char path[MAX_PATH];
+	char *path;
 
 	// Script buffer
 	long size;
@@ -62,7 +62,7 @@
 void EncryptionBinaryData2(unsigned char *pData, long size);
 BOOL LoadTextScript2(const char *name);
 BOOL LoadTextScript_Stage(const char *name);
-void GetTextScriptPath(char *path);
+char *GetTextScriptPath();
 BOOL StartTextScript(int no);
 void StopTextScript(void);
 void PutTextScript(void);
--- a/src/WindowsWrapper.h
+++ b/src/WindowsWrapper.h
@@ -4,10 +4,9 @@
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #undef FindResource
+#undef MAX_PATH	// Explicitly undefine MAX_PATH to avoid accidental usage of it
 #else
 
-#include <stdio.h>
-
 #define RGB(r,g,b) ((r) | ((g) << 8) | ((b) << 16))
 
 typedef bool BOOL;
@@ -22,7 +21,5 @@
 	long right;
 	long bottom;
 };
-
-#define MAX_PATH FILENAME_MAX
 
 #endif