diff --git a/nxemu.sln b/nxemu.sln
index ca4a8151..ffa345cc 100644
--- a/nxemu.sln
+++ b/nxemu.sln
@@ -9,6 +9,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3rd Party", "3rd Party", "{
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wtl", "src\3rd_party\wtl\wtl.vcxproj", "{1F4A3DC8-4CCA-485E-B120-FEB2B5A15023}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nxemu-core", "src\nxemu-core\nxemu-core.vcxproj", "{CF365EDD-C903-47B1-AAAE-A483D3C0D274}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D442FBFE-1018-4056-A0AE-CBCB6BF9DD90} = {D442FBFE-1018-4056-A0AE-CBCB6BF9DD90}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "update_version", "src\update_version\update_version.vcxproj", "{D442FBFE-1018-4056-A0AE-CBCB6BF9DD90}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "src\common\common.vcxproj", "{EC81BE93-8316-4DB6-8A26-B13FB5B13848}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -33,6 +42,30 @@ Global
{1F4A3DC8-4CCA-485E-B120-FEB2B5A15023}.Release|x64.Build.0 = Release|x64
{1F4A3DC8-4CCA-485E-B120-FEB2B5A15023}.Release|x86.ActiveCfg = Release|Win32
{1F4A3DC8-4CCA-485E-B120-FEB2B5A15023}.Release|x86.Build.0 = Release|Win32
+ {CF365EDD-C903-47B1-AAAE-A483D3C0D274}.Debug|x64.ActiveCfg = Debug|x64
+ {CF365EDD-C903-47B1-AAAE-A483D3C0D274}.Debug|x64.Build.0 = Debug|x64
+ {CF365EDD-C903-47B1-AAAE-A483D3C0D274}.Debug|x86.ActiveCfg = Debug|Win32
+ {CF365EDD-C903-47B1-AAAE-A483D3C0D274}.Debug|x86.Build.0 = Debug|Win32
+ {CF365EDD-C903-47B1-AAAE-A483D3C0D274}.Release|x64.ActiveCfg = Release|x64
+ {CF365EDD-C903-47B1-AAAE-A483D3C0D274}.Release|x64.Build.0 = Release|x64
+ {CF365EDD-C903-47B1-AAAE-A483D3C0D274}.Release|x86.ActiveCfg = Release|Win32
+ {CF365EDD-C903-47B1-AAAE-A483D3C0D274}.Release|x86.Build.0 = Release|Win32
+ {D442FBFE-1018-4056-A0AE-CBCB6BF9DD90}.Debug|x64.ActiveCfg = Debug|x64
+ {D442FBFE-1018-4056-A0AE-CBCB6BF9DD90}.Debug|x64.Build.0 = Debug|x64
+ {D442FBFE-1018-4056-A0AE-CBCB6BF9DD90}.Debug|x86.ActiveCfg = Debug|Win32
+ {D442FBFE-1018-4056-A0AE-CBCB6BF9DD90}.Debug|x86.Build.0 = Debug|Win32
+ {D442FBFE-1018-4056-A0AE-CBCB6BF9DD90}.Release|x64.ActiveCfg = Release|x64
+ {D442FBFE-1018-4056-A0AE-CBCB6BF9DD90}.Release|x64.Build.0 = Release|x64
+ {D442FBFE-1018-4056-A0AE-CBCB6BF9DD90}.Release|x86.ActiveCfg = Release|Win32
+ {D442FBFE-1018-4056-A0AE-CBCB6BF9DD90}.Release|x86.Build.0 = Release|Win32
+ {EC81BE93-8316-4DB6-8A26-B13FB5B13848}.Debug|x64.ActiveCfg = Debug|x64
+ {EC81BE93-8316-4DB6-8A26-B13FB5B13848}.Debug|x64.Build.0 = Debug|x64
+ {EC81BE93-8316-4DB6-8A26-B13FB5B13848}.Debug|x86.ActiveCfg = Debug|Win32
+ {EC81BE93-8316-4DB6-8A26-B13FB5B13848}.Debug|x86.Build.0 = Debug|Win32
+ {EC81BE93-8316-4DB6-8A26-B13FB5B13848}.Release|x64.ActiveCfg = Release|x64
+ {EC81BE93-8316-4DB6-8A26-B13FB5B13848}.Release|x64.Build.0 = Release|x64
+ {EC81BE93-8316-4DB6-8A26-B13FB5B13848}.Release|x86.ActiveCfg = Release|Win32
+ {EC81BE93-8316-4DB6-8A26-B13FB5B13848}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
new file mode 100644
index 00000000..ccaf51a8
--- /dev/null
+++ b/src/common/common.vcxproj
@@ -0,0 +1,38 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {ec81be93-8316-4db6-8a26-b13fb5b13848}
+ common
+
+
+ StaticLibrary
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters
new file mode 100644
index 00000000..a8a65633
--- /dev/null
+++ b/src/common/common.vcxproj.filters
@@ -0,0 +1,17 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
\ No newline at end of file
diff --git a/src/common/common.vcxproj.user b/src/common/common.vcxproj.user
new file mode 100644
index 00000000..88a55094
--- /dev/null
+++ b/src/common/common.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/common/file.cpp b/src/common/file.cpp
new file mode 100644
index 00000000..d37f78a5
--- /dev/null
+++ b/src/common/file.cpp
@@ -0,0 +1,174 @@
+#include "file.h"
+#include
+
+File::File() :
+ m_file(INVALID_HANDLE_VALUE),
+ m_closeOnDestroy(false)
+{
+}
+
+File::File(void * fileHandle) :
+ m_file(fileHandle),
+ m_closeOnDestroy(true)
+{
+}
+
+File::File(const char * fileName, uint32_t openFlags) :
+ m_file(INVALID_HANDLE_VALUE),
+ m_closeOnDestroy(true)
+{
+ Open(fileName, openFlags);
+}
+
+File::~File()
+{
+ if (m_file != INVALID_HANDLE_VALUE && m_closeOnDestroy)
+ {
+ Close();
+ }
+}
+
+bool File::Open(const char * fileName, uint32_t openFlags)
+{
+ if (!Close())
+ {
+ return false;
+ }
+
+ if (fileName == nullptr || strlen(fileName) == 0)
+ {
+ return false;
+ }
+
+ m_file = INVALID_HANDLE_VALUE;
+
+ ULONG dwAccess = 0;
+ switch (openFlags & 3)
+ {
+ case modeRead:
+ dwAccess = GENERIC_READ;
+ break;
+ case modeWrite:
+ dwAccess = GENERIC_WRITE;
+ break;
+ case modeReadWrite:
+ dwAccess = GENERIC_READ | GENERIC_WRITE;
+ break;
+ default:
+ return false;
+ }
+
+ ULONG shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ if ((openFlags & shareDenyWrite) == shareDenyWrite) { shareMode &= ~FILE_SHARE_WRITE; }
+ if ((openFlags & shareDenyRead) == shareDenyRead) { shareMode &= ~FILE_SHARE_READ; }
+ if ((openFlags & shareExclusive) == shareExclusive) { shareMode = 0; }
+
+ // map modeNoInherit flag
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = nullptr;
+ sa.bInheritHandle = (openFlags & modeNoInherit) == 0;
+
+ // map creation flags
+ ULONG createFlag = OPEN_EXISTING;
+ if (openFlags & modeCreate)
+ {
+ createFlag = ((openFlags & modeNoTruncate) != 0) ? OPEN_ALWAYS : CREATE_ALWAYS;
+ }
+
+ HANDLE hFile = ::CreateFileA(fileName, dwAccess, shareMode, &sa, createFlag, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ return false;
+ }
+ m_file = hFile;
+ m_closeOnDestroy = true;
+ return true;
+}
+
+void File::SeekToBegin(void)
+{
+ Seek(0, SeekPosition::begin);
+}
+
+uint64_t File::Seek(int64_t offset, SeekPosition from)
+{
+ LONG distanceToMove = (LONG)(offset & 0xFFFFFFFF);
+ LONG distanceToMoveHigh = (LONG)(offset >> 32);
+ ULONG dwNew = ::SetFilePointer(m_file, distanceToMove, &distanceToMoveHigh, (ULONG)from);
+ if (dwNew == (ULONG)-1)
+ {
+ return (uint64_t)-1;
+ }
+ return ((uint64_t)distanceToMoveHigh << 32) | dwNew;
+}
+
+bool File::SetLength(uint64_t dwNewLen)
+{
+ Seek(dwNewLen, SeekPosition::begin);
+ return SetEndOfFile();
+}
+
+uint64_t File::GetLength() const
+{
+ DWORD hiWord = 0;
+ DWORD loWord = GetFileSize(m_file, &hiWord);
+ return ((uint64_t)hiWord << 32) | (uint64_t)loWord;
+}
+
+uint32_t File::Read(void* lpBuf, uint32_t nCount)
+{
+ if (nCount == 0)
+ {
+ return 0;
+ }
+
+ DWORD read = 0;
+ if (!::ReadFile(m_file, lpBuf, nCount, &read, nullptr))
+ {
+ return 0;
+ }
+ return (uint32_t)read;
+}
+
+bool File::Write(const void * buffer, uint32_t bufferSize)
+{
+ if (bufferSize == 0)
+ {
+ return true;
+ }
+
+ ULONG written = 0;
+ if (!::WriteFile(m_file, buffer, bufferSize, &written, nullptr))
+ {
+ return false;
+ }
+
+ if (written != bufferSize)
+ {
+ return false;
+ }
+ return true;
+}
+
+bool File::Close()
+{
+ bool bError = true;
+ if (m_file != INVALID_HANDLE_VALUE)
+ {
+ bError = !::CloseHandle(m_file);
+ }
+ m_file = INVALID_HANDLE_VALUE;
+ m_closeOnDestroy = false;
+ return bError;
+}
+
+bool File::IsOpen(void) const
+{
+ return m_file != INVALID_HANDLE_VALUE;
+}
+
+bool File::SetEndOfFile()
+{
+ return ::SetEndOfFile(m_file) != 0;
+}
diff --git a/src/common/file.h b/src/common/file.h
new file mode 100644
index 00000000..6a86cb69
--- /dev/null
+++ b/src/common/file.h
@@ -0,0 +1,74 @@
+#pragma once
+#include
+
+class IFile
+{
+public:
+ enum OpenFlags
+ {
+ modeRead = 0x0000,
+ modeWrite = 0x0001,
+ modeReadWrite = 0x0002,
+ shareCompat = 0x0000,
+ shareExclusive = 0x0010,
+ shareDenyWrite = 0x0020,
+ shareDenyRead = 0x0030,
+ shareDenyNone = 0x0040,
+ modeNoInherit = 0x0080,
+ modeCreate = 0x1000,
+ modeNoTruncate = 0x2000,
+ };
+
+ enum class SeekPosition
+ {
+ begin = 0x0,
+ current = 0x1,
+ end = 0x2
+ };
+
+ virtual bool Open(const char * fileName, uint32_t openFlags) = 0;
+
+ virtual uint64_t Seek(int64_t Offset, SeekPosition from) = 0;
+ virtual bool SetLength(uint64_t newLen) = 0;
+ virtual uint64_t GetLength() const = 0;
+
+ virtual uint32_t Read(void * buffer, uint32_t bufferSize) = 0;
+ virtual bool Write(const void * buffer, uint32_t bufferSize) = 0;
+
+ virtual bool Close() = 0;
+ virtual bool IsOpen() const = 0;
+ virtual bool SetEndOfFile() = 0;
+};
+
+class File :
+ public IFile
+{
+public:
+ File();
+ File(void * fileHandle);
+ File(const char * fileName, uint32_t openFlags);
+ ~File();
+
+ bool Open(const char* fileName, uint32_t openFlags);
+
+ uint64_t SeekToEnd(void);
+ void SeekToBegin(void);
+
+ uint64_t Seek(int64_t offset, SeekPosition from);
+ bool SetLength(uint64_t newLen);
+ uint64_t GetLength() const;
+
+ uint32_t Read(void * buffer, uint32_t bufferSize);
+ bool Write(const void * buffer, uint32_t bufferSize);
+
+ bool Close();
+ bool IsOpen() const;
+ bool SetEndOfFile();
+
+private:
+ File(const File&) = delete;
+ File& operator=(const File&) = delete;
+
+ bool m_closeOnDestroy;
+ void * m_file;
+};
diff --git a/src/common/path.cpp b/src/common/path.cpp
new file mode 100644
index 00000000..858da374
--- /dev/null
+++ b/src/common/path.cpp
@@ -0,0 +1,254 @@
+#include "path.h"
+#include "std_string.h"
+#include
+
+const char DRIVE_DELIMITER = ':';
+const char * const DIR_DOUBLEDELIM = "\\\\";
+const char DIRECTORY_DELIMITER = '\\';
+const char DIRECTORY_DELIMITER2 = '/';
+
+Path::Path()
+{
+}
+
+Path::Path(const char * path)
+{
+ m_path = path ? path : "";
+ CleanPath(m_path);
+}
+
+Path::Path(const char * path, const char * fileName)
+{
+ SetDriveDirectory(path);
+ SetNameExtension(fileName);
+}
+
+Path::Path(const std::string & path)
+{
+ m_path = path;
+ CleanPath(m_path);
+}
+
+Path::Path(const std::string & path, const char * fileName)
+{
+ SetDriveDirectory(path.c_str());
+ SetNameExtension(fileName);
+}
+
+Path::Path(const std::string & path, const std::string & fileName)
+{
+ SetDriveDirectory(path.c_str());
+ SetNameExtension(fileName.c_str());
+}
+
+Path::Path(DIR_CURRENT_DIRECTORY /*sdt*/, const char * nameExten)
+{
+ // Application's current directory
+ SetToCurrentDirectory();
+ if (nameExten)
+ {
+ SetNameExtension(nameExten);
+ }
+}
+
+Path::operator const char* () const
+{
+ return (const char*)m_path.c_str();
+}
+
+Path::operator const std::string& ()
+{
+ return m_path;
+}
+
+void Path::GetDriveDirectory(std::string & driveDirectory) const
+{
+ std::string drive, directory;
+ GetComponents(&drive, &directory);
+ driveDirectory = drive;
+ if (!drive.empty())
+ {
+ driveDirectory += DRIVE_DELIMITER;
+ }
+ driveDirectory += directory;
+}
+
+std::string Path::GetDriveDirectory(void) const
+{
+ std::string driveDirectory;
+ GetDriveDirectory(driveDirectory);
+ return driveDirectory;
+}
+
+void Path::GetComponents(std::string * drive, std::string * directory, std::string * name, std::string * extension) const
+{
+ char driveBuff[_MAX_DRIVE + 1] = { 0 }, dirBuff[_MAX_DIR + 1] = { 0 }, nameBuff[_MAX_FNAME + 1] = { 0 }, extBuff[_MAX_EXT + 1] = { 0 };
+
+ const char * basePath = m_path.c_str();
+ const char * driveDir = strrchr(basePath, DRIVE_DELIMITER);
+ if (driveDir != nullptr)
+ {
+ size_t len = sizeof(dirBuff) < (driveDir - basePath) ? sizeof(driveBuff) : driveDir - basePath;
+ strncpy(driveBuff, basePath, len);
+ basePath += len + 1;
+ }
+
+ const char * last = strrchr(basePath, DIRECTORY_DELIMITER);
+ if (last != nullptr)
+ {
+ size_t len = sizeof(dirBuff) < (last - basePath) ? sizeof(dirBuff) : last - basePath;
+ if (len > 0)
+ {
+ strncpy(dirBuff, basePath, len);
+ }
+ else
+ {
+ dirBuff[0] = DIRECTORY_DELIMITER;
+ dirBuff[1] = '\0';
+ }
+ strncpy(nameBuff, last + 1, sizeof(nameBuff));
+ }
+ else
+ {
+ strncpy(dirBuff, basePath, sizeof(dirBuff));
+ }
+ char * ext = strrchr(nameBuff, '.');
+ if (ext != nullptr)
+ {
+ strncpy(extBuff, ext + 1, sizeof(extBuff));
+ *ext = '\0';
+ }
+
+ if (drive)
+ {
+ *drive = driveBuff;
+ }
+ if (directory)
+ {
+ *directory = dirBuff;
+ }
+ if (name)
+ {
+ *name = nameBuff;
+ }
+ if (extension)
+ {
+ *extension = extBuff;
+ }
+}
+
+void Path::SetDriveDirectory(const char * driveDirectory)
+{
+ std::string driveDirectoryStr = driveDirectory;
+ if (driveDirectoryStr.length() > 0)
+ {
+ EnsureTrailingBackslash(driveDirectoryStr);
+ CleanPath(driveDirectoryStr);
+ }
+
+ std::string name, extension;
+ GetComponents(nullptr, nullptr, &name, &extension);
+ SetComponents(nullptr, driveDirectoryStr.c_str(), name.c_str(), extension.c_str());
+}
+
+void Path::SetNameExtension(const char * nameExtension)
+{
+ std::string directory, drive;
+ GetComponents(&drive, &directory);
+ SetComponents(drive.c_str(), directory.c_str(), nameExtension, nullptr);
+}
+
+void Path::SetComponents(const char * drive, const char * directory, const char * name, const char * extension)
+{
+ char fullname[MAX_PATH];
+
+ memset(fullname, 0, sizeof(fullname));
+ if (directory == nullptr || strlen(directory) == 0)
+ {
+ static char emptyDir[] = { DIRECTORY_DELIMITER, '\0' };
+ directory = emptyDir;
+ }
+
+ _makepath(fullname, drive, directory, name, extension);
+ m_path = fullname;
+}
+
+bool Path::FileDelete(bool evenIfReadOnly) const
+{
+ std::wstring filePath = stdstr(m_path).ToUTF16();
+ uint32_t attributes = ::GetFileAttributes(filePath.c_str());
+ if (attributes == (uint32_t)-1)
+ {
+ return false;
+ }
+
+ if (((attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY) && !evenIfReadOnly)
+ {
+ return false;
+ }
+
+ SetFileAttributes(filePath.c_str(), FILE_ATTRIBUTE_NORMAL);
+ return DeleteFile(filePath.c_str()) != 0;
+}
+
+bool Path::FileExists() const
+{
+ WIN32_FIND_DATA FindData;
+ HANDLE hFindFile = FindFirstFile(stdstr(m_path).ToUTF16().c_str(), &FindData);
+ bool bSuccess = (hFindFile != INVALID_HANDLE_VALUE);
+
+ if (hFindFile != nullptr) // Make sure we close the search
+ {
+ FindClose(hFindFile);
+ }
+
+ return bSuccess;
+}
+
+bool Path::DirectoryChange() const
+{
+ std::string driveDirectory;
+ GetDriveDirectory(driveDirectory);
+
+ return SetCurrentDirectory(stdstr(driveDirectory).ToUTF16().c_str()) != 0;
+}
+
+void Path::SetToCurrentDirectory()
+{
+ wchar_t path[260];
+ memset(path, 0, sizeof(path));
+ ::GetCurrentDirectory(sizeof(path), path);
+ SetDriveDirectory(stdstr().FromUTF16(path).c_str());
+}
+
+void Path::CleanPath(std::string& path) const
+{
+ std::string::size_type pos = path.find(DIRECTORY_DELIMITER2);
+ while (pos != std::string::npos)
+ {
+ path.replace(pos, 1, &DIRECTORY_DELIMITER);
+ pos = path.find(DIRECTORY_DELIMITER2, pos + 1);
+ }
+
+ bool appendEnd = !_strnicmp(path.c_str(), DIR_DOUBLEDELIM, 2);
+ pos = path.find(DIR_DOUBLEDELIM);
+ while (pos != std::string::npos)
+ {
+ path.replace(pos, 2, &DIRECTORY_DELIMITER);
+ pos = path.find(DIR_DOUBLEDELIM, pos + 1);
+ }
+ if (appendEnd)
+ {
+ path.insert(0, stdstr_f("%c", DIRECTORY_DELIMITER).c_str());
+ }
+}
+
+void Path::EnsureTrailingBackslash(std::string & directory) const
+{
+ std::string::size_type length = directory.length();
+
+ if (directory.empty() || (directory[length - 1] != DIRECTORY_DELIMITER))
+ {
+ directory += DIRECTORY_DELIMITER;
+ }
+}
diff --git a/src/common/path.h b/src/common/path.h
new file mode 100644
index 00000000..f8016665
--- /dev/null
+++ b/src/common/path.h
@@ -0,0 +1,42 @@
+#pragma once
+#include
+
+class Path
+{
+public:
+ enum DIR_CURRENT_DIRECTORY { CURRENT_DIRECTORY = 1 };
+
+ Path();
+ Path(const char * path);
+ Path(const char * path, const char * fileName);
+ Path(const std::string & path);
+ Path(const std::string & path, const char * fileName);
+ Path(const std::string & path, const std::string & fileName);
+
+ Path(DIR_CURRENT_DIRECTORY sdt, const char * nameExten = nullptr);
+
+ operator const char *() const;
+ operator const std::string& ();
+
+ void GetDriveDirectory(std::string & directory) const;
+ std::string GetDriveDirectory(void) const;
+ void GetComponents(std::string * drive = nullptr, std::string * directory = nullptr, std::string * name = nullptr, std::string * extension = nullptr) const;
+
+ void SetDriveDirectory(const char * driveDirectory);
+ void SetNameExtension(const char * nameExtension);
+ void SetComponents(const char * drive, const char * directory, const char * name, const char * extension);
+
+ bool FileDelete(bool evenIfReadOnly = true) const;
+ bool FileExists() const;
+
+ bool DirectoryChange() const;
+
+private:
+ void SetToCurrentDirectory();
+
+ void CleanPath(std::string & path) const;
+
+ void EnsureTrailingBackslash(std::string & directory) const;
+
+ std::string m_path;
+};
\ No newline at end of file
diff --git a/src/common/sha256.cpp b/src/common/sha256.cpp
new file mode 100644
index 00000000..5b8be064
--- /dev/null
+++ b/src/common/sha256.cpp
@@ -0,0 +1,192 @@
+/*
+* Updated to C++, zedwood.com 2012
+* Based on Olivier Gay's version
+* See Modified BSD License below:
+*
+* FIPS 180-2 SHA-224/256/384/512 implementation
+* Issue date: 04/30/2005
+* http://www.ouah.org/ogay/sha2/
+*
+* Copyright (C) 2005, 2007 Olivier Gay
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+* 3. Neither the name of the project nor the names of its contributors
+* may be used to endorse or promote products derived from this software
+* without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+* SUCH DAMAGE.
+*/
+#include
+#include
+#include "sha256.h"
+
+#define SHA2_SHFR(x, n) (x >> n)
+#define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
+#define SHA2_ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
+#define SHA2_CH(x, y, z) ((x & y) ^ (~x & z))
+#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+#define SHA256_F1(x) (SHA2_ROTR(x, 2) ^ SHA2_ROTR(x, 13) ^ SHA2_ROTR(x, 22))
+#define SHA256_F2(x) (SHA2_ROTR(x, 6) ^ SHA2_ROTR(x, 11) ^ SHA2_ROTR(x, 25))
+#define SHA256_F3(x) (SHA2_ROTR(x, 7) ^ SHA2_ROTR(x, 18) ^ SHA2_SHFR(x, 3))
+#define SHA256_F4(x) (SHA2_ROTR(x, 17) ^ SHA2_ROTR(x, 19) ^ SHA2_SHFR(x, 10))
+#define SHA2_UNPACK32(x, str) \
+{ \
+ *((str) + 3) = (uint8_t) ((x) ); \
+ *((str) + 2) = (uint8_t) ((x) >> 8); \
+ *((str) + 1) = (uint8_t) ((x) >> 16); \
+ *((str) + 0) = (uint8_t) ((x) >> 24); \
+}
+#define SHA2_PACK32(str, x) \
+{ \
+ *(x) = ((uint32_t) *((str) + 3) ) \
+ | ((uint32_t) *((str) + 2) << 8) \
+ | ((uint32_t) *((str) + 1) << 16) \
+ | ((uint32_t) *((str) + 0) << 24); \
+}
+
+const unsigned int SHA256::sha256_k[64] = //UL = uint32
+{ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
+
+void SHA256::transform(const unsigned char *message, unsigned int block_nb)
+{
+ uint32_t w[64];
+ uint32_t wv[8];
+ uint32_t t1, t2;
+ const unsigned char *sub_block;
+ int i;
+ int j;
+ for (i = 0; i < (int)block_nb; i++) {
+ sub_block = message + (i << 6);
+ for (j = 0; j < 16; j++) {
+ SHA2_PACK32(&sub_block[j << 2], &w[j]);
+ }
+ for (j = 16; j < 64; j++) {
+ w[j] = SHA256_F4(w[j - 2]) + w[j - 7] + SHA256_F3(w[j - 15]) + w[j - 16];
+ }
+ for (j = 0; j < 8; j++) {
+ wv[j] = m_h[j];
+ }
+ for (j = 0; j < 64; j++) {
+ t1 = wv[7] + SHA256_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6])
+ + sha256_k[j] + w[j];
+ t2 = SHA256_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]);
+ wv[7] = wv[6];
+ wv[6] = wv[5];
+ wv[5] = wv[4];
+ wv[4] = wv[3] + t1;
+ wv[3] = wv[2];
+ wv[2] = wv[1];
+ wv[1] = wv[0];
+ wv[0] = t1 + t2;
+ }
+ for (j = 0; j < 8; j++) {
+ m_h[j] += wv[j];
+ }
+ }
+}
+
+void SHA256::init()
+{
+ m_h[0] = 0x6a09e667;
+ m_h[1] = 0xbb67ae85;
+ m_h[2] = 0x3c6ef372;
+ m_h[3] = 0xa54ff53a;
+ m_h[4] = 0x510e527f;
+ m_h[5] = 0x9b05688c;
+ m_h[6] = 0x1f83d9ab;
+ m_h[7] = 0x5be0cd19;
+ m_len = 0;
+ m_tot_len = 0;
+}
+
+void SHA256::update(const unsigned char *message, unsigned int len)
+{
+ unsigned int block_nb;
+ unsigned int new_len, rem_len, tmp_len;
+ const unsigned char *shifted_message;
+ tmp_len = SHA224_256_BLOCK_SIZE - m_len;
+ rem_len = len < tmp_len ? len : tmp_len;
+ memcpy(&m_block[m_len], message, rem_len);
+ if (m_len + len < SHA224_256_BLOCK_SIZE) {
+ m_len += len;
+ return;
+ }
+ new_len = len - rem_len;
+ block_nb = new_len / SHA224_256_BLOCK_SIZE;
+ shifted_message = message + rem_len;
+ transform(m_block, 1);
+ transform(shifted_message, block_nb);
+ rem_len = new_len % SHA224_256_BLOCK_SIZE;
+ memcpy(m_block, &shifted_message[block_nb << 6], rem_len);
+ m_len = rem_len;
+ m_tot_len += (block_nb + 1) << 6;
+}
+
+void SHA256::final(unsigned char *digest)
+{
+ unsigned int block_nb;
+ unsigned int pm_len;
+ unsigned int len_b;
+ int i;
+ block_nb = (1 + ((SHA224_256_BLOCK_SIZE - 9)
+ < (m_len % SHA224_256_BLOCK_SIZE)));
+ len_b = (m_tot_len + m_len) << 3;
+ pm_len = block_nb << 6;
+ memset(m_block + m_len, 0, pm_len - m_len);
+ m_block[m_len] = 0x80;
+ SHA2_UNPACK32(len_b, m_block + pm_len - 4);
+ transform(m_block, block_nb);
+ for (i = 0; i < 8; i++) {
+ SHA2_UNPACK32(m_h[i], &digest[i << 2]);
+ }
+}
+
+std::string sha256(std::string input)
+{
+ unsigned char digest[SHA256::DIGEST_SIZE];
+ memset(digest, 0, SHA256::DIGEST_SIZE);
+
+ SHA256 ctx = SHA256();
+ ctx.init();
+ ctx.update((unsigned char*)input.c_str(), (unsigned int)input.length());
+ ctx.final(digest);
+
+ char buf[2 * SHA256::DIGEST_SIZE + 1];
+ buf[2 * SHA256::DIGEST_SIZE] = 0;
+ for (int i = 0; i < SHA256::DIGEST_SIZE; i++)
+ sprintf(buf + i * 2, "%02x", digest[i]);
+ return std::string(buf);
+}
diff --git a/src/common/sha256.h b/src/common/sha256.h
new file mode 100644
index 00000000..4e2ee77f
--- /dev/null
+++ b/src/common/sha256.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include
+#include
+
+class SHA256
+{
+protected:
+ const static uint32_t sha256_k[];
+ static const unsigned int SHA224_256_BLOCK_SIZE = (512 / 8);
+
+public:
+ void init();
+ void update(const unsigned char *message, unsigned int len);
+ void final(unsigned char *digest);
+ static const unsigned int DIGEST_SIZE = (256 / 8);
+
+protected:
+ void transform(const unsigned char *message, unsigned int block_nb);
+ unsigned int m_tot_len;
+ unsigned int m_len;
+ unsigned char m_block[2 * SHA224_256_BLOCK_SIZE];
+ uint32_t m_h[8];
+};
+
+std::string sha256(std::string input);
\ No newline at end of file
diff --git a/src/common/std_string.cpp b/src/common/std_string.cpp
new file mode 100644
index 00000000..73362282
--- /dev/null
+++ b/src/common/std_string.cpp
@@ -0,0 +1,252 @@
+#include "std_string.h"
+#include
+
+stdstr::stdstr()
+{
+}
+
+stdstr::stdstr(const std::string & str) :
+ std::string(str)
+{
+}
+
+stdstr::stdstr(const stdstr & str) :
+ std::string((const std::string &)str)
+{
+}
+
+stdstr::stdstr(const char * str) :
+ std::string(str ? str : "")
+{
+}
+
+strvector stdstr::Tokenize(const char* delimiter) const
+{
+ strvector tokens;
+
+ stdstr::size_type lastPos = find_first_not_of(delimiter, 0);
+ stdstr::size_type pos = find_first_of(delimiter, lastPos);
+ size_t delLen = strlen(delimiter);
+ while (stdstr::npos != pos)
+ {
+ tokens.push_back(substr(lastPos, pos - lastPos));
+ lastPos = pos + delLen;
+ pos = find_first_of(delimiter, lastPos);
+ }
+ if (stdstr::npos != lastPos)
+ {
+ tokens.push_back(substr(lastPos));
+ }
+ return tokens;
+}
+
+strvector stdstr::Tokenize(char delimiter) const
+{
+ strvector tokens;
+
+ stdstr::size_type lastPos = find_first_not_of(delimiter, 0);
+ stdstr::size_type pos = find_first_of(delimiter, lastPos);
+ while (stdstr::npos != pos)
+ {
+ tokens.push_back(substr(lastPos, pos - lastPos));
+ lastPos = pos + 1;
+ pos = find_first_of(delimiter, lastPos);
+ }
+ if (stdstr::npos != lastPos)
+ {
+ tokens.push_back(substr(lastPos));
+ }
+ return tokens;
+}
+
+void stdstr::Format(const char * strFormat, ...)
+{
+ va_list args;
+ va_start(args, strFormat);
+ ArgFormat(strFormat, args);
+ va_end(args);
+}
+
+void stdstr::Replace(const char search, const char replace)
+{
+ std::string & str = *this;
+ std::string::size_type pos = str.find(search);
+ while (pos != std::string::npos)
+ {
+ str.replace(pos, 1, &replace);
+ pos = str.find(search, pos + 1);
+ }
+}
+
+void stdstr::Replace(const char * search, const char replace)
+{
+ std::string & str = *this;
+ std::string::size_type pos = str.find(search);
+ size_t SearchSize = strlen(search);
+ while (pos != std::string::npos)
+ {
+ str.replace(pos, SearchSize, &replace);
+ pos = str.find(search, pos + 1);
+ }
+}
+
+void stdstr::Replace(const std::string & search, const std::string & replace)
+{
+ std::string & str = *this;
+ std::string::size_type pos = str.find(search);
+ size_t SearchSize = search.size();
+ while (pos != std::string::npos)
+ {
+ str.replace(pos, SearchSize, replace);
+ pos = str.find(search, pos + replace.length());
+ }
+}
+
+stdstr & stdstr::TrimLeft(const char * chars2remove)
+{
+ if (!empty())
+ {
+ std::string::size_type pos = find_first_not_of(chars2remove);
+ if (pos != std::string::npos)
+ {
+ erase(0, pos);
+ }
+ else
+ {
+ erase(begin(), end());
+ }
+ }
+ return *this;
+}
+
+stdstr & stdstr::TrimRight(const char* chars2remove)
+{
+ if (!empty())
+ {
+ std::string::size_type pos = find_last_not_of(chars2remove);
+ if (pos != std::string::npos)
+ {
+ erase(pos + 1);
+ }
+ else
+ {
+ erase(begin(), end());
+ }
+ }
+ return *this;
+}
+
+stdstr& stdstr::Trim(const char* chars2remove)
+{
+ if (!empty())
+ {
+ std::string::size_type pos = find_first_not_of(chars2remove);
+ if (pos != std::string::npos)
+ {
+ erase(0, pos);
+ }
+ else
+ {
+ erase(begin(), end());
+ }
+
+ pos = find_last_not_of(chars2remove);
+ if (pos != std::string::npos)
+ {
+ erase(pos + 1);
+ }
+ else
+ {
+ erase(begin(), end());
+ }
+ }
+ return *this;
+}
+
+#ifdef _WIN32
+stdstr & stdstr::FromUTF16(const wchar_t * utf16Source, bool * success)
+{
+ bool converted = false;
+
+ if (utf16Source == nullptr)
+ {
+ *this = "";
+ converted = true;
+ }
+ else if (wcslen(utf16Source) > 0)
+ {
+ uint32_t needed = WideCharToMultiByte(CODEPAGE_UTF8, 0, utf16Source, -1, nullptr, 0, nullptr, nullptr);
+ if (needed > 0)
+ {
+ char * buf = (char *)alloca(needed + 1);
+ if (buf != nullptr)
+ {
+ memset(buf, 0, needed + 1);
+
+ needed = WideCharToMultiByte(CODEPAGE_UTF8, 0, utf16Source, -1, buf, needed, nullptr, nullptr);
+ if (needed)
+ {
+ *this = buf;
+ converted = true;
+ }
+ }
+ }
+ }
+ if (success)
+ {
+ *success = converted;
+ }
+ return *this;
+}
+
+std::wstring stdstr::ToUTF16(unsigned int codePage, bool * success) const
+{
+ bool converted = false;
+ std::wstring res;
+
+ DWORD needed = MultiByteToWideChar(codePage, 0, this->c_str(), (int)this->length(), nullptr, 0);
+ if (needed > 0)
+ {
+ wchar_t * buf = (wchar_t *)alloca((needed + 1) * sizeof(wchar_t));
+ if (buf != nullptr)
+ {
+ memset(buf, 0, (needed + 1) * sizeof(wchar_t));
+
+ needed = MultiByteToWideChar(codePage, 0, this->c_str(), (int)this->length(), buf, needed);
+ if (needed)
+ {
+ res = buf;
+ converted = true;
+ }
+ }
+ }
+ if (success)
+ {
+ *success = converted;
+ }
+ return res;
+}
+#endif
+
+void stdstr::ArgFormat(const char* strFormat, va_list& args)
+{
+#pragma warning(push)
+#pragma warning(disable:4996)
+ size_t nlen = _vscprintf(strFormat, args) + 1;
+ char* buffer = (char*)alloca(nlen * sizeof(char));
+ buffer[nlen - 1] = 0;
+ if (buffer != nullptr)
+ {
+ vsprintf(buffer, strFormat, args);
+ *this = buffer;
+ }
+#pragma warning(pop)
+}
+
+stdstr_f::stdstr_f(const char * strFormat, ...)
+{
+ va_list args;
+ va_start(args, strFormat);
+ ArgFormat(strFormat, args);
+ va_end(args);
+}
diff --git a/src/common/std_string.h b/src/common/std_string.h
new file mode 100644
index 00000000..38cd04a6
--- /dev/null
+++ b/src/common/std_string.h
@@ -0,0 +1,48 @@
+#pragma once
+
+class stdstr;
+
+#include
+#include
+
+typedef std::vector strvector;
+
+class stdstr :
+ public std::string
+{
+public:
+ enum
+ {
+ CODEPAGE_UTF8 = 65001,
+ };
+
+ stdstr();
+ stdstr(const std::string & str);
+ stdstr(const stdstr & str);
+ stdstr(const char * str);
+
+ strvector Tokenize(char delimiter) const;
+ strvector Tokenize(const char * delimiter) const;
+ void Format(const char * strFormat, ...);
+
+ void Replace(const char search, const char replace);
+ void Replace(const char * search, const char replace);
+ void Replace(const std::string & search, const std::string & replace);
+
+ stdstr & Trim(const char * chars2remove = "\t ");
+ stdstr & TrimLeft(const char * chars2remove = "\t ");
+ stdstr & TrimRight(const char * chars2remove = "\t ");
+
+#ifdef _WIN32
+ stdstr & FromUTF16(const wchar_t * utf16Source, bool * success = nullptr);
+ std::wstring ToUTF16(unsigned int codePage = CODEPAGE_UTF8, bool * success = nullptr) const;
+#endif
+
+ void ArgFormat(const char * format, va_list & args);
+};
+
+class stdstr_f : public stdstr
+{
+public:
+ stdstr_f(const char * strFormat, ...);
+};
diff --git a/src/nxemu-core/Version.h.in b/src/nxemu-core/Version.h.in
new file mode 100644
index 00000000..6d0be0fb
--- /dev/null
+++ b/src/nxemu-core/Version.h.in
@@ -0,0 +1,33 @@
+#define STRINGIZE2(s) #s
+#define STRINGIZE(s) STRINGIZE2(s)
+
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 1
+#define VERSION_REVISION 0
+#define VERSION_BUILD 9999
+#define GIT_VERSION ""
+
+#define VER_FILE_DESCRIPTION_STR "NxEmu"
+#define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
+#define VER_FILE_VERSION_STR STRINGIZE(VERSION_MAJOR) \
+ "." STRINGIZE(VERSION_MINOR) \
+ "." STRINGIZE(VERSION_REVISION) \
+ "." STRINGIZE(VERSION_BUILD) \
+ "-" GIT_VERSION
+
+#define VER_PRODUCTNAME_STR "NxEmu"
+#define VER_PRODUCT_VERSION VER_FILE_VERSION
+#define VER_PRODUCT_VERSION_STR VER_FILE_VERSION_STR
+#define VER_ORIGINAL_FILENAME_STR VER_PRODUCTNAME_STR ".exe"
+#define VER_INTERNAL_NAME_STR VER_PRODUCTNAME_STR
+#define VER_COPYRIGHT_STR "Copyright (C) 2025"
+
+#ifdef _DEBUG
+#define VER_VER_DEBUG VS_FF_DEBUG
+#else
+#define VER_VER_DEBUG 0
+#endif
+
+#define VER_FILEOS VOS_NT_WINDOWS32
+#define VER_FILEFLAGS VER_VER_DEBUG
+#define VER_FILETYPE VFT_APP
diff --git a/src/nxemu-core/nxemu-core.vcxproj b/src/nxemu-core/nxemu-core.vcxproj
new file mode 100644
index 00000000..8c7cbbb8
--- /dev/null
+++ b/src/nxemu-core/nxemu-core.vcxproj
@@ -0,0 +1,33 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {cf365edd-c903-47b1-aaae-a483d3c0d274}
+ nxemucore
+
+
+ StaticLibrary
+
+
+
+
+
+
+
+
+ "$(SolutionDir)src\script\update_version.cmd" "$(Configuration)" "$(Platform)" "$(SolutionDir)src\nxemu-core\version.h.in" "$(SolutionDir)src\nxemu-core\version.h"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/nxemu-core/nxemu-core.vcxproj.filters b/src/nxemu-core/nxemu-core.vcxproj.filters
new file mode 100644
index 00000000..a8a65633
--- /dev/null
+++ b/src/nxemu-core/nxemu-core.vcxproj.filters
@@ -0,0 +1,17 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
\ No newline at end of file
diff --git a/src/nxemu-core/nxemu-core.vcxproj.user b/src/nxemu-core/nxemu-core.vcxproj.user
new file mode 100644
index 00000000..88a55094
--- /dev/null
+++ b/src/nxemu-core/nxemu-core.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/nxemu-core/version.h b/src/nxemu-core/version.h
new file mode 100644
index 00000000..118bc547
--- /dev/null
+++ b/src/nxemu-core/version.h
@@ -0,0 +1,34 @@
+#define STRINGIZE2(s) #s
+#define STRINGIZE(s) STRINGIZE2(s)
+
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 1
+#define VERSION_REVISION 0
+#define VERSION_BUILD 2
+#define GIT_VERSION "189be11-Dirty"
+
+#define VER_FILE_DESCRIPTION_STR "NxEmu"
+#define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
+#define VER_FILE_VERSION_STR STRINGIZE(VERSION_MAJOR) \
+ "." STRINGIZE(VERSION_MINOR) \
+ "." STRINGIZE(VERSION_REVISION) \
+ "." STRINGIZE(VERSION_BUILD) \
+ "-" GIT_VERSION
+
+#define VER_PRODUCTNAME_STR "NxEmu"
+#define VER_PRODUCT_VERSION VER_FILE_VERSION
+#define VER_PRODUCT_VERSION_STR VER_FILE_VERSION_STR
+#define VER_ORIGINAL_FILENAME_STR VER_PRODUCTNAME_STR ".exe"
+#define VER_INTERNAL_NAME_STR VER_PRODUCTNAME_STR
+#define VER_COPYRIGHT_STR "Copyright (C) 2025"
+
+#ifdef _DEBUG
+#define VER_VER_DEBUG VS_FF_DEBUG
+#else
+#define VER_VER_DEBUG 0
+#endif
+
+#define VER_FILEOS VOS_NT_WINDOWS32
+#define VER_FILEFLAGS VER_VER_DEBUG
+#define VER_FILETYPE VFT_APP
+
diff --git a/src/nxemu/main.cpp b/src/nxemu/main.cpp
index 23b5bf65..6c9b395e 100644
--- a/src/nxemu/main.cpp
+++ b/src/nxemu/main.cpp
@@ -1,10 +1,12 @@
#include "user_interface/main_window.h"
+#include
+#include
#include
#include
int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpszArgs*/, int /*nWinMode*/)
{
- std::unique_ptr mainWindow = std::make_unique(L"NxEmu");
+ std::unique_ptr mainWindow = std::make_unique(stdstr_f("NXEmu %s", VER_FILE_VERSION_STR).ToUTF16().c_str());
mainWindow->ShowWindow(true);
mainWindow->ProcessAllMessages();
mainWindow.reset(nullptr);
diff --git a/src/nxemu/nxemu.vcxproj b/src/nxemu/nxemu.vcxproj
index 18fe201e..88b8f025 100644
--- a/src/nxemu/nxemu.vcxproj
+++ b/src/nxemu/nxemu.vcxproj
@@ -29,5 +29,10 @@
+
+
+ {ec81be93-8316-4db6-8a26-b13fb5b13848}
+
+
\ No newline at end of file
diff --git a/src/script/update_version.cmd b/src/script/update_version.cmd
new file mode 100644
index 00000000..3bf5c568
--- /dev/null
+++ b/src/script/update_version.cmd
@@ -0,0 +1,20 @@
+@echo off
+SETLOCAL
+
+set origdir=%cd%
+cd /d %~dp0..\..
+set base_dir=%cd%
+cd /d %origdir%
+
+if "%~4"=="" (
+ echo Usage: update_version.cmd [BuildMode] [Platform] [InFile] [OutFile]
+ goto :eof
+)
+
+if not "%1" == "" set BuildMode=%~1
+if not "%~2" == "" set Platform=%~2
+if not "%~2" == "" set InFile="%~3"
+if not "%~3" == "" set OutFile="%~4"
+
+echo "%base_dir%\Bin\%Platform%\%BuildMode%\update_version.exe" %InFile% %OutFile%
+"%base_dir%\Bin\%Platform%\%BuildMode%\update_version.exe" %InFile% %OutFile%
diff --git a/src/update_version/update_version.cpp b/src/update_version/update_version.cpp
new file mode 100644
index 00000000..0727fced
--- /dev/null
+++ b/src/update_version/update_version.cpp
@@ -0,0 +1,263 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+bool GitCommand(const Path & sourceDirectory, const char * command, std::string & output)
+{
+ Path currentDir(Path::CURRENT_DIRECTORY);
+ if (currentDir != sourceDirectory)
+ {
+ sourceDirectory.DirectoryChange();
+ }
+ output.clear();
+ FILE * pipe = _popen(stdstr_f("git %s", command).c_str(), "r");
+ if (pipe == nullptr)
+ {
+ if (currentDir != sourceDirectory)
+ {
+ currentDir.DirectoryChange();
+ }
+ return false;
+ }
+
+ char buffer[128];
+ while (!feof(pipe))
+ {
+ if (fgets(buffer, 128, pipe) != NULL)
+ {
+ output += buffer;
+ }
+ }
+ if (currentDir != sourceDirectory)
+ {
+ currentDir.DirectoryChange();
+ }
+ if (feof(pipe))
+ {
+ _pclose(pipe);
+ return true;
+ }
+ return false;
+}
+
+uint32_t GitBuildVersion(const Path & sourceDirectory)
+{
+ enum
+ {
+ DefaultBuildVersion = 9999
+ };
+ std::string result;
+ if (!GitCommand(sourceDirectory, "rev-list --count HEAD", result))
+ {
+ return DefaultBuildVersion;
+ }
+ if (result.empty())
+ {
+ return DefaultBuildVersion;
+ }
+ uint32_t BuildVersion = atoi(result.c_str());
+ if (BuildVersion != 0)
+ {
+ return BuildVersion;
+ }
+ return 9999;
+}
+
+bool GitBuildDirty(Path & sourceDirectory)
+{
+ std::string result;
+ if (!GitCommand(sourceDirectory, "diff --stat", result))
+ {
+ return false;
+ }
+ return !result.empty();
+}
+
+std::string GitRevision(Path & sourceDirectory)
+{
+ stdstr result;
+ if (!GitCommand(sourceDirectory, "rev-parse HEAD", result))
+ {
+ return "";
+ }
+ result.Replace("\r", "");
+ strvector ResultVector = result.Tokenize("\n");
+ if (ResultVector.size() > 0)
+ {
+ return ResultVector[0];
+ }
+ return "";
+}
+
+std::string GitRevisionShort(Path & sourceDirectory)
+{
+ stdstr result;
+ if (!GitCommand(sourceDirectory, "rev-parse --short HEAD", result))
+ {
+ return "";
+ }
+ result.Replace("\r", "");
+ strvector ResultVector = result.Tokenize("\n");
+ if (ResultVector.size() > 0)
+ {
+ return ResultVector[0];
+ }
+ return "";
+}
+
+int main()
+{
+ if (__argc < 3)
+ {
+ return 0;
+ }
+
+ Path sourceFile(__argv[1]), destFile(__argv[2]);
+ if (!sourceFile.FileExists())
+ {
+ return 0;
+ }
+
+ std::string destHash;
+ if (destFile.FileExists())
+ {
+ File outFile(destFile, IFile::OpenFlags::modeRead);
+ if (outFile.IsOpen())
+ {
+ uint32_t OutFileLen = (uint32_t)outFile.GetLength();
+ std::string data("", OutFileLen);
+ if (outFile.Read((void *)data.data(), OutFileLen) != 0)
+ {
+ destHash = sha256(data.c_str());
+ }
+ }
+ }
+
+ Path sourceDirectory(sourceFile.GetDriveDirectory(), "");
+ uint32_t versionBuild = GitBuildVersion(sourceDirectory);
+ bool BuildDirty = GitBuildDirty(sourceDirectory);
+ std::string Revision = GitRevision(sourceDirectory);
+ std::string RevisionShort = GitRevisionShort(sourceDirectory);
+
+ File inFile(sourceFile, IFile::modeRead);
+ if (!inFile.IsOpen())
+ {
+ return 0;
+ }
+ inFile.SeekToBegin();
+ uint32_t fileLen = (uint32_t)inFile.GetLength();
+ std::unique_ptr InputData = std::make_unique(fileLen);
+ inFile.Read(InputData.get(), fileLen);
+ strvector versionData = stdstr(std::string((char *)InputData.get(), fileLen)).Tokenize("\n");
+
+ uint32_t versionMajor = 0, versionMinor = 0, versionRevison = 0;
+ std::string versionPrefix;
+ for (size_t i = 0, n = versionData.size(); i < n; i++)
+ {
+ stdstr line = versionData[i];
+ line.Trim();
+
+ if (_strnicmp(line.c_str(), "VersionMajor = ", 13) == 0)
+ {
+ versionMajor = atoi(&(line.c_str()[14]));
+ }
+ else if (_strnicmp(line.c_str(), "VersionMinor = ", 13) == 0)
+ {
+ versionMinor = atoi(&(line.c_str()[14]));
+ }
+ else if (_strnicmp(line.c_str(), "VersionRevison = ", 17) == 0)
+ {
+ versionRevison = atoi(&(line.c_str()[17]));
+ }
+ else if (_strnicmp(line.c_str(), "VersionPrefix = ", 15) == 0)
+ {
+ size_t startPrefix = line.find("\"", 15);
+ size_t endPrefix = std::string::npos;
+ if (startPrefix != std::string::npos)
+ {
+ endPrefix = line.find("\"", startPrefix + 1);
+ }
+ if (endPrefix != std::string::npos)
+ {
+ versionPrefix = line.substr(startPrefix + 1, endPrefix - (startPrefix + 1));
+ }
+ }
+ else
+ {
+ continue;
+ }
+ versionData.erase(versionData.begin() + i);
+ i -= 1;
+ n -= 1;
+ }
+
+ std::string OutData;
+ for (size_t i = 0, n = versionData.size(); i < n; i++)
+ {
+ stdstr &line = versionData[i];
+ line += "\n";
+ if (_strnicmp(line.c_str(), "#define GIT_VERSION ", 20) == 0)
+ {
+ line.Format("#define GIT_VERSION \"%s%s%s\"\n", RevisionShort.c_str(), BuildDirty ? "-" : "", BuildDirty ? "Dirty" : "");
+ }
+ else if (_strnicmp(line.c_str(), "#define VERSION_BUILD ", 22) == 0)
+ {
+ line.Format("#define VERSION_BUILD %d\n", versionBuild);
+ }
+ else if (_strnicmp(line.c_str(), "#define VERSION_BUILD_YEAR ", 27) == 0)
+ {
+ time_t now = time(NULL);
+ struct tm * tnow = gmtime(&now);
+
+ line.Format("#define VERSION_BUILD_YEAR %d\n", tnow->tm_year + 1900);
+ }
+ else if (_strnicmp(line.c_str(), "#define GIT_REVISION ", 21) == 0)
+ {
+ line.Format("#define GIT_REVISION \"%s\"\n", Revision.c_str());
+ }
+ else if (_strnicmp(line.c_str(), "#define GIT_REVISION_SHORT ", 26) == 0)
+ {
+ line.Format("#define GIT_REVISION_SHORT \"%s\"\n", RevisionShort.c_str());
+ }
+ else if (_strnicmp(line.c_str(), "#define GIT_DIRTY ", 11) == 0)
+ {
+ line.Format("#define GIT_DIRTY \"%s\"\n", BuildDirty ? "Dirty" : "");
+ }
+ else if (_strnicmp(line.c_str(), " versionCode = ", 22) == 0)
+ {
+ line.Format(" versionCode = %d\n", versionBuild);
+ }
+ else if (_strnicmp(line.c_str(), " versionName = ", 22) == 0)
+ {
+ line.Format(" versionName = \"%d.%d.%d.%d", versionMajor, versionMinor, versionRevison, versionBuild);
+ if (versionPrefix.length() > 0)
+ {
+ line += stdstr_f(" (%s)", versionPrefix.c_str());
+ }
+ line += "\"\n";
+ }
+ OutData += line.c_str();
+ }
+
+ std::string outHash = sha256(OutData);
+ if (outHash != destHash)
+ {
+ if (destFile.FileExists() && !destFile.FileDelete())
+ {
+ return 0;
+ }
+
+ File outFile(destFile, IFile::modeWrite | IFile::modeCreate);
+ if (!outFile.IsOpen())
+ {
+ return 0;
+ }
+ outFile.Write(OutData.c_str(), (uint32_t)OutData.length());
+ }
+ return 0;
+}
diff --git a/src/update_version/update_version.vcxproj b/src/update_version/update_version.vcxproj
new file mode 100644
index 00000000..f7ef685b
--- /dev/null
+++ b/src/update_version/update_version.vcxproj
@@ -0,0 +1,34 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {d442fbfe-1018-4056-a0ae-cbcb6bf9dd90}
+ updateversion
+
+
+ Application
+
+
+
+
+
+
+
+
+
+
+
+
+ {ec81be93-8316-4db6-8a26-b13fb5b13848}
+
+
+
\ No newline at end of file
diff --git a/src/update_version/update_version.vcxproj.filters b/src/update_version/update_version.vcxproj.filters
new file mode 100644
index 00000000..a8a65633
--- /dev/null
+++ b/src/update_version/update_version.vcxproj.filters
@@ -0,0 +1,17 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
\ No newline at end of file
diff --git a/src/update_version/update_version.vcxproj.user b/src/update_version/update_version.vcxproj.user
new file mode 100644
index 00000000..1c19bc32
--- /dev/null
+++ b/src/update_version/update_version.vcxproj.user
@@ -0,0 +1,7 @@
+
+
+
+ "D:\Dev\nxemu-public\src\nxemu-core\version.h.in" "D:\Dev\nxemu-public\src\nxemu-core\version.h"
+ WindowsLocalDebugger
+
+
\ No newline at end of file