diff --git a/CMakeLists.txt b/CMakeLists.txt
index 03b7875..9640369 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,10 @@
PROJECT(mst06_base)
CMAKE_MINIMUM_REQUIRED(VERSION 3.1)
+LIST(APPEND CMAKE_MODULE_PATH
+ "${CMAKE_SOURCE_DIR}/cmake/modules"
+ )
+
# If no build type is set, default to "Debug".
# TODO: Default to "Release"?
STRING(TOLOWER "${CMAKE_BUILD_TYPE}" TMP_BUILD_TYPE)
@@ -37,6 +41,31 @@ CONFIGURE_FILE(
ADD_CUSTOM_TARGET(uninstall
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake")
+# Windows-specific compile flags.
+IF(WIN32)
+ SET(WIN32_C_FLAGS_COMMON "-DSTRICT -DWIN32_LEAN_AND_MEAN -DNOMINMAX")
+
+ # NOTE: This program only supports Unicode on Windows.
+ # No support for ANSI Windows, i.e. Win9x.
+ SET(WIN32_C_FLAGS_COMMON "${WIN32_C_FLAGS_COMMON} -DUNICODE -D_UNICODE")
+
+ # Minimum Windows version for the SDK is Windows 2000.
+ SET(WIN32_C_FLAGS_COMMON "${WIN32_C_FLAGS_COMMON} -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0500")
+
+ # Enable secure template overloads for C++.
+ # References:
+ # - MinGW's _mingw_secapi.h
+ # - http://msdn.microsoft.com/en-us/library/ms175759%28v=VS.100%29.aspx
+ SET(WIN32_CXX_FLAGS_COMMON "-D_CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES=1")
+ SET(WIN32_CXX_FLAGS_COMMON "${WIN32_CXX_FLAGS_COMMON} -D_CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES_MEMORY=1")
+ SET(WIN32_CXX_FLAGS_COMMON "${WIN32_CXX_FLAGS_COMMON} -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1")
+ SET(WIN32_CXX_FLAGS_COMMON "${WIN32_CXX_FLAGS_COMMON} -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1")
+ SET(WIN32_CXX_FLAGS_COMMON "${WIN32_CXX_FLAGS_COMMON} -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_MEMORY=1")
+
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WIN32_C_FLAGS_COMMON}")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WIN32_C_FLAGS_COMMON} ${WIN32_CXX_FLAGS_COMMON}")
+ENDIF(WIN32)
+
# Disable unnecessary warnings.
INCLUDE(CheckCCompilerFlag)
IF(MSVC)
diff --git a/cmake/modules/SetWindowsEntrypoint.cmake b/cmake/modules/SetWindowsEntrypoint.cmake
new file mode 100644
index 0000000..92d2df1
--- /dev/null
+++ b/cmake/modules/SetWindowsEntrypoint.cmake
@@ -0,0 +1,51 @@
+# Set Windows entrypoint.
+# _target: Target.
+# _entrypoint: Entry point. (main, wmain, WinMain, wWinMain)
+# _setargv: If true, link to setargv.obj/wsetargv.obj in MSVC builds.
+FUNCTION(SET_WINDOWS_ENTRYPOINT _target _entrypoint _setargv)
+IF(WIN32)
+ IF(MSVC)
+ # MSVC automatically prepends an underscore if necessary.
+ SET(ENTRY_POINT_FLAG "/ENTRY:${_entrypoint}CRTStartup")
+ IF(_setargv)
+ IF(_entrypoint MATCHES ^wmain OR _entrypoint MATCHES ^wWinMain)
+ SET(SETARGV_FLAG "wsetargv.obj")
+ ELSE()
+ SET(SETARGV_FLAG "setargv.obj")
+ ENDIF()
+ ENDIF(_setargv)
+ UNSET(UNICODE_FLAG)
+ ELSE(MSVC)
+ # MinGW does not automatically prepend an underscore.
+ # TODO: Does ARM Windows have a leading underscore?
+ # TODO: _setargv for MinGW.
+
+ # NOTE: MinGW uses separate crt*.o files for Unicode
+ # instead of a separate entry point.
+ IF(_entrypoint MATCHES "^w")
+ SET(UNICODE_FLAG "-municode")
+ STRING(SUBSTRING "${_entrypoint}" 1 -1 _entrypoint)
+ ENDIF()
+
+ IF(CPU_i386 OR CPU_amd64)
+ IF(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ SET(ENTRY_POINT "_${_entrypoint}CRTStartup")
+ ELSE()
+ SET(ENTRY_POINT "${_entrypoint}CRTStartup")
+ ENDIF()
+ ELSE()
+ SET(ENTRY_POINT "${_entrypoint}CRTStartup")
+ ENDIF(CPU_i386 OR CPU_amd64)
+ SET(ENTRY_POINT_FLAG "-Wl,-e,${ENTRY_POINT}")
+ UNSET(SETARGV_FLAG)
+ ENDIF(MSVC)
+
+ GET_TARGET_PROPERTY(TARGET_LINK_FLAGS ${_target} LINK_FLAGS)
+ IF(TARGET_LINK_FLAGS)
+ SET(TARGET_LINK_FLAGS "${TARGET_LINK_FLAGS} ${UNICODE_FLAG} ${ENTRY_POINT_FLAG} ${SETARGV_FLAG}")
+ ELSE()
+ SET(TARGET_LINK_FLAGS "${UNICODE_FLAG} ${ENTRY_POINT_FLAG} ${SETARGV_FLAG}")
+ ENDIF()
+ SET_TARGET_PROPERTIES(${_target} PROPERTIES LINK_FLAGS "${TARGET_LINK_FLAGS}")
+ENDIF(WIN32)
+ENDFUNCTION()
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9e41314..f450f8d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -23,3 +23,7 @@ SET(mst06_H
ADD_EXECUTABLE(mst06 ${mst06_SRCS} ${mst06_H})
SET_PROPERTY(TARGET mst06 PROPERTY CXX_STANDARD 11)
TARGET_INCLUDE_DIRECTORIES(mst06 PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
+
+# TODO: Enable wildcard expansion on Windows?
+INCLUDE(SetWindowsEntrypoint)
+SET_WINDOWS_ENTRYPOINT(mst06 wmain OFF)
\ No newline at end of file
diff --git a/src/mst06.cpp b/src/mst06.cpp
index be1010c..3ff5579 100644
--- a/src/mst06.cpp
+++ b/src/mst06.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
* MST Decoder/Encoder for Sonic '06 *
- * main.cpp: Main program. *
+ * mst06.cpp: Main program. *
* *
* Copyright (c) 2019 by David Korth. *
* *
@@ -32,17 +32,18 @@ using std::pair;
#include "mst_structs.h"
#include "byteswap.h"
+#include "tcharx.h"
-int main(int argc, char *argv[])
+int _tmain(int argc, TCHAR *argv[])
{
if (argc != 2) {
- fprintf(stderr, "Syntax: %s mst_file.mst\n", argv[0]);
+ _ftprintf(stderr, _T("Syntax: %s mst_file.mst\n"), argv[0]);
return EXIT_FAILURE;
}
- FILE *f_mst = fopen(argv[1], "rb");
+ FILE *f_mst = _tfopen(argv[1], _T("rb"));
if (!f_mst) {
- fprintf(stderr, "*** ERROR opening %s: %s\n", argv[1], strerror(errno));
+ _ftprintf(stderr, _T("*** ERROR opening %s: %s\n"), argv[1], _tcserror(errno));
return EXIT_FAILURE;
}
diff --git a/src/tcharx.h b/src/tcharx.h
new file mode 100644
index 0000000..7558433
--- /dev/null
+++ b/src/tcharx.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * MST Decoder/Encoder for Sonic '06 *
+ * tcharx.h: TCHAR support for Windows and Linux. *
+ * *
+ * Copyright (c) 2018-2019 by David Korth. *
+ * *
+ * This program 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 . *
+ ***************************************************************************/
+
+#ifndef __MST06_TCHARX_H__
+#define __MST06_TCHARX_H__
+
+#ifdef _WIN32
+
+// Windows: Use the SDK tchar.h.
+#include
+
+// std::tstring
+#ifdef _UNICODE
+# define tstring wstring
+#else /* !_UNICODE */
+# define tstring string
+#endif /* _UNICODE */
+
+#else /* !_WIN32 */
+
+// Other systems: Define TCHAR and related macros.
+typedef char TCHAR;
+#define _T(x) x
+#define _tmain main
+#define tstring string
+
+// ctype.h
+#define _istalpha(c) isalpha(c)
+
+// stdio.h
+#define _fputts(s, stream) fputs(s, stream)
+
+#define _tfopen(filename, mode) fopen((filename), (mode))
+
+#define _tprintf printf
+#define _ftprintf fprintf
+#define _sntprintf snprintf
+#define _vtprintf vprintf
+#define _vftprintf vfprintf
+#define _vsprintf vsprintf
+
+// stdlib.h
+#define _tcscmp(s1, s2) strcmp((s1), (s2))
+#define _tcsicmp(s1, s2) strcasecmp((s1), (s2))
+#define _tcsnicmp(s1, s2) strncasecmp((s1), (s2), (n))
+#define _tcstoul(nptr, endptr, base) strtoul((nptr), (endptr), (base))
+
+// string.h
+#define _tcsdup(s) strdup(s)
+#define _tcserror(err) strerror(err)
+
+#endif /* _WIN32 */
+
+#endif /* __MST06_TCHARX_H__ */