mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	Common: Error/FileSystem backports
This commit is contained in:
		
							parent
							
								
									7890051165
								
							
						
					
					
						commit
						39f64a03ee
					
				|  | @ -1,182 +1,140 @@ | ||||||
| // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
 | // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
 | ||||||
| // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | ||||||
| 
 | 
 | ||||||
| #include "error.h" | #include "error.h" | ||||||
|  | #include "string_util.h" | ||||||
|  | 
 | ||||||
| #include <cstdlib> | #include <cstdlib> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include <type_traits> | #include <type_traits> | ||||||
| 
 | 
 | ||||||
| // Platform-specific includes
 | #include "fmt/format.h" | ||||||
|  | 
 | ||||||
| #if defined(_WIN32) | #if defined(_WIN32) | ||||||
| #include "windows_headers.h" | #include "windows_headers.h" | ||||||
| static_assert(std::is_same<DWORD, unsigned long>::value, "DWORD is unsigned long"); |  | ||||||
| static_assert(std::is_same<HRESULT, long>::value, "HRESULT is long"); |  | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| namespace Common { | Error::Error() = default; | ||||||
| 
 | 
 | ||||||
| Error::Error() : m_type(Type::None) | Error::Error(const Error& c) = default; | ||||||
| { |  | ||||||
|   m_error.none = 0; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| Error::Error(const Error& c) | Error::Error(Error&& e) = default; | ||||||
| { |  | ||||||
|   m_type = c.m_type; |  | ||||||
|   std::memcpy(&m_error, &c.m_error, sizeof(m_error)); |  | ||||||
|   m_code_string.AppendString(c.m_code_string); |  | ||||||
|   m_message.AppendString(c.m_message); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| Error::~Error() = default; | Error::~Error() = default; | ||||||
| 
 | 
 | ||||||
| void Error::Clear() | void Error::Clear() | ||||||
| { | { | ||||||
|   m_type = Type::None; |   m_description = {}; | ||||||
|   m_error.none = 0; |  | ||||||
|   m_code_string.Clear(); |  | ||||||
|   m_message.Clear(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Error::SetErrno(int err) | void Error::SetErrno(int err) | ||||||
| { | { | ||||||
|   m_type = Type::Errno; |   m_type = Type::Errno; | ||||||
|   m_error.errno_f = err; |  | ||||||
| 
 |  | ||||||
|   m_code_string.Format("%i", err); |  | ||||||
| 
 | 
 | ||||||
| #ifdef _MSC_VER | #ifdef _MSC_VER | ||||||
|   strerror_s(m_message.GetWriteableCharArray(), m_message.GetBufferSize(), err); |   char buf[128]; | ||||||
|   m_message.UpdateSize(); |   if (strerror_s(buf, sizeof(buf), err) != 0) | ||||||
| #else |     m_description = fmt::format("errno {}: {}", err, buf); | ||||||
|   const char* message = std::strerror(err); |  | ||||||
|   if (message) |  | ||||||
|     m_message = message; |  | ||||||
|   else |   else | ||||||
|     m_message = StaticString("<Could not get error message>"); |     m_description = fmt::format("errno {}: <Could not get error message>", err); | ||||||
| #endif |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Error::SetSocket(int err) |  | ||||||
| { |  | ||||||
| // Socket errors are win32 errors on windows
 |  | ||||||
| #ifdef _WIN32 |  | ||||||
|   SetWin32(err); |  | ||||||
| #else | #else | ||||||
|   SetErrno(err); |   const char* buf = std::strerror(err); | ||||||
|  |   if (buf) | ||||||
|  |     m_description = fmt::format("errno {}: {}", err, buf); | ||||||
|  |   else | ||||||
|  |     m_description = fmt::format("errno {}: <Could not get error message>", err); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Error::SetMessage(const char* msg) | void Error::SetErrno(Error* errptr, int err) | ||||||
| { | { | ||||||
|   m_type = Type::User; |   if (errptr) | ||||||
|   m_error.user = 0; |     errptr->SetErrno(err); | ||||||
|   m_code_string.Clear(); |  | ||||||
|   m_message = msg; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Error::SetUser(int err, const char* msg) | void Error::SetString(std::string description) | ||||||
| { | { | ||||||
|   m_type = Type::User; |   m_type = Type::User; | ||||||
|   m_error.user = err; |   m_description = std::move(description); | ||||||
|   m_code_string.Format("%d", err); |  | ||||||
|   m_message = msg; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Error::SetUser(const char* code, const char* message) | void Error::SetString(Error* errptr, std::string description) | ||||||
| { | { | ||||||
|   m_type = Type::User; |   if (errptr) | ||||||
|   m_error.user = 0; |     errptr->SetString(std::move(description)); | ||||||
|   m_code_string = code; |  | ||||||
|   m_message = message; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Error::SetUserFormatted(int err, const char* format, ...) |  | ||||||
| { |  | ||||||
|   std::va_list ap; |  | ||||||
|   va_start(ap, format); |  | ||||||
| 
 |  | ||||||
|   m_type = Type::User; |  | ||||||
|   m_error.user = err; |  | ||||||
|   m_code_string.Format("%d", err); |  | ||||||
|   m_message.FormatVA(format, ap); |  | ||||||
|   va_end(ap); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Error::SetUserFormatted(const char* code, const char* format, ...) |  | ||||||
| { |  | ||||||
|   std::va_list ap; |  | ||||||
|   va_start(ap, format); |  | ||||||
| 
 |  | ||||||
|   m_type = Type::User; |  | ||||||
|   m_error.user = 0; |  | ||||||
|   m_code_string = code; |  | ||||||
|   m_message.FormatVA(format, ap); |  | ||||||
|   va_end(ap); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Error::SetFormattedMessage(const char* format, ...) |  | ||||||
| { |  | ||||||
|   std::va_list ap; |  | ||||||
|   va_start(ap, format); |  | ||||||
| 
 |  | ||||||
|   m_type = Type::User; |  | ||||||
|   m_error.user = 0; |  | ||||||
|   m_code_string.Clear(); |  | ||||||
|   m_message.FormatVA(format, ap); |  | ||||||
|   va_end(ap); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| 
 |  | ||||||
| void Error::SetWin32(unsigned long err) | void Error::SetWin32(unsigned long err) | ||||||
| { | { | ||||||
|   m_type = Type::Win32; |   m_type = Type::Win32; | ||||||
|   m_error.win32 = err; |  | ||||||
|   m_code_string.Format("%u", static_cast<u32>(err)); |  | ||||||
|   m_message.Clear(); |  | ||||||
| 
 | 
 | ||||||
|   const DWORD r = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, m_error.win32, 0, m_message.GetWriteableCharArray(), |   WCHAR buf[128]; | ||||||
|                                  m_message.GetWritableBufferSize(), NULL); |   const DWORD r = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, err, LANG_USER_DEFAULT, buf, | ||||||
|  |                                  static_cast<DWORD>(std::size(buf)), nullptr); | ||||||
|   if (r > 0) |   if (r > 0) | ||||||
|   { |   { | ||||||
|     m_message.Resize(r); |     m_description = | ||||||
|     m_message.RStrip(); |       fmt::format("Win32 Error {}: {}", err, StringUtil::WideStringToUTF8String(std::wstring_view(buf, r))); | ||||||
|   } |   } | ||||||
|   else |   else | ||||||
|   { |   { | ||||||
|     m_message = "<Could not resolve system error ID>"; |     m_description = fmt::format("Win32 Error {}: <Could not resolve system error ID>", err); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void Error::SetWin32(Error* errptr, unsigned long err) | ||||||
|  | { | ||||||
|  |   if (errptr) | ||||||
|  |     errptr->SetWin32(err); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Error::SetHResult(long err) | void Error::SetHResult(long err) | ||||||
| { | { | ||||||
|   m_type = Type::HResult; |   m_type = Type::HResult; | ||||||
|   m_error.win32 = err; |  | ||||||
|   m_code_string.Format("%08X", static_cast<u32>(err)); |  | ||||||
|   m_message.Clear(); |  | ||||||
| 
 | 
 | ||||||
|   const DWORD r = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, m_error.win32, 0, m_message.GetWriteableCharArray(), |   WCHAR buf[128]; | ||||||
|                                  m_message.GetWritableBufferSize(), NULL); |   const DWORD r = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, err, LANG_USER_DEFAULT, buf, | ||||||
|  |                                  static_cast<DWORD>(std::size(buf)), nullptr); | ||||||
|   if (r > 0) |   if (r > 0) | ||||||
|   { |   { | ||||||
|     m_message.Resize(r); |     m_description = | ||||||
|     m_message.RStrip(); |       fmt::format("HRESULT {:08X}: {}", err, StringUtil::WideStringToUTF8String(std::wstring_view(buf, r))); | ||||||
|   } |   } | ||||||
|   else |   else | ||||||
|   { |   { | ||||||
|     m_message = "<Could not resolve system error ID>"; |     m_description = fmt::format("HRESULT {:08X}: <Could not resolve system error ID>", err); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void Error::SetHResult(Error* errptr, long err) | ||||||
|  | { | ||||||
|  |   if (errptr) | ||||||
|  |     errptr->SetHResult(err); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // constructors
 | void Error::SetSocket(int err) | ||||||
|  | { | ||||||
|  |   // Socket errors are win32 errors on windows
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  |   SetWin32(err); | ||||||
|  | #else | ||||||
|  |   SetErrno(err); | ||||||
|  | #endif | ||||||
|  |   m_type = Type::Socket; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Error::SetSocket(Error* errptr, int err) | ||||||
|  | { | ||||||
|  |   if (errptr) | ||||||
|  |     errptr->SetSocket(err); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Error Error::CreateNone() | Error Error::CreateNone() | ||||||
| { | { | ||||||
|   Error ret; |   return Error(); | ||||||
|   ret.Clear(); |  | ||||||
|   return ret; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Error Error::CreateErrno(int err) | Error Error::CreateErrno(int err) | ||||||
|  | @ -193,70 +151,10 @@ Error Error::CreateSocket(int err) | ||||||
|   return ret; |   return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Error Error::CreateMessage(const char* msg) | Error Error::CreateString(std::string description) | ||||||
| { | { | ||||||
|   Error ret; |   Error ret; | ||||||
|   ret.SetMessage(msg); |   ret.SetString(std::move(description)); | ||||||
|   return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Error Error::CreateUser(int err, const char* msg) |  | ||||||
| { |  | ||||||
|   Error ret; |  | ||||||
|   ret.SetUser(err, msg); |  | ||||||
|   return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Error Error::CreateUser(const char* code, const char* message) |  | ||||||
| { |  | ||||||
|   Error ret; |  | ||||||
|   ret.SetUser(code, message); |  | ||||||
|   return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Error Error::CreateMessageFormatted(const char* format, ...) |  | ||||||
| { |  | ||||||
|   std::va_list ap; |  | ||||||
|   va_start(ap, format); |  | ||||||
| 
 |  | ||||||
|   Error ret; |  | ||||||
|   ret.m_type = Type::User; |  | ||||||
|   ret.m_message.FormatVA(format, ap); |  | ||||||
| 
 |  | ||||||
|   va_end(ap); |  | ||||||
| 
 |  | ||||||
|   return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Error Error::CreateUserFormatted(int err, const char* format, ...) |  | ||||||
| { |  | ||||||
|   std::va_list ap; |  | ||||||
|   va_start(ap, format); |  | ||||||
| 
 |  | ||||||
|   Error ret; |  | ||||||
|   ret.m_type = Type::User; |  | ||||||
|   ret.m_error.user = err; |  | ||||||
|   ret.m_code_string.Format("%d", err); |  | ||||||
|   ret.m_message.FormatVA(format, ap); |  | ||||||
| 
 |  | ||||||
|   va_end(ap); |  | ||||||
| 
 |  | ||||||
|   return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Error Error::CreateUserFormatted(const char* code, const char* format, ...) |  | ||||||
| { |  | ||||||
|   std::va_list ap; |  | ||||||
|   va_start(ap, format); |  | ||||||
| 
 |  | ||||||
|   Error ret; |  | ||||||
|   ret.m_type = Type::User; |  | ||||||
|   ret.m_error.user = 0; |  | ||||||
|   ret.m_code_string = code; |  | ||||||
|   ret.m_message.FormatVA(format, ap); |  | ||||||
| 
 |  | ||||||
|   va_end(ap); |  | ||||||
| 
 |  | ||||||
|   return ret; |   return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -277,86 +175,16 @@ Error Error::CreateHResult(long err) | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| Error& Error::operator=(const Error& e) | Error& Error::operator=(const Error& e) = default; | ||||||
| { | 
 | ||||||
|   m_type = e.m_type; | Error& Error::operator=(Error&& e) = default; | ||||||
|   std::memcpy(&m_error, &e.m_error, sizeof(m_error)); |  | ||||||
|   m_code_string.Clear(); |  | ||||||
|   m_code_string.AppendString(e.m_code_string); |  | ||||||
|   m_message.Clear(); |  | ||||||
|   m_message.AppendString(e.m_message); |  | ||||||
|   return *this; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| bool Error::operator==(const Error& e) const | bool Error::operator==(const Error& e) const | ||||||
| { | { | ||||||
|   switch (m_type) |   return (m_type == e.m_type && m_description == e.m_description); | ||||||
|   { |  | ||||||
|     case Type::None: |  | ||||||
|       return true; |  | ||||||
| 
 |  | ||||||
|     case Type::Errno: |  | ||||||
|       return m_error.errno_f == e.m_error.errno_f; |  | ||||||
| 
 |  | ||||||
|     case Type::Socket: |  | ||||||
|       return m_error.socketerr == e.m_error.socketerr; |  | ||||||
| 
 |  | ||||||
|     case Type::User: |  | ||||||
|       return m_error.user == e.m_error.user; |  | ||||||
| 
 |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     case Type::Win32: |  | ||||||
|       return m_error.win32 == e.m_error.win32; |  | ||||||
| 
 |  | ||||||
|     case Type::HResult: |  | ||||||
|       return m_error.hresult == e.m_error.hresult; |  | ||||||
| #endif |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return false; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool Error::operator!=(const Error& e) const | bool Error::operator!=(const Error& e) const | ||||||
| { | { | ||||||
|   switch (m_type) |   return (m_type != e.m_type || m_description != e.m_description); | ||||||
|   { | } | ||||||
|     case Type::None: |  | ||||||
|       return false; |  | ||||||
| 
 |  | ||||||
|     case Type::Errno: |  | ||||||
|       return m_error.errno_f != e.m_error.errno_f; |  | ||||||
| 
 |  | ||||||
|     case Type::Socket: |  | ||||||
|       return m_error.socketerr != e.m_error.socketerr; |  | ||||||
| 
 |  | ||||||
|     case Type::User: |  | ||||||
|       return m_error.user != e.m_error.user; |  | ||||||
| 
 |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     case Type::Win32: |  | ||||||
|       return m_error.win32 != e.m_error.win32; |  | ||||||
| 
 |  | ||||||
|     case Type::HResult: |  | ||||||
|       return m_error.hresult != e.m_error.hresult; |  | ||||||
| #endif |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| SmallString Error::GetCodeAndMessage() const |  | ||||||
| { |  | ||||||
|   SmallString ret; |  | ||||||
|   GetCodeAndMessage(ret); |  | ||||||
|   return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Error::GetCodeAndMessage(String& dest) const |  | ||||||
| { |  | ||||||
|   if (m_code_string.IsEmpty()) |  | ||||||
|     dest.Assign(m_message); |  | ||||||
|   else |  | ||||||
|     dest.Format("[%s]: %s", m_code_string.GetCharArray(), m_message.GetCharArray()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } // namespace Common
 |  | ||||||
|  | @ -1,100 +1,75 @@ | ||||||
| // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
 | // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
 | ||||||
| // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| #include "string.h" | 
 | ||||||
| #include "types.h" | #include "types.h" | ||||||
| 
 | 
 | ||||||
| namespace Common { | #include <string> | ||||||
| 
 | 
 | ||||||
| // this class provides enough storage room for all of these types
 |  | ||||||
| class Error | class Error | ||||||
| { | { | ||||||
| public: | public: | ||||||
|   Error(); |   Error(); | ||||||
|   Error(const Error& e); |   Error(const Error& e); | ||||||
|  |   Error(Error&& e); | ||||||
|   ~Error(); |   ~Error(); | ||||||
| 
 | 
 | ||||||
|   enum class Type |   enum class Type | ||||||
|   { |   { | ||||||
|     None = 0,   // Set by default constructor, returns 'No Error'.
 |     None = 0, | ||||||
|     Errno = 1,  // Error that is set by system functions, such as open().
 |     Errno = 1, | ||||||
|     Socket = 2, // Error that is set by socket functions, such as socket(). On Unix this is the same as errno.
 |     Socket = 2, | ||||||
|     User = 3,   // When translated, will return 'User Error %u' if no message is specified.
 |     User = 3, | ||||||
|     Win32 = 4,  // Error that is returned by some Win32 functions, such as RegOpenKeyEx. Also used by other APIs through
 |     Win32 = 4, | ||||||
|                // GetLastError().
 |     HResult = 5, | ||||||
|     HResult = 5, // Error that is returned by Win32 COM methods, e.g. S_OK.
 |  | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   ALWAYS_INLINE Type GetType() const { return m_type; } |   ALWAYS_INLINE Type GetType() const { return m_type; } | ||||||
|   ALWAYS_INLINE int GetErrnoCode() const { return m_error.errno_f; } |   ALWAYS_INLINE const std::string& GetDescription() const { return m_description; } | ||||||
|   ALWAYS_INLINE int GetSocketCode() const { return m_error.socketerr; } |  | ||||||
|   ALWAYS_INLINE int GetUserCode() const { return m_error.user; } |  | ||||||
| #ifdef _WIN32 |  | ||||||
|   ALWAYS_INLINE unsigned long GetWin32Code() const { return m_error.win32; } |  | ||||||
|   ALWAYS_INLINE long GetHResultCode() const { return m_error.hresult; } |  | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
|   // get code, e.g. "0x00000002"
 |  | ||||||
|   ALWAYS_INLINE const String& GetCodeString() const { return m_code_string; } |  | ||||||
| 
 |  | ||||||
|   // get description, e.g. "File not Found"
 |  | ||||||
|   ALWAYS_INLINE const String& GetMessage() const { return m_message; } |  | ||||||
| 
 |  | ||||||
|   // setter functions
 |  | ||||||
|   void Clear(); |   void Clear(); | ||||||
|  | 
 | ||||||
|  |   /// Error that is set by system functions, such as open().
 | ||||||
|   void SetErrno(int err); |   void SetErrno(int err); | ||||||
|  | 
 | ||||||
|  |   /// Error that is set by socket functions, such as socket(). On Unix this is the same as errno.
 | ||||||
|   void SetSocket(int err); |   void SetSocket(int err); | ||||||
|   void SetMessage(const char* msg); | 
 | ||||||
|   void SetFormattedMessage(const char* format, ...) printflike(2, 3); |   /// Set both description and message.
 | ||||||
|   void SetUser(int err, const char* msg); |   void SetString(std::string description); | ||||||
|   void SetUser(const char* code, const char* message); | 
 | ||||||
|   void SetUserFormatted(int err, const char* format, ...) printflike(3, 4); |  | ||||||
|   void SetUserFormatted(const char* code, const char* format, ...) printflike(3, 4); |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|  |   /// Error that is returned by some Win32 functions, such as RegOpenKeyEx. Also used by other APIs through
 | ||||||
|  |   /// GetLastError().
 | ||||||
|   void SetWin32(unsigned long err); |   void SetWin32(unsigned long err); | ||||||
|  | 
 | ||||||
|  |   /// Error that is returned by Win32 COM methods, e.g. S_OK.
 | ||||||
|   void SetHResult(long err); |   void SetHResult(long err); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|   // constructors
 |  | ||||||
|   static Error CreateNone(); |   static Error CreateNone(); | ||||||
|   static Error CreateErrno(int err); |   static Error CreateErrno(int err); | ||||||
|   static Error CreateSocket(int err); |   static Error CreateSocket(int err); | ||||||
|   static Error CreateMessage(const char* msg); |   static Error CreateString(std::string description); | ||||||
|   static Error CreateMessageFormatted(const char* format, ...) printflike(1, 2); |  | ||||||
|   static Error CreateUser(int err, const char* msg); |  | ||||||
|   static Error CreateUser(const char* code, const char* message); |  | ||||||
|   static Error CreateUserFormatted(int err, const char* format, ...) printflike(2, 3); |  | ||||||
|   static Error CreateUserFormatted(const char* code, const char* format, ...) printflike(2, 3); |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|   static Error CreateWin32(unsigned long err); |   static Error CreateWin32(unsigned long err); | ||||||
|   static Error CreateHResult(long err); |   static Error CreateHResult(long err); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|   // get code and description, e.g. "[0x00000002]: File not Found"
 |   // helpers for setting
 | ||||||
|   SmallString GetCodeAndMessage() const; |   static void SetErrno(Error* errptr, int err); | ||||||
|   void GetCodeAndMessage(String& dest) const; |   static void SetSocket(Error* errptr, int err); | ||||||
|  |   static void SetString(Error* errptr, std::string description); | ||||||
|  |   static void SetWin32(Error* errptr, unsigned long err); | ||||||
|  |   static void SetHResult(Error* errptr, long err); | ||||||
| 
 | 
 | ||||||
|   // operators
 |  | ||||||
|   Error& operator=(const Error& e); |   Error& operator=(const Error& e); | ||||||
|  |   Error& operator=(Error&& e); | ||||||
|   bool operator==(const Error& e) const; |   bool operator==(const Error& e) const; | ||||||
|   bool operator!=(const Error& e) const; |   bool operator!=(const Error& e) const; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|   Type m_type = Type::None; |   Type m_type = Type::None; | ||||||
|   union |   std::string m_description; | ||||||
|   { |  | ||||||
|     int none; |  | ||||||
|     int errno_f; // renamed from errno to avoid conflicts with #define'd errnos.
 |  | ||||||
|     int socketerr; |  | ||||||
|     int user; |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     unsigned long win32; |  | ||||||
|     long hresult; |  | ||||||
| #endif |  | ||||||
|   } m_error{}; |  | ||||||
|   StackString<16> m_code_string; |  | ||||||
|   TinyString m_message; |  | ||||||
| }; | }; | ||||||
| 
 |  | ||||||
| } // namespace Common
 |  | ||||||
|  |  | ||||||
|  | @ -3,9 +3,11 @@ | ||||||
| 
 | 
 | ||||||
| #include "file_system.h" | #include "file_system.h" | ||||||
| #include "assert.h" | #include "assert.h" | ||||||
|  | #include "error.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
| #include "path.h" | #include "path.h" | ||||||
| #include "string_util.h" | #include "string_util.h" | ||||||
|  | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <cstdlib> | #include <cstdlib> | ||||||
| #include <cstring> | #include <cstring> | ||||||
|  | @ -587,7 +589,7 @@ std::string Path::Combine(const std::string_view& base, const std::string_view& | ||||||
|   return ret; |   return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::FILE* FileSystem::OpenCFile(const char* filename, const char* mode) | std::FILE* FileSystem::OpenCFile(const char* filename, const char* mode, Error* error) | ||||||
| { | { | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|   const std::wstring wfilename(StringUtil::UTF8StringToWideString(filename)); |   const std::wstring wfilename(StringUtil::UTF8StringToWideString(filename)); | ||||||
|  | @ -595,23 +597,34 @@ std::FILE* FileSystem::OpenCFile(const char* filename, const char* mode) | ||||||
|   if (!wfilename.empty() && !wmode.empty()) |   if (!wfilename.empty() && !wmode.empty()) | ||||||
|   { |   { | ||||||
|     std::FILE* fp; |     std::FILE* fp; | ||||||
|     if (_wfopen_s(&fp, wfilename.c_str(), wmode.c_str()) != 0) |     const errno_t err = _wfopen_s(&fp, wfilename.c_str(), wmode.c_str()); | ||||||
|  |     if (err != 0) | ||||||
|  |     { | ||||||
|  |       Error::SetErrno(error, err); | ||||||
|       return nullptr; |       return nullptr; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return fp; |     return fp; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   std::FILE* fp; |   std::FILE* fp; | ||||||
|   if (fopen_s(&fp, filename, mode) != 0) |   const errno_t err = fopen_s(&fp, filename, mode); | ||||||
|  |   if (err != 0) | ||||||
|  |   { | ||||||
|  |     Error::SetErrno(error, err); | ||||||
|     return nullptr; |     return nullptr; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   return fp; |   return fp; | ||||||
| #else | #else | ||||||
|   return std::fopen(filename, mode); |   std::FILE* fp = std::fopen(filename, mode); | ||||||
|  |   if (!fp) | ||||||
|  |     Error::SetErrno(error, errno); | ||||||
|  |   return fp; | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int FileSystem::OpenFDFile(const char* filename, int flags, int mode) | int FileSystem::OpenFDFile(const char* filename, int flags, int mode, Error* error) | ||||||
| { | { | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|   const std::wstring wfilename(StringUtil::UTF8StringToWideString(filename)); |   const std::wstring wfilename(StringUtil::UTF8StringToWideString(filename)); | ||||||
|  | @ -620,18 +633,21 @@ int FileSystem::OpenFDFile(const char* filename, int flags, int mode) | ||||||
| 
 | 
 | ||||||
|   return -1; |   return -1; | ||||||
| #else | #else | ||||||
|   return open(filename, flags, mode); |   const int fd = open(filename, flags, mode); | ||||||
|  |   if (fd < 0) | ||||||
|  |     Error::SetErrno(error, errno); | ||||||
|  |   return fd; | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| FileSystem::ManagedCFilePtr FileSystem::OpenManagedCFile(const char* filename, const char* mode) | FileSystem::ManagedCFilePtr FileSystem::OpenManagedCFile(const char* filename, const char* mode, Error* error) | ||||||
| { | { | ||||||
|   return ManagedCFilePtr(OpenCFile(filename, mode), [](std::FILE* fp) { std::fclose(fp); }); |   return ManagedCFilePtr(OpenCFile(filename, mode, error)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::FILE* FileSystem::OpenSharedCFile(const char* filename, const char* mode, FileShareMode share_mode) | std::FILE* FileSystem::OpenSharedCFile(const char* filename, const char* mode, FileShareMode share_mode, Error* error) | ||||||
| { | { | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|   const std::wstring wfilename(StringUtil::UTF8StringToWideString(filename)); |   const std::wstring wfilename(StringUtil::UTF8StringToWideString(filename)); | ||||||
|  | @ -661,16 +677,20 @@ std::FILE* FileSystem::OpenSharedCFile(const char* filename, const char* mode, F | ||||||
|   if (fp) |   if (fp) | ||||||
|     return fp; |     return fp; | ||||||
| 
 | 
 | ||||||
|  |   Error::SetErrno(error, errno); | ||||||
|   return nullptr; |   return nullptr; | ||||||
| #else | #else | ||||||
|   return std::fopen(filename, mode); |   std::FILE* fp = std::fopen(filename, mode); | ||||||
|  |   if (!fp) | ||||||
|  |     Error::SetErrno(error, errno); | ||||||
|  |   return fp; | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FileSystem::ManagedCFilePtr FileSystem::OpenManagedSharedCFile(const char* filename, const char* mode, | FileSystem::ManagedCFilePtr FileSystem::OpenManagedSharedCFile(const char* filename, const char* mode, | ||||||
|                                                                FileShareMode share_mode) |                                                                FileShareMode share_mode, Error* error) | ||||||
| { | { | ||||||
|   return ManagedCFilePtr(OpenSharedCFile(filename, mode, share_mode), [](std::FILE* fp) { std::fclose(fp); }); |   return ManagedCFilePtr(OpenSharedCFile(filename, mode, share_mode, error)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int FileSystem::FSeek64(std::FILE* fp, s64 offset, int whence) | int FileSystem::FSeek64(std::FILE* fp, s64 offset, int whence) | ||||||
|  |  | ||||||
|  | @ -1,8 +1,10 @@ | ||||||
| // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
 | // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
 | ||||||
| // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include "types.h" | #include "types.h" | ||||||
|  | 
 | ||||||
| #include <cstdio> | #include <cstdio> | ||||||
| #include <ctime> | #include <ctime> | ||||||
| #include <memory> | #include <memory> | ||||||
|  | @ -11,6 +13,8 @@ | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
|  | class Error; | ||||||
|  | 
 | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| #define FS_OSPATH_SEPARATOR_CHARACTER '\\' | #define FS_OSPATH_SEPARATOR_CHARACTER '\\' | ||||||
| #define FS_OSPATH_SEPARATOR_STR "\\" | #define FS_OSPATH_SEPARATOR_STR "\\" | ||||||
|  | @ -87,15 +91,25 @@ bool DeleteFile(const char* path); | ||||||
| /// Rename file
 | /// Rename file
 | ||||||
| bool RenamePath(const char* OldPath, const char* NewPath); | bool RenamePath(const char* OldPath, const char* NewPath); | ||||||
| 
 | 
 | ||||||
|  | /// Deleter functor for managed file pointers
 | ||||||
|  | struct FileDeleter | ||||||
|  | { | ||||||
|  |   ALWAYS_INLINE void operator()(std::FILE* fp) | ||||||
|  |   { | ||||||
|  |     if (fp) | ||||||
|  |       std::fclose(fp); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /// open files
 | /// open files
 | ||||||
| using ManagedCFilePtr = std::unique_ptr<std::FILE, void (*)(std::FILE*)>; | using ManagedCFilePtr = std::unique_ptr<std::FILE, FileDeleter>; | ||||||
| ManagedCFilePtr OpenManagedCFile(const char* filename, const char* mode); | ManagedCFilePtr OpenManagedCFile(const char* filename, const char* mode, Error* error = nullptr); | ||||||
| std::FILE* OpenCFile(const char* filename, const char* mode); | std::FILE* OpenCFile(const char* filename, const char* mode, Error* error = nullptr); | ||||||
| int FSeek64(std::FILE* fp, s64 offset, int whence); | int FSeek64(std::FILE* fp, s64 offset, int whence); | ||||||
| s64 FTell64(std::FILE* fp); | s64 FTell64(std::FILE* fp); | ||||||
| s64 FSize64(std::FILE* fp); | s64 FSize64(std::FILE* fp); | ||||||
| 
 | 
 | ||||||
| int OpenFDFile(const char* filename, int flags, int mode); | int OpenFDFile(const char* filename, int flags, int mode, Error* error = nullptr); | ||||||
| 
 | 
 | ||||||
| /// Sharing modes for OpenSharedCFile().
 | /// Sharing modes for OpenSharedCFile().
 | ||||||
| enum class FileShareMode | enum class FileShareMode | ||||||
|  | @ -108,8 +122,9 @@ enum class FileShareMode | ||||||
| 
 | 
 | ||||||
| /// Opens a file in shareable mode (where other processes can access it concurrently).
 | /// Opens a file in shareable mode (where other processes can access it concurrently).
 | ||||||
| /// Only has an effect on Windows systems.
 | /// Only has an effect on Windows systems.
 | ||||||
| ManagedCFilePtr OpenManagedSharedCFile(const char* filename, const char* mode, FileShareMode share_mode); | ManagedCFilePtr OpenManagedSharedCFile(const char* filename, const char* mode, FileShareMode share_mode, | ||||||
| std::FILE* OpenSharedCFile(const char* filename, const char* mode, FileShareMode share_mode); |                                        Error* error = nullptr); | ||||||
|  | std::FILE* OpenSharedCFile(const char* filename, const char* mode, FileShareMode share_mode, Error* error = nullptr); | ||||||
| 
 | 
 | ||||||
| /// Abstracts a POSIX file lock.
 | /// Abstracts a POSIX file lock.
 | ||||||
| #ifndef _WIN32 | #ifndef _WIN32 | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ static s32 GetFreeFileHandle() | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const s32 index = static_cast<s32>(s_files.size()); |   const s32 index = static_cast<s32>(s_files.size()); | ||||||
|   s_files.emplace_back(nullptr, [](std::FILE*) {}); |   s_files.emplace_back(nullptr, FileSystem::FileDeleter()); | ||||||
|   return index; |   return index; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1142,7 +1142,7 @@ bool System::BootSystem(SystemBootParameters parameters) | ||||||
|   Host::OnSystemStarting(); |   Host::OnSystemStarting(); | ||||||
| 
 | 
 | ||||||
|   // Load CD image up and detect region.
 |   // Load CD image up and detect region.
 | ||||||
|   Common::Error error; |   Error error; | ||||||
|   std::unique_ptr<CDImage> disc; |   std::unique_ptr<CDImage> disc; | ||||||
|   DiscRegion disc_region = DiscRegion::NonPS1; |   DiscRegion disc_region = DiscRegion::NonPS1; | ||||||
|   std::string exe_boot; |   std::string exe_boot; | ||||||
|  | @ -1172,7 +1172,7 @@ bool System::BootSystem(SystemBootParameters parameters) | ||||||
|       if (!disc) |       if (!disc) | ||||||
|       { |       { | ||||||
|         Host::ReportErrorAsync("Error", fmt::format("Failed to load CD image '{}': {}", |         Host::ReportErrorAsync("Error", fmt::format("Failed to load CD image '{}': {}", | ||||||
|                                                     Path::GetFileName(parameters.filename), error.GetCodeAndMessage())); |                                                     Path::GetFileName(parameters.filename), error.GetDescription())); | ||||||
|         s_state = State::Shutdown; |         s_state = State::Shutdown; | ||||||
|         Host::OnSystemDestroyed(); |         Host::OnSystemDestroyed(); | ||||||
|         return false; |         return false; | ||||||
|  | @ -1209,9 +1209,9 @@ bool System::BootSystem(SystemBootParameters parameters) | ||||||
|   // Switch subimage.
 |   // Switch subimage.
 | ||||||
|   if (disc && parameters.media_playlist_index != 0 && !disc->SwitchSubImage(parameters.media_playlist_index, &error)) |   if (disc && parameters.media_playlist_index != 0 && !disc->SwitchSubImage(parameters.media_playlist_index, &error)) | ||||||
|   { |   { | ||||||
|     Host::ReportFormattedErrorAsync("Error", "Failed to switch to subimage %u in '%s': %s", |     Host::ReportErrorAsync("Error", | ||||||
|                                     parameters.media_playlist_index, parameters.filename.c_str(), |                            fmt::format("Failed to switch to subimage {] in '{}': {}", parameters.media_playlist_index, | ||||||
|                                     error.GetCodeAndMessage().GetCharArray()); |                                        parameters.filename, error.GetDescription())); | ||||||
|     s_state = State::Shutdown; |     s_state = State::Shutdown; | ||||||
|     Host::OnSystemDestroyed(); |     Host::OnSystemDestroyed(); | ||||||
|     return false; |     return false; | ||||||
|  | @ -2137,7 +2137,7 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Common::Error error; |   Error error; | ||||||
|   std::string media_filename; |   std::string media_filename; | ||||||
|   std::unique_ptr<CDImage> media; |   std::unique_ptr<CDImage> media; | ||||||
|   if (header.media_filename_length > 0) |   if (header.media_filename_length > 0) | ||||||
|  | @ -2166,7 +2166,7 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u | ||||||
|             30.0f, |             30.0f, | ||||||
|             Host::TranslateString("OSDMessage", "Failed to open CD image from save state '%s': %s. Using " |             Host::TranslateString("OSDMessage", "Failed to open CD image from save state '%s': %s. Using " | ||||||
|                                                 "existing image '%s', this may result in instability."), |                                                 "existing image '%s', this may result in instability."), | ||||||
|             media_filename.c_str(), error.GetCodeAndMessage().GetCharArray(), old_media->GetFileName().c_str()); |             media_filename.c_str(), error.GetDescription().c_str(), old_media->GetFileName().c_str()); | ||||||
|           media = std::move(old_media); |           media = std::move(old_media); | ||||||
|           header.media_subimage_index = media->GetCurrentSubImage(); |           header.media_subimage_index = media->GetCurrentSubImage(); | ||||||
|         } |         } | ||||||
|  | @ -2174,7 +2174,7 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u | ||||||
|         { |         { | ||||||
|           Host::ReportFormattedErrorAsync( |           Host::ReportFormattedErrorAsync( | ||||||
|             "Error", Host::TranslateString("System", "Failed to open CD image '%s' used by save state: %s."), |             "Error", Host::TranslateString("System", "Failed to open CD image '%s' used by save state: %s."), | ||||||
|             media_filename.c_str(), error.GetCodeAndMessage().GetCharArray()); |             media_filename.c_str(), error.GetDescription().c_str()); | ||||||
|           return false; |           return false; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  | @ -2193,7 +2193,7 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u | ||||||
|       Host::ReportFormattedErrorAsync( |       Host::ReportFormattedErrorAsync( | ||||||
|         "Error", |         "Error", | ||||||
|         Host::TranslateString("System", "Failed to switch to subimage %u in CD image '%s' used by save state: %s."), |         Host::TranslateString("System", "Failed to switch to subimage %u in CD image '%s' used by save state: %s."), | ||||||
|         header.media_subimage_index + 1u, media_filename.c_str(), error.GetCodeAndMessage().GetCharArray()); |         header.media_subimage_index + 1u, media_filename.c_str(), error.GetDescription().c_str()); | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|  | @ -3081,12 +3081,12 @@ std::string System::GetMediaFileName() | ||||||
| 
 | 
 | ||||||
| bool System::InsertMedia(const char* path) | bool System::InsertMedia(const char* path) | ||||||
| { | { | ||||||
|   Common::Error error; |   Error error; | ||||||
|   std::unique_ptr<CDImage> image = CDImage::Open(path, g_settings.cdrom_load_image_patches, &error); |   std::unique_ptr<CDImage> image = CDImage::Open(path, g_settings.cdrom_load_image_patches, &error); | ||||||
|   if (!image) |   if (!image) | ||||||
|   { |   { | ||||||
|     Host::AddFormattedOSDMessage(10.0f, Host::TranslateString("OSDMessage", "Failed to open disc image '%s': %s."), |     Host::AddFormattedOSDMessage(10.0f, Host::TranslateString("OSDMessage", "Failed to open disc image '%s': %s."), | ||||||
|                                  path, error.GetCodeAndMessage().GetCharArray()); |                                  path, error.GetDescription().c_str()); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -3272,12 +3272,12 @@ bool System::SwitchMediaSubImage(u32 index) | ||||||
|   std::unique_ptr<CDImage> image = CDROM::RemoveMedia(true); |   std::unique_ptr<CDImage> image = CDROM::RemoveMedia(true); | ||||||
|   Assert(image); |   Assert(image); | ||||||
| 
 | 
 | ||||||
|   Common::Error error; |   Error error; | ||||||
|   if (!image->SwitchSubImage(index, &error)) |   if (!image->SwitchSubImage(index, &error)) | ||||||
|   { |   { | ||||||
|     Host::AddFormattedOSDMessage(10.0f, |     Host::AddFormattedOSDMessage(10.0f, | ||||||
|                                  Host::TranslateString("OSDMessage", "Failed to switch to subimage %u in '%s': %s."), |                                  Host::TranslateString("OSDMessage", "Failed to switch to subimage %u in '%s': %s."), | ||||||
|                                  index + 1u, image->GetFileName().c_str(), error.GetCodeAndMessage().GetCharArray()); |                                  index + 1u, image->GetFileName().c_str(), error.GetDescription().c_str()); | ||||||
| 
 | 
 | ||||||
|     const DiscRegion region = GetRegionForImage(image.get()); |     const DiscRegion region = GetRegionForImage(image.get()); | ||||||
|     CDROM::InsertMedia(std::move(image), region); |     CDROM::InsertMedia(std::move(image), region); | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ u32 CDImage::GetBytesPerSector(TrackMode mode) | ||||||
|   return sizes[static_cast<u32>(mode)]; |   return sizes[static_cast<u32>(mode)]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<CDImage> CDImage::Open(const char* filename, bool allow_patches, Common::Error* error) | std::unique_ptr<CDImage> CDImage::Open(const char* filename, bool allow_patches, Error* error) | ||||||
| { | { | ||||||
|   const char* extension; |   const char* extension; | ||||||
| 
 | 
 | ||||||
|  | @ -94,10 +94,7 @@ std::unique_ptr<CDImage> CDImage::Open(const char* filename, bool allow_patches, | ||||||
|     { |     { | ||||||
|       image = CDImage::OverlayPPFPatch(ppf_filename.c_str(), std::move(image)); |       image = CDImage::OverlayPPFPatch(ppf_filename.c_str(), std::move(image)); | ||||||
|       if (!image) |       if (!image) | ||||||
|       { |         Error::SetString(error, fmt::format("Failed to apply ppf patch from '{}'.", ppf_filename)); | ||||||
|         if (error) |  | ||||||
|           error->SetFormattedMessage("Failed to apply ppf patch from '%s'.", ppf_filename.c_str()); |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -343,7 +340,7 @@ u32 CDImage::GetCurrentSubImage() const | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImage::SwitchSubImage(u32 index, Common::Error* error) | bool CDImage::SwitchSubImage(u32 index, Error* error) | ||||||
| { | { | ||||||
|   return false; |   return false; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,9 +11,7 @@ | ||||||
| #include <tuple> | #include <tuple> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
| namespace Common { |  | ||||||
| class Error; | class Error; | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| class CDImage | class CDImage | ||||||
| { | { | ||||||
|  | @ -220,15 +218,15 @@ public: | ||||||
|   static bool IsDeviceName(const char* filename); |   static bool IsDeviceName(const char* filename); | ||||||
| 
 | 
 | ||||||
|   // Opening disc image.
 |   // Opening disc image.
 | ||||||
|   static std::unique_ptr<CDImage> Open(const char* filename, bool allow_patches, Common::Error* error); |   static std::unique_ptr<CDImage> Open(const char* filename, bool allow_patches, Error* error); | ||||||
|   static std::unique_ptr<CDImage> OpenBinImage(const char* filename, Common::Error* error); |   static std::unique_ptr<CDImage> OpenBinImage(const char* filename, Error* error); | ||||||
|   static std::unique_ptr<CDImage> OpenCueSheetImage(const char* filename, Common::Error* error); |   static std::unique_ptr<CDImage> OpenCueSheetImage(const char* filename, Error* error); | ||||||
|   static std::unique_ptr<CDImage> OpenCHDImage(const char* filename, Common::Error* error); |   static std::unique_ptr<CDImage> OpenCHDImage(const char* filename, Error* error); | ||||||
|   static std::unique_ptr<CDImage> OpenEcmImage(const char* filename, Common::Error* error); |   static std::unique_ptr<CDImage> OpenEcmImage(const char* filename, Error* error); | ||||||
|   static std::unique_ptr<CDImage> OpenMdsImage(const char* filename, Common::Error* error); |   static std::unique_ptr<CDImage> OpenMdsImage(const char* filename, Error* error); | ||||||
|   static std::unique_ptr<CDImage> OpenPBPImage(const char* filename, Common::Error* error); |   static std::unique_ptr<CDImage> OpenPBPImage(const char* filename, Error* error); | ||||||
|   static std::unique_ptr<CDImage> OpenM3uImage(const char* filename, bool apply_patches, Common::Error* error); |   static std::unique_ptr<CDImage> OpenM3uImage(const char* filename, bool apply_patches, Error* error); | ||||||
|   static std::unique_ptr<CDImage> OpenDeviceImage(const char* filename, Common::Error* error); |   static std::unique_ptr<CDImage> OpenDeviceImage(const char* filename, Error* error); | ||||||
|   static std::unique_ptr<CDImage> |   static std::unique_ptr<CDImage> | ||||||
|   CreateMemoryImage(CDImage* image, ProgressCallback* progress = ProgressCallback::NullProgressCallback); |   CreateMemoryImage(CDImage* image, ProgressCallback* progress = ProgressCallback::NullProgressCallback); | ||||||
|   static std::unique_ptr<CDImage> OverlayPPFPatch(const char* filename, std::unique_ptr<CDImage> parent_image, |   static std::unique_ptr<CDImage> OverlayPPFPatch(const char* filename, std::unique_ptr<CDImage> parent_image, | ||||||
|  | @ -341,7 +339,7 @@ public: | ||||||
|   virtual u32 GetCurrentSubImage() const; |   virtual u32 GetCurrentSubImage() const; | ||||||
| 
 | 
 | ||||||
|   // Changes the current sub-image. If this fails, the image state is unchanged.
 |   // Changes the current sub-image. If this fails, the image state is unchanged.
 | ||||||
|   virtual bool SwitchSubImage(u32 index, Common::Error* error); |   virtual bool SwitchSubImage(u32 index, Error* error); | ||||||
| 
 | 
 | ||||||
|   // Retrieve sub-image metadata.
 |   // Retrieve sub-image metadata.
 | ||||||
|   virtual std::string GetSubImageMetadata(u32 index, const std::string_view& type) const; |   virtual std::string GetSubImageMetadata(u32 index, const std::string_view& type) const; | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ public: | ||||||
|   CDImageBin(); |   CDImageBin(); | ||||||
|   ~CDImageBin() override; |   ~CDImageBin() override; | ||||||
| 
 | 
 | ||||||
|   bool Open(const char* filename, Common::Error* error); |   bool Open(const char* filename, Error* error); | ||||||
| 
 | 
 | ||||||
|   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; |   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; | ||||||
|   bool HasNonStandardSubchannel() const override; |   bool HasNonStandardSubchannel() const override; | ||||||
|  | @ -38,7 +38,7 @@ CDImageBin::~CDImageBin() | ||||||
|     std::fclose(m_fp); |     std::fclose(m_fp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImageBin::Open(const char* filename, Common::Error* error) | bool CDImageBin::Open(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   m_filename = filename; |   m_filename = filename; | ||||||
|   m_fp = FileSystem::OpenCFile(filename, "rb"); |   m_fp = FileSystem::OpenCFile(filename, "rb"); | ||||||
|  | @ -136,7 +136,7 @@ bool CDImageBin::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_i | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<CDImage> CDImage::OpenBinImage(const char* filename, Common::Error* error) | std::unique_ptr<CDImage> CDImage::OpenBinImage(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   std::unique_ptr<CDImageBin> image = std::make_unique<CDImageBin>(); |   std::unique_ptr<CDImageBin> image = std::make_unique<CDImageBin>(); | ||||||
|   if (!image->Open(filename, error)) |   if (!image->Open(filename, error)) | ||||||
|  |  | ||||||
|  | @ -59,7 +59,7 @@ public: | ||||||
|   CDImageCHD(); |   CDImageCHD(); | ||||||
|   ~CDImageCHD() override; |   ~CDImageCHD() override; | ||||||
| 
 | 
 | ||||||
|   bool Open(const char* filename, Common::Error* error); |   bool Open(const char* filename, Error* error); | ||||||
| 
 | 
 | ||||||
|   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; |   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; | ||||||
|   bool HasNonStandardSubchannel() const override; |   bool HasNonStandardSubchannel() const override; | ||||||
|  | @ -77,7 +77,7 @@ private: | ||||||
|     MAX_PARENTS = 32 // Surely someone wouldn't be insane enough to go beyond this...
 |     MAX_PARENTS = 32 // Surely someone wouldn't be insane enough to go beyond this...
 | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   chd_file* OpenCHD(const char* filename, FileSystem::ManagedCFilePtr fp, Common::Error* error, u32 recursion_level); |   chd_file* OpenCHD(const char* filename, FileSystem::ManagedCFilePtr fp, Error* error, u32 recursion_level); | ||||||
|   bool ReadHunk(u32 hunk_index); |   bool ReadHunk(u32 hunk_index); | ||||||
| 
 | 
 | ||||||
|   chd_file* m_chd = nullptr; |   chd_file* m_chd = nullptr; | ||||||
|  | @ -100,8 +100,7 @@ CDImageCHD::~CDImageCHD() | ||||||
|     chd_close(m_chd); |     chd_close(m_chd); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| chd_file* CDImageCHD::OpenCHD(const char* filename, FileSystem::ManagedCFilePtr fp, Common::Error* error, | chd_file* CDImageCHD::OpenCHD(const char* filename, FileSystem::ManagedCFilePtr fp, Error* error, u32 recursion_level) | ||||||
|                               u32 recursion_level) |  | ||||||
| { | { | ||||||
|   chd_file* chd; |   chd_file* chd; | ||||||
|   chd_error err = chd_open_file(fp.get(), CHD_OPEN_READ | CHD_OPEN_TRANSFER_FILE, nullptr, &chd); |   chd_error err = chd_open_file(fp.get(), CHD_OPEN_READ | CHD_OPEN_TRANSFER_FILE, nullptr, &chd); | ||||||
|  | @ -114,16 +113,14 @@ chd_file* CDImageCHD::OpenCHD(const char* filename, FileSystem::ManagedCFilePtr | ||||||
|   else if (err != CHDERR_REQUIRES_PARENT) |   else if (err != CHDERR_REQUIRES_PARENT) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Failed to open CHD '%s': %s", filename, chd_error_string(err)); |     Log_ErrorPrintf("Failed to open CHD '%s': %s", filename, chd_error_string(err)); | ||||||
|     if (error) |     Error::SetString(error, chd_error_string(err)); | ||||||
|       error->SetMessage(chd_error_string(err)); |  | ||||||
|     return nullptr; |     return nullptr; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (recursion_level >= MAX_PARENTS) |   if (recursion_level >= MAX_PARENTS) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Failed to open CHD '%s': Too many parent files", filename); |     Log_ErrorPrintf("Failed to open CHD '%s': Too many parent files", filename); | ||||||
|     if (error) |     Error::SetString(error, "Too many parent files"); | ||||||
|       error->SetMessage("Too many parent files"); |  | ||||||
|     return nullptr; |     return nullptr; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -133,8 +130,7 @@ chd_file* CDImageCHD::OpenCHD(const char* filename, FileSystem::ManagedCFilePtr | ||||||
|   if (err != CHDERR_NONE) |   if (err != CHDERR_NONE) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Failed to read CHD header '%s': %s", filename, chd_error_string(err)); |     Log_ErrorPrintf("Failed to read CHD header '%s': %s", filename, chd_error_string(err)); | ||||||
|     if (error) |     Error::SetString(error, chd_error_string(err)); | ||||||
|       error->SetMessage(chd_error_string(err)); |  | ||||||
|     return nullptr; |     return nullptr; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -163,15 +159,16 @@ chd_file* CDImageCHD::OpenCHD(const char* filename, FileSystem::ManagedCFilePtr | ||||||
|     // Match! Open this one.
 |     // Match! Open this one.
 | ||||||
|     if ((parent_chd = OpenCHD(fd.FileName.c_str(), std::move(parent_fp), error, recursion_level + 1)) != nullptr) |     if ((parent_chd = OpenCHD(fd.FileName.c_str(), std::move(parent_fp), error, recursion_level + 1)) != nullptr) | ||||||
|     { |     { | ||||||
|       Log_DevPrintf(fmt::format("Found parent CHD '{}' for '{}'.", Path::GetFileName(fd.FileName), Path::GetFileName(filename)).c_str()); |       Log_DevPrintf( | ||||||
|  |         fmt::format("Found parent CHD '{}' for '{}'.", Path::GetFileName(fd.FileName), Path::GetFileName(filename)) | ||||||
|  |           .c_str()); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   if (!parent_chd) |   if (!parent_chd) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Failed to open CHD '%s': Failed to find parent CHD, it must be in the same directory.", filename); |     Log_ErrorPrintf("Failed to open CHD '%s': Failed to find parent CHD, it must be in the same directory.", filename); | ||||||
|     if (error) |     Error::SetString(error, "Failed to find parent CHD, it must be in the same directory."); | ||||||
|       error->SetMessage("Failed to find parent CHD, it must be in the same directory."); |  | ||||||
|     return nullptr; |     return nullptr; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -180,8 +177,7 @@ chd_file* CDImageCHD::OpenCHD(const char* filename, FileSystem::ManagedCFilePtr | ||||||
|   if (err != CHDERR_NONE) |   if (err != CHDERR_NONE) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Failed to open CHD '%s': %s", filename, chd_error_string(err)); |     Log_ErrorPrintf("Failed to open CHD '%s': %s", filename, chd_error_string(err)); | ||||||
|     if (error) |     Error::SetString(error, chd_error_string(err)); | ||||||
|       error->SetMessage(chd_error_string(err)); |  | ||||||
|     return nullptr; |     return nullptr; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -190,7 +186,7 @@ chd_file* CDImageCHD::OpenCHD(const char* filename, FileSystem::ManagedCFilePtr | ||||||
|   return chd; |   return chd; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImageCHD::Open(const char* filename, Common::Error* error) | bool CDImageCHD::Open(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   auto fp = FileSystem::OpenManagedSharedCFile(filename, "rb", FileSystem::FileShareMode::DenyWrite); |   auto fp = FileSystem::OpenManagedSharedCFile(filename, "rb", FileSystem::FileShareMode::DenyWrite); | ||||||
|   if (!fp) |   if (!fp) | ||||||
|  | @ -211,9 +207,8 @@ bool CDImageCHD::Open(const char* filename, Common::Error* error) | ||||||
|   if ((m_hunk_size % CHD_CD_SECTOR_DATA_SIZE) != 0) |   if ((m_hunk_size % CHD_CD_SECTOR_DATA_SIZE) != 0) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Hunk size (%u) is not a multiple of %u", m_hunk_size, CHD_CD_SECTOR_DATA_SIZE); |     Log_ErrorPrintf("Hunk size (%u) is not a multiple of %u", m_hunk_size, CHD_CD_SECTOR_DATA_SIZE); | ||||||
|     if (error) |     Error::SetString(error, fmt::format("Hunk size ({}) is not a multiple of {}", m_hunk_size, | ||||||
|       error->SetFormattedMessage("Hunk size (%u) is not a multiple of %u", m_hunk_size, CHD_CD_SECTOR_DATA_SIZE); |                                         static_cast<u32>(CHD_CD_SECTOR_DATA_SIZE))); | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -244,9 +239,7 @@ bool CDImageCHD::Open(const char* filename, Common::Error* error) | ||||||
|                       &pregap_frames, pgtype_str, pgsub_str, &postgap_frames) != 8) |                       &pregap_frames, pgtype_str, pgsub_str, &postgap_frames) != 8) | ||||||
|       { |       { | ||||||
|         Log_ErrorPrintf("Invalid track v2 metadata: '%s'", metadata_str); |         Log_ErrorPrintf("Invalid track v2 metadata: '%s'", metadata_str); | ||||||
|         if (error) |         Error::SetString(error, fmt::format("Invalid track v2 metadata: '{}'", metadata_str)); | ||||||
|           error->SetFormattedMessage("Invalid track v2 metadata: '%s'", metadata_str); |  | ||||||
| 
 |  | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | @ -264,9 +257,7 @@ bool CDImageCHD::Open(const char* filename, Common::Error* error) | ||||||
|       if (std::sscanf(metadata_str, CDROM_TRACK_METADATA_FORMAT, &track_num, type_str, subtype_str, &frames) != 4) |       if (std::sscanf(metadata_str, CDROM_TRACK_METADATA_FORMAT, &track_num, type_str, subtype_str, &frames) != 4) | ||||||
|       { |       { | ||||||
|         Log_ErrorPrintf("Invalid track metadata: '%s'", metadata_str); |         Log_ErrorPrintf("Invalid track metadata: '%s'", metadata_str); | ||||||
|         if (error) |         Error::SetString(error, fmt::format("Invalid track v2 metadata: '{}'", metadata_str)); | ||||||
|           error->SetFormattedMessage("Invalid track v2 metadata: '%s'", metadata_str); |  | ||||||
| 
 |  | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | @ -275,12 +266,8 @@ bool CDImageCHD::Open(const char* filename, Common::Error* error) | ||||||
|     { |     { | ||||||
|       Log_ErrorPrintf("Incorrect track number at index %d, expected %d got %d", num_tracks, (num_tracks + 1), |       Log_ErrorPrintf("Incorrect track number at index %d, expected %d got %d", num_tracks, (num_tracks + 1), | ||||||
|                       track_num); |                       track_num); | ||||||
|       if (error) |       Error::SetString(error, fmt::format("Incorrect track number at index {}, expected {} got {}", num_tracks, | ||||||
|       { |                                           (num_tracks + 1), track_num)); | ||||||
|         error->SetFormattedMessage("Incorrect track number at index %d, expected %d got %d", num_tracks, |  | ||||||
|                                    (num_tracks + 1), track_num); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -288,9 +275,7 @@ bool CDImageCHD::Open(const char* filename, Common::Error* error) | ||||||
|     if (!mode.has_value()) |     if (!mode.has_value()) | ||||||
|     { |     { | ||||||
|       Log_ErrorPrintf("Invalid track mode: '%s'", type_str); |       Log_ErrorPrintf("Invalid track mode: '%s'", type_str); | ||||||
|       if (error) |       Error::SetString(error, fmt::format("Invalid track mode: '{}'", type_str)); | ||||||
|         error->SetFormattedMessage("Invalid track mode: '%s'", type_str); |  | ||||||
| 
 |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -321,9 +306,7 @@ bool CDImageCHD::Open(const char* filename, Common::Error* error) | ||||||
|         if (pregap_frames > frames) |         if (pregap_frames > frames) | ||||||
|         { |         { | ||||||
|           Log_ErrorPrintf("Pregap length %u exceeds track length %u", pregap_frames, frames); |           Log_ErrorPrintf("Pregap length %u exceeds track length %u", pregap_frames, frames); | ||||||
|           if (error) |           Error::SetString(error, fmt::format("Pregap length {} exceeds track length {}", pregap_frames, frames)); | ||||||
|             error->SetFormattedMessage("Pregap length %u exceeds track length %u", pregap_frames, frames); |  | ||||||
| 
 |  | ||||||
|           return false; |           return false; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -368,9 +351,7 @@ bool CDImageCHD::Open(const char* filename, Common::Error* error) | ||||||
|   if (m_tracks.empty()) |   if (m_tracks.empty()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("File '%s' contains no tracks", filename); |     Log_ErrorPrintf("File '%s' contains no tracks", filename); | ||||||
|     if (error) |     Error::SetString(error, fmt::format("File '{}' contains no tracks", filename)); | ||||||
|       error->SetFormattedMessage("File '%s' contains no tracks", filename); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -497,7 +478,7 @@ bool CDImageCHD::ReadHunk(u32 hunk_index) | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<CDImage> CDImage::OpenCHDImage(const char* filename, Common::Error* error) | std::unique_ptr<CDImage> CDImage::OpenCHDImage(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   std::unique_ptr<CDImageCHD> image = std::make_unique<CDImageCHD>(); |   std::unique_ptr<CDImageCHD> image = std::make_unique<CDImageCHD>(); | ||||||
|   if (!image->Open(filename, error)) |   if (!image->Open(filename, error)) | ||||||
|  |  | ||||||
|  | @ -3,16 +3,21 @@ | ||||||
| 
 | 
 | ||||||
| #include "cd_image.h" | #include "cd_image.h" | ||||||
| #include "cd_subchannel_replacement.h" | #include "cd_subchannel_replacement.h" | ||||||
|  | #include "cue_parser.h" | ||||||
|  | 
 | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/error.h" | #include "common/error.h" | ||||||
| #include "common/file_system.h" | #include "common/file_system.h" | ||||||
| #include "common/log.h" | #include "common/log.h" | ||||||
| #include "common/path.h" | #include "common/path.h" | ||||||
| #include "cue_parser.h" | 
 | ||||||
|  | #include "fmt/format.h" | ||||||
|  | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <cerrno> | #include <cerrno> | ||||||
| #include <cinttypes> | #include <cinttypes> | ||||||
| #include <map> | #include <map> | ||||||
|  | 
 | ||||||
| Log_SetChannel(CDImageCueSheet); | Log_SetChannel(CDImageCueSheet); | ||||||
| 
 | 
 | ||||||
| class CDImageCueSheet : public CDImage | class CDImageCueSheet : public CDImage | ||||||
|  | @ -21,7 +26,7 @@ public: | ||||||
|   CDImageCueSheet(); |   CDImageCueSheet(); | ||||||
|   ~CDImageCueSheet() override; |   ~CDImageCueSheet() override; | ||||||
| 
 | 
 | ||||||
|   bool OpenAndParse(const char* filename, Common::Error* error); |   bool OpenAndParse(const char* filename, Error* error); | ||||||
| 
 | 
 | ||||||
|   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; |   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; | ||||||
|   bool HasNonStandardSubchannel() const override; |   bool HasNonStandardSubchannel() const override; | ||||||
|  | @ -48,7 +53,7 @@ CDImageCueSheet::~CDImageCueSheet() | ||||||
|   std::for_each(m_files.begin(), m_files.end(), [](TrackFile& t) { std::fclose(t.file); }); |   std::for_each(m_files.begin(), m_files.end(), [](TrackFile& t) { std::fclose(t.file); }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImageCueSheet::OpenAndParse(const char* filename, Common::Error* error) | bool CDImageCueSheet::OpenAndParse(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   std::FILE* fp = FileSystem::OpenCFile(filename, "rb"); |   std::FILE* fp = FileSystem::OpenCFile(filename, "rb"); | ||||||
|   if (!fp) |   if (!fp) | ||||||
|  | @ -94,7 +99,8 @@ bool CDImageCueSheet::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|     { |     { | ||||||
|       const std::string track_full_filename( |       const std::string track_full_filename( | ||||||
|         !Path::IsAbsolute(track_filename) ? Path::BuildRelativePath(m_filename, track_filename) : track_filename); |         !Path::IsAbsolute(track_filename) ? Path::BuildRelativePath(m_filename, track_filename) : track_filename); | ||||||
|       std::FILE* track_fp = FileSystem::OpenCFile(track_full_filename.c_str(), "rb"); |       Error track_error; | ||||||
|  |       std::FILE* track_fp = FileSystem::OpenCFile(track_full_filename.c_str(), "rb", &track_error); | ||||||
|       if (!track_fp && track_file_index == 0) |       if (!track_fp && track_file_index == 0) | ||||||
|       { |       { | ||||||
|         // many users have bad cuesheets, or they're renamed the files without updating the cuesheet.
 |         // many users have bad cuesheets, or they're renamed the files without updating the cuesheet.
 | ||||||
|  | @ -110,14 +116,11 @@ bool CDImageCueSheet::OpenAndParse(const char* filename, Common::Error* error) | ||||||
| 
 | 
 | ||||||
|       if (!track_fp) |       if (!track_fp) | ||||||
|       { |       { | ||||||
|         Log_ErrorPrintf("Failed to open track filename '%s' (from '%s' and '%s'): errno %d", |         Log_ErrorPrintf("Failed to open track filename '%s' (from '%s' and '%s'): %s", track_full_filename.c_str(), | ||||||
|                         track_full_filename.c_str(), track_filename.c_str(), filename, errno); |                         track_filename.c_str(), filename, track_error.GetDescription().c_str()); | ||||||
|         if (error) |         Error::SetString(error, | ||||||
|         { |                          fmt::format("Failed to open track filename '{}' (from '{}' and '{}'): {}", track_full_filename, | ||||||
|           error->SetFormattedMessage("Failed to open track filename '%s' (from '%s' and '%s'): errno %d", |                                      track_filename, filename, track_error.GetDescription())); | ||||||
|                                      track_full_filename.c_str(), track_filename.c_str(), filename, errno); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  | @ -148,11 +151,8 @@ bool CDImageCueSheet::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|       { |       { | ||||||
|         Log_ErrorPrintf("Failed to open track %u in '%s': track start is out of range (%u vs %" PRIu64 ")", track_num, |         Log_ErrorPrintf("Failed to open track %u in '%s': track start is out of range (%u vs %" PRIu64 ")", track_num, | ||||||
|                         filename, track_start, file_size); |                         filename, track_start, file_size); | ||||||
|         if (error) |         Error::SetString(error, fmt::format("Failed to open track {} in '{}': track start is out of range ({} vs {}))", | ||||||
|         { |                                             track_num, filename, track_start, file_size)); | ||||||
|           error->SetFormattedMessage("Failed to open track %u in '%s': track start is out of range (%u vs %" PRIu64 ")", |  | ||||||
|                                      track_num, filename, track_start, file_size); |  | ||||||
|         } |  | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  | @ -283,8 +283,7 @@ bool CDImageCueSheet::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|   if (m_tracks.empty()) |   if (m_tracks.empty()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("File '%s' contains no tracks", filename); |     Log_ErrorPrintf("File '%s' contains no tracks", filename); | ||||||
|     if (error) |     Error::SetString(error, fmt::format("File '{}' contains no tracks", filename)); | ||||||
|       error->SetFormattedMessage("File '%s' contains no tracks", filename); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -333,7 +332,7 @@ bool CDImageCueSheet::ReadSectorFromIndex(void* buffer, const Index& index, LBA | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<CDImage> CDImage::OpenCueSheetImage(const char* filename, Common::Error* error) | std::unique_ptr<CDImage> CDImage::OpenCueSheetImage(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   std::unique_ptr<CDImageCueSheet> image = std::make_unique<CDImageCueSheet>(); |   std::unique_ptr<CDImageCueSheet> image = std::make_unique<CDImageCueSheet>(); | ||||||
|   if (!image->OpenAndParse(filename, error)) |   if (!image->OpenAndParse(filename, error)) | ||||||
|  |  | ||||||
|  | @ -72,7 +72,7 @@ public: | ||||||
|   CDImageDeviceWin32(); |   CDImageDeviceWin32(); | ||||||
|   ~CDImageDeviceWin32() override; |   ~CDImageDeviceWin32() override; | ||||||
| 
 | 
 | ||||||
|   bool Open(const char* filename, Common::Error* error); |   bool Open(const char* filename, Error* error); | ||||||
| 
 | 
 | ||||||
|   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; |   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; | ||||||
|   bool HasNonStandardSubchannel() const override; |   bool HasNonStandardSubchannel() const override; | ||||||
|  | @ -112,7 +112,7 @@ CDImageDeviceWin32::~CDImageDeviceWin32() | ||||||
|     CloseHandle(m_hDevice); |     CloseHandle(m_hDevice); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImageDeviceWin32::Open(const char* filename, Common::Error* error) | bool CDImageDeviceWin32::Open(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   m_filename = filename; |   m_filename = filename; | ||||||
|   m_hDevice = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, |   m_hDevice = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, | ||||||
|  | @ -244,8 +244,7 @@ bool CDImageDeviceWin32::Open(const char* filename, Common::Error* error) | ||||||
|   if (m_tracks.empty()) |   if (m_tracks.empty()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("File '%s' contains no tracks", filename); |     Log_ErrorPrintf("File '%s' contains no tracks", filename); | ||||||
|     if (error) |     Error::SetString(error, fmt::format("File '{}' contains no tracks", filename)); | ||||||
|       error->SetFormattedMessage("File '%s' contains no tracks", filename); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -270,9 +269,7 @@ bool CDImageDeviceWin32::Open(const char* filename, Common::Error* error) | ||||||
|   if (!DetermineReadMode()) |   if (!DetermineReadMode()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Could not determine read mode"); |     Log_ErrorPrintf("Could not determine read mode"); | ||||||
|     if (error) |     Error::SetString(error, "Could not determine read mode"); | ||||||
|       error->SetMessage("Could not determine read mode"); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -476,7 +473,7 @@ bool CDImageDeviceWin32::DetermineReadMode() | ||||||
|   return false; |   return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<CDImage> CDImage::OpenDeviceImage(const char* filename, Common::Error* error) | std::unique_ptr<CDImage> CDImage::OpenDeviceImage(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   std::unique_ptr<CDImageDeviceWin32> image = std::make_unique<CDImageDeviceWin32>(); |   std::unique_ptr<CDImageDeviceWin32> image = std::make_unique<CDImageDeviceWin32>(); | ||||||
|   if (!image->Open(filename, error)) |   if (!image->Open(filename, error)) | ||||||
|  | @ -528,7 +525,7 @@ bool CDImage::IsDeviceName(const char* filename) | ||||||
| 
 | 
 | ||||||
| #else | #else | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<CDImage> CDImage::OpenDeviceImage(const char* filename, Common::Error* error) | std::unique_ptr<CDImage> CDImage::OpenDeviceImage(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   return {}; |   return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -164,7 +164,7 @@ public: | ||||||
|   CDImageEcm(); |   CDImageEcm(); | ||||||
|   ~CDImageEcm() override; |   ~CDImageEcm() override; | ||||||
| 
 | 
 | ||||||
|   bool Open(const char* filename, Common::Error* error); |   bool Open(const char* filename, Error* error); | ||||||
| 
 | 
 | ||||||
|   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; |   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; | ||||||
|   bool HasNonStandardSubchannel() const override; |   bool HasNonStandardSubchannel() const override; | ||||||
|  | @ -224,7 +224,7 @@ CDImageEcm::~CDImageEcm() | ||||||
|     std::fclose(m_fp); |     std::fclose(m_fp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImageEcm::Open(const char* filename, Common::Error* error) | bool CDImageEcm::Open(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   m_filename = filename; |   m_filename = filename; | ||||||
|   m_fp = FileSystem::OpenCFile(filename, "rb"); |   m_fp = FileSystem::OpenCFile(filename, "rb"); | ||||||
|  | @ -253,9 +253,7 @@ bool CDImageEcm::Open(const char* filename, Common::Error* error) | ||||||
|       header[3] != 0) |       header[3] != 0) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Failed to read/invalid header"); |     Log_ErrorPrintf("Failed to read/invalid header"); | ||||||
|     if (error) |     Error::SetString(error, "Failed to read/invalid header"); | ||||||
|       error->SetMessage("Failed to read/invalid header"); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -269,9 +267,7 @@ bool CDImageEcm::Open(const char* filename, Common::Error* error) | ||||||
|     if (bits == EOF) |     if (bits == EOF) | ||||||
|     { |     { | ||||||
|       Log_ErrorPrintf("Unexpected EOF after %zu chunks", m_data_map.size()); |       Log_ErrorPrintf("Unexpected EOF after %zu chunks", m_data_map.size()); | ||||||
|       if (error) |       Error::SetString(error, fmt::format("Unexpected EOF after {} chunks", m_data_map.size())); | ||||||
|         error->SetFormattedMessage("Unexpected EOF after %zu chunks", m_data_map.size()); |  | ||||||
| 
 |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -285,9 +281,7 @@ bool CDImageEcm::Open(const char* filename, Common::Error* error) | ||||||
|       if (bits == EOF) |       if (bits == EOF) | ||||||
|       { |       { | ||||||
|         Log_ErrorPrintf("Unexpected EOF after %zu chunks", m_data_map.size()); |         Log_ErrorPrintf("Unexpected EOF after %zu chunks", m_data_map.size()); | ||||||
|         if (error) |         Error::SetString(error, fmt::format("Unexpected EOF after {} chunks", m_data_map.size())); | ||||||
|           error->SetFormattedMessage("Unexpected EOF after %zu chunks", m_data_map.size()); |  | ||||||
| 
 |  | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  | @ -305,9 +299,7 @@ bool CDImageEcm::Open(const char* filename, Common::Error* error) | ||||||
|     if (count >= 0x80000000u) |     if (count >= 0x80000000u) | ||||||
|     { |     { | ||||||
|       Log_ErrorPrintf("Corrupted header after %zu chunks", m_data_map.size()); |       Log_ErrorPrintf("Corrupted header after %zu chunks", m_data_map.size()); | ||||||
|       if (error) |       Error::SetString(error, fmt::format("Corrupted header after {} chunks", m_data_map.size())); | ||||||
|         error->SetFormattedMessage("Corrupted header after %zu chunks", m_data_map.size()); |  | ||||||
| 
 |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -324,8 +316,7 @@ bool CDImageEcm::Open(const char* filename, Common::Error* error) | ||||||
|         if (static_cast<s64>(file_offset) > file_size) |         if (static_cast<s64>(file_offset) > file_size) | ||||||
|         { |         { | ||||||
|           Log_ErrorPrintf("Out of file bounds after %zu chunks", m_data_map.size()); |           Log_ErrorPrintf("Out of file bounds after %zu chunks", m_data_map.size()); | ||||||
|           if (error) |           Error::SetString(error, fmt::format("Out of file bounds after {} chunks", m_data_map.size())); | ||||||
|             error->SetFormattedMessage("Out of file bounds after %zu chunks", m_data_map.size()); |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | @ -342,8 +333,7 @@ bool CDImageEcm::Open(const char* filename, Common::Error* error) | ||||||
|         if (static_cast<s64>(file_offset) > file_size) |         if (static_cast<s64>(file_offset) > file_size) | ||||||
|         { |         { | ||||||
|           Log_ErrorPrintf("Out of file bounds after %zu chunks", m_data_map.size()); |           Log_ErrorPrintf("Out of file bounds after %zu chunks", m_data_map.size()); | ||||||
|           if (error) |           Error::SetString(error, fmt::format("Out of file bounds after {} chunks", m_data_map.size())); | ||||||
|             error->SetFormattedMessage("Out of file bounds after %zu chunks", m_data_map.size()); |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | @ -351,9 +341,7 @@ bool CDImageEcm::Open(const char* filename, Common::Error* error) | ||||||
|     if (std::fseek(m_fp, file_offset, SEEK_SET) != 0) |     if (std::fseek(m_fp, file_offset, SEEK_SET) != 0) | ||||||
|     { |     { | ||||||
|       Log_ErrorPrintf("Failed to seek to offset %u after %zu chunks", file_offset, m_data_map.size()); |       Log_ErrorPrintf("Failed to seek to offset %u after %zu chunks", file_offset, m_data_map.size()); | ||||||
|       if (error) |       Error::SetString(error, fmt::format("Failed to seek to offset {} after {} chunks", file_offset, m_data_map.size())); | ||||||
|         error->SetFormattedMessage("Failed to seek to offset %u after %zu chunks", file_offset, m_data_map.size()); |  | ||||||
| 
 |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | @ -361,9 +349,7 @@ bool CDImageEcm::Open(const char* filename, Common::Error* error) | ||||||
|   if (m_data_map.empty()) |   if (m_data_map.empty()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("No data in image '%s'", filename); |     Log_ErrorPrintf("No data in image '%s'", filename); | ||||||
|     if (error) |     Error::SetString(error, fmt::format("No data in image '{}'", filename)); | ||||||
|       error->SetFormattedMessage("No data in image '%s'", filename); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -549,7 +535,7 @@ bool CDImageEcm::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_i | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<CDImage> CDImage::OpenEcmImage(const char* filename, Common::Error* error) | std::unique_ptr<CDImage> CDImage::OpenEcmImage(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   std::unique_ptr<CDImageEcm> image = std::make_unique<CDImageEcm>(); |   std::unique_ptr<CDImageEcm> image = std::make_unique<CDImageEcm>(); | ||||||
|   if (!image->Open(filename, error)) |   if (!image->Open(filename, error)) | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ public: | ||||||
|   CDImageM3u(); |   CDImageM3u(); | ||||||
|   ~CDImageM3u() override; |   ~CDImageM3u() override; | ||||||
| 
 | 
 | ||||||
|   bool Open(const char* path, bool apply_patches, Common::Error* Error); |   bool Open(const char* path, bool apply_patches, Error* Error); | ||||||
| 
 | 
 | ||||||
|   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; |   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; | ||||||
|   bool HasNonStandardSubchannel() const override; |   bool HasNonStandardSubchannel() const override; | ||||||
|  | @ -29,7 +29,7 @@ public: | ||||||
|   u32 GetSubImageCount() const override; |   u32 GetSubImageCount() const override; | ||||||
|   u32 GetCurrentSubImage() const override; |   u32 GetCurrentSubImage() const override; | ||||||
|   std::string GetSubImageMetadata(u32 index, const std::string_view& type) const override; |   std::string GetSubImageMetadata(u32 index, const std::string_view& type) const override; | ||||||
|   bool SwitchSubImage(u32 index, Common::Error* error) override; |   bool SwitchSubImage(u32 index, Error* error) override; | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|   bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override; |   bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override; | ||||||
|  | @ -52,7 +52,7 @@ CDImageM3u::CDImageM3u() = default; | ||||||
| 
 | 
 | ||||||
| CDImageM3u::~CDImageM3u() = default; | CDImageM3u::~CDImageM3u() = default; | ||||||
| 
 | 
 | ||||||
| bool CDImageM3u::Open(const char* path, bool apply_patches, Common::Error* error) | bool CDImageM3u::Open(const char* path, bool apply_patches, Error* error) | ||||||
| { | { | ||||||
|   std::FILE* fp = FileSystem::OpenCFile(path, "rb"); |   std::FILE* fp = FileSystem::OpenCFile(path, "rb"); | ||||||
|   if (!fp) |   if (!fp) | ||||||
|  | @ -62,8 +62,7 @@ bool CDImageM3u::Open(const char* path, bool apply_patches, Common::Error* error | ||||||
|   std::fclose(fp); |   std::fclose(fp); | ||||||
|   if (!m3u_file.has_value() || m3u_file->empty()) |   if (!m3u_file.has_value() || m3u_file->empty()) | ||||||
|   { |   { | ||||||
|     if (error) |     Error::SetString(error, "Failed to read M3u file"); | ||||||
|       error->SetMessage("Failed to read M3u file"); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -128,7 +127,7 @@ u32 CDImageM3u::GetCurrentSubImage() const | ||||||
|   return m_current_image_index; |   return m_current_image_index; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImageM3u::SwitchSubImage(u32 index, Common::Error* error) | bool CDImageM3u::SwitchSubImage(u32 index, Error* error) | ||||||
| { | { | ||||||
|   if (index >= m_entries.size()) |   if (index >= m_entries.size()) | ||||||
|     return false; |     return false; | ||||||
|  | @ -175,7 +174,7 @@ bool CDImageM3u::ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_ | ||||||
|   return m_current_image->ReadSubChannelQ(subq, index, lba_in_index); |   return m_current_image->ReadSubChannelQ(subq, index, lba_in_index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<CDImage> CDImage::OpenM3uImage(const char* filename, bool apply_patches, Common::Error* error) | std::unique_ptr<CDImage> CDImage::OpenM3uImage(const char* filename, bool apply_patches, Error* error) | ||||||
| { | { | ||||||
|   std::unique_ptr<CDImageM3u> image = std::make_unique<CDImageM3u>(); |   std::unique_ptr<CDImageM3u> image = std::make_unique<CDImageM3u>(); | ||||||
|   if (!image->Open(filename, apply_patches, error)) |   if (!image->Open(filename, apply_patches, error)) | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ public: | ||||||
|   CDImageMds(); |   CDImageMds(); | ||||||
|   ~CDImageMds() override; |   ~CDImageMds() override; | ||||||
| 
 | 
 | ||||||
|   bool OpenAndParse(const char* filename, Common::Error* error); |   bool OpenAndParse(const char* filename, Error* error); | ||||||
| 
 | 
 | ||||||
|   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; |   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; | ||||||
|   bool HasNonStandardSubchannel() const override; |   bool HasNonStandardSubchannel() const override; | ||||||
|  | @ -61,15 +61,12 @@ CDImageMds::~CDImageMds() | ||||||
|     std::fclose(m_mdf_file); |     std::fclose(m_mdf_file); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImageMds::OpenAndParse(const char* filename, Common::Error* error) | bool CDImageMds::OpenAndParse(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   std::FILE* mds_fp = FileSystem::OpenCFile(filename, "rb"); |   std::FILE* mds_fp = FileSystem::OpenCFile(filename, "rb", error); | ||||||
|   if (!mds_fp) |   if (!mds_fp) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Failed to open mds '%s': errno %d", filename, errno); |     Log_ErrorPrintf("Failed to open mds '%s': errno %d", filename, errno); | ||||||
|     if (error) |  | ||||||
|       error->SetErrno(errno); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -78,20 +75,15 @@ bool CDImageMds::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|   if (!mds_data_opt.has_value() || mds_data_opt->size() < 0x54) |   if (!mds_data_opt.has_value() || mds_data_opt->size() < 0x54) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Failed to read mds file '%s'", filename); |     Log_ErrorPrintf("Failed to read mds file '%s'", filename); | ||||||
|     if (error) |     Error::SetString(error, fmt::format("Failed to read mds file '{}'", filename)); | ||||||
|       error->SetFormattedMessage("Failed to read mds file '%s'", filename); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   std::string mdf_filename(Path::ReplaceExtension(filename, "mdf")); |   std::string mdf_filename(Path::ReplaceExtension(filename, "mdf")); | ||||||
|   m_mdf_file = FileSystem::OpenCFile(mdf_filename.c_str(), "rb"); |   m_mdf_file = FileSystem::OpenCFile(mdf_filename.c_str(), "rb", error); | ||||||
|   if (!m_mdf_file) |   if (!m_mdf_file) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Failed to open mdf file '%s': errno %d", mdf_filename.c_str(), errno); |     Log_ErrorPrintf("Failed to open mdf file '%s': errno %d", mdf_filename.c_str(), errno); | ||||||
|     if (error) |  | ||||||
|       error->SetFormattedMessage("Failed to open mdf file '%s': errno %d", mdf_filename.c_str(), errno); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -100,9 +92,7 @@ bool CDImageMds::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|   if (std::memcmp(&mds[0], expected_signature, sizeof(expected_signature) - 1) != 0) |   if (std::memcmp(&mds[0], expected_signature, sizeof(expected_signature) - 1) != 0) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Incorrect signature in '%s'", filename); |     Log_ErrorPrintf("Incorrect signature in '%s'", filename); | ||||||
|     if (error) |     Error::SetString(error, fmt::format("Incorrect signature in '{}'", filename)); | ||||||
|       error->SetFormattedMessage("Incorrect signature in '%s'", filename); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -111,9 +101,7 @@ bool CDImageMds::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|   if ((session_offset + 24) > mds.size()) |   if ((session_offset + 24) > mds.size()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Invalid session offset in '%s'", filename); |     Log_ErrorPrintf("Invalid session offset in '%s'", filename); | ||||||
|     if (error) |     Error::SetString(error, fmt::format("Invalid session offset in '{}'", filename)); | ||||||
|       error->SetFormattedMessage("Invalid session offset in '%s'", filename); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -124,9 +112,7 @@ bool CDImageMds::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|   if (track_count > 99 || track_offset >= mds.size()) |   if (track_count > 99 || track_offset >= mds.size()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Invalid track count/block offset %u/%u in '%s'", track_count, track_offset, filename); |     Log_ErrorPrintf("Invalid track count/block offset %u/%u in '%s'", track_count, track_offset, filename); | ||||||
|     if (error) |     Error::SetString(error, fmt::format("Invalid track count/block offset {}/{} in '{}'", track_count, track_offset, filename)); | ||||||
|       error->SetFormattedMessage("Invalid track count/block offset %u/%u in '%s'", track_count, track_offset, filename); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -145,9 +131,7 @@ bool CDImageMds::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|     if ((track_offset + sizeof(TrackEntry)) > mds.size()) |     if ((track_offset + sizeof(TrackEntry)) > mds.size()) | ||||||
|     { |     { | ||||||
|       Log_ErrorPrintf("End of file in '%s' at track %u", filename, track_number); |       Log_ErrorPrintf("End of file in '%s' at track %u", filename, track_number); | ||||||
|       if (error) |       Error::SetString(error, fmt::format("End of file in '{}' at track {}", filename, track_number)); | ||||||
|         error->SetFormattedMessage("End of file in '%s' at track %u", filename, track_number); |  | ||||||
| 
 |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -158,9 +142,7 @@ bool CDImageMds::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|     if (PackedBCDToBinary(track.track_number) != track_number) |     if (PackedBCDToBinary(track.track_number) != track_number) | ||||||
|     { |     { | ||||||
|       Log_ErrorPrintf("Unexpected track number 0x%02X in track %u", track.track_number, track_number); |       Log_ErrorPrintf("Unexpected track number 0x%02X in track %u", track.track_number, track_number); | ||||||
|       if (error) |       Error::SetString(error, fmt::format("Unexpected track number 0x{:02X} in track {}", track.track_number, track_number)); | ||||||
|         error->SetFormattedMessage("Unexpected track number 0x%02X in track %u", track.track_number, track_number); |  | ||||||
| 
 |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -171,9 +153,7 @@ bool CDImageMds::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|     if ((track.extra_offset + sizeof(u32) + sizeof(u32)) > mds.size()) |     if ((track.extra_offset + sizeof(u32) + sizeof(u32)) > mds.size()) | ||||||
|     { |     { | ||||||
|       Log_ErrorPrintf("Invalid extra offset %u in track %u", track.extra_offset, track_number); |       Log_ErrorPrintf("Invalid extra offset %u in track %u", track.extra_offset, track_number); | ||||||
|       if (error) |       Error::SetString(error, fmt::format("Invalid extra offset {} in track {}", track.extra_offset, track_number)); | ||||||
|         error->SetFormattedMessage("Invalid extra offset %u in track %u", track.extra_offset, track_number); |  | ||||||
| 
 |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -196,9 +176,7 @@ bool CDImageMds::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|       if (track_pregap > track_start_lba) |       if (track_pregap > track_start_lba) | ||||||
|       { |       { | ||||||
|         Log_ErrorPrintf("Track pregap %u is too large for start lba %u", track_pregap, track_start_lba); |         Log_ErrorPrintf("Track pregap %u is too large for start lba %u", track_pregap, track_start_lba); | ||||||
|         if (error) |         Error::SetString(error, fmt::format("Track pregap {} is too large for start lba {}", track_pregap, track_start_lba)); | ||||||
|           error->SetFormattedMessage("Track pregap %u is too large for start lba %u", track_pregap, track_start_lba); |  | ||||||
| 
 |  | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  | @ -247,9 +225,7 @@ bool CDImageMds::OpenAndParse(const char* filename, Common::Error* error) | ||||||
|   if (m_tracks.empty()) |   if (m_tracks.empty()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("File '%s' contains no tracks", filename); |     Log_ErrorPrintf("File '%s' contains no tracks", filename); | ||||||
|     if (error) |     Error::SetString(error, fmt::format("File '{}' contains no tracks", filename)); | ||||||
|       error->SetFormattedMessage("File '%s' contains no tracks", filename); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -297,7 +273,7 @@ bool CDImageMds::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_i | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<CDImage> CDImage::OpenMdsImage(const char* filename, Common::Error* error) | std::unique_ptr<CDImage> CDImage::OpenMdsImage(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   std::unique_ptr<CDImageMds> image = std::make_unique<CDImageMds>(); |   std::unique_ptr<CDImageMds> image = std::make_unique<CDImageMds>(); | ||||||
|   if (!image->OpenAndParse(filename, error)) |   if (!image->OpenAndParse(filename, error)) | ||||||
|  |  | ||||||
|  | @ -27,7 +27,7 @@ public: | ||||||
|   CDImagePBP() = default; |   CDImagePBP() = default; | ||||||
|   ~CDImagePBP() override; |   ~CDImagePBP() override; | ||||||
| 
 | 
 | ||||||
|   bool Open(const char* filename, Common::Error* error); |   bool Open(const char* filename, Error* error); | ||||||
| 
 | 
 | ||||||
|   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; |   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; | ||||||
|   bool HasNonStandardSubchannel() const override; |   bool HasNonStandardSubchannel() const override; | ||||||
|  | @ -35,7 +35,7 @@ public: | ||||||
|   bool HasSubImages() const override; |   bool HasSubImages() const override; | ||||||
|   u32 GetSubImageCount() const override; |   u32 GetSubImageCount() const override; | ||||||
|   u32 GetCurrentSubImage() const override; |   u32 GetCurrentSubImage() const override; | ||||||
|   bool SwitchSubImage(u32 index, Common::Error* error) override; |   bool SwitchSubImage(u32 index, Error* error) override; | ||||||
|   std::string GetMetadata(const std::string_view& type) const override; |   std::string GetMetadata(const std::string_view& type) const override; | ||||||
|   std::string GetSubImageMetadata(u32 index, const std::string_view& type) const override; |   std::string GetSubImageMetadata(u32 index, const std::string_view& type) const override; | ||||||
| 
 | 
 | ||||||
|  | @ -61,12 +61,12 @@ private: | ||||||
|   bool LoadSFOIndexTable(); |   bool LoadSFOIndexTable(); | ||||||
|   bool LoadSFOTable(); |   bool LoadSFOTable(); | ||||||
| 
 | 
 | ||||||
|   bool IsValidEboot(Common::Error* error); |   bool IsValidEboot(Error* error); | ||||||
| 
 | 
 | ||||||
|   bool InitDecompressionStream(); |   bool InitDecompressionStream(); | ||||||
|   bool DecompressBlock(const BlockInfo& block_info); |   bool DecompressBlock(const BlockInfo& block_info); | ||||||
| 
 | 
 | ||||||
|   bool OpenDisc(u32 index, Common::Error* error); |   bool OpenDisc(u32 index, Error* error); | ||||||
| 
 | 
 | ||||||
|   static const std::string* LookupStringSFOTableEntry(const char* key, const SFOTable& table); |   static const std::string* LookupStringSFOTableEntry(const char* key, const SFOTable& table); | ||||||
| 
 | 
 | ||||||
|  | @ -277,7 +277,7 @@ bool CDImagePBP::LoadSFOTable() | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImagePBP::IsValidEboot(Common::Error* error) | bool CDImagePBP::IsValidEboot(Error* error) | ||||||
| { | { | ||||||
|   // Check some fields to make sure this is a valid PS1 EBOOT.PBP
 |   // Check some fields to make sure this is a valid PS1 EBOOT.PBP
 | ||||||
| 
 | 
 | ||||||
|  | @ -288,16 +288,14 @@ bool CDImagePBP::IsValidEboot(Common::Error* error) | ||||||
|     if (!std::holds_alternative<u32>(data_value) || std::get<u32>(data_value) != 1) |     if (!std::holds_alternative<u32>(data_value) || std::get<u32>(data_value) != 1) | ||||||
|     { |     { | ||||||
|       Log_ErrorPrint("Invalid BOOTABLE value"); |       Log_ErrorPrint("Invalid BOOTABLE value"); | ||||||
|       if (error) |       Error::SetString(error, "Invalid BOOTABLE value"); | ||||||
|         error->SetMessage("Invalid BOOTABLE value"); |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   else |   else | ||||||
|   { |   { | ||||||
|     Log_ErrorPrint("No BOOTABLE value found"); |     Log_ErrorPrint("No BOOTABLE value found"); | ||||||
|     if (error) |     Error::SetString(error, "No BOOTABLE value found"); | ||||||
|       error->SetMessage("No BOOTABLE value found"); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -308,23 +306,21 @@ bool CDImagePBP::IsValidEboot(Common::Error* error) | ||||||
|     if (!std::holds_alternative<std::string>(data_value) || std::get<std::string>(data_value) != "ME") |     if (!std::holds_alternative<std::string>(data_value) || std::get<std::string>(data_value) != "ME") | ||||||
|     { |     { | ||||||
|       Log_ErrorPrint("Invalid CATEGORY value"); |       Log_ErrorPrint("Invalid CATEGORY value"); | ||||||
|       if (error) |       Error::SetString(error, "Invalid CATEGORY value"); | ||||||
|         error->SetMessage("Invalid CATEGORY value"); |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   else |   else | ||||||
|   { |   { | ||||||
|     Log_ErrorPrint("No CATEGORY value found"); |     Log_ErrorPrint("No CATEGORY value found"); | ||||||
|     if (error) |     Error::SetString(error, "No CATEGORY value found"); | ||||||
|       error->SetMessage("No CATEGORY value found"); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImagePBP::Open(const char* filename, Common::Error* error) | bool CDImagePBP::Open(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   if (!EndianHelper::HostIsLittleEndian()) |   if (!EndianHelper::HostIsLittleEndian()) | ||||||
|   { |   { | ||||||
|  | @ -347,8 +343,7 @@ bool CDImagePBP::Open(const char* filename, Common::Error* error) | ||||||
|   if (!LoadPBPHeader()) |   if (!LoadPBPHeader()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrint("Failed to load PBP header"); |     Log_ErrorPrint("Failed to load PBP header"); | ||||||
|     if (error) |     Error::SetString(error, "Failed to load PBP header"); | ||||||
|       error->SetMessage("Failed to load PBP header"); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -356,8 +351,7 @@ bool CDImagePBP::Open(const char* filename, Common::Error* error) | ||||||
|   if (!LoadSFOHeader()) |   if (!LoadSFOHeader()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrint("Failed to load SFO header"); |     Log_ErrorPrint("Failed to load SFO header"); | ||||||
|     if (error) |     Error::SetString(error, "Failed to load SFO header"); | ||||||
|       error->SetMessage("Failed to load SFO header"); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -365,8 +359,7 @@ bool CDImagePBP::Open(const char* filename, Common::Error* error) | ||||||
|   if (!LoadSFOIndexTable()) |   if (!LoadSFOIndexTable()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrint("Failed to load SFO index table"); |     Log_ErrorPrint("Failed to load SFO index table"); | ||||||
|     if (error) |     Error::SetString(error, "Failed to load SFO index table"); | ||||||
|       error->SetMessage("Failed to load SFO index table"); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -374,8 +367,7 @@ bool CDImagePBP::Open(const char* filename, Common::Error* error) | ||||||
|   if (!LoadSFOTable()) |   if (!LoadSFOTable()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrint("Failed to load SFO table"); |     Log_ErrorPrint("Failed to load SFO table"); | ||||||
|     if (error) |     Error::SetString(error, "Failed to load SFO table"); | ||||||
|       error->SetMessage("Failed to load SFO table"); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -412,9 +404,7 @@ bool CDImagePBP::Open(const char* filename, Common::Error* error) | ||||||
|     if (disc_table[0] == 0x44475000) // "\0PGD"
 |     if (disc_table[0] == 0x44475000) // "\0PGD"
 | ||||||
|     { |     { | ||||||
|       Log_ErrorPrintf("Encrypted PBP images are not supported, skipping %s", m_filename.c_str()); |       Log_ErrorPrintf("Encrypted PBP images are not supported, skipping %s", m_filename.c_str()); | ||||||
|       if (error) |       Error::SetString(error, "Encrypted PBP images are not supported"); | ||||||
|         error->SetMessage("Encrypted PBP images are not supported"); |  | ||||||
| 
 |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -442,13 +432,12 @@ bool CDImagePBP::Open(const char* filename, Common::Error* error) | ||||||
|   return OpenDisc(0, error); |   return OpenDisc(0, error); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImagePBP::OpenDisc(u32 index, Common::Error* error) | bool CDImagePBP::OpenDisc(u32 index, Error* error) | ||||||
| { | { | ||||||
|   if (index >= m_disc_offsets.size()) |   if (index >= m_disc_offsets.size()) | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("File does not contain disc %u", index + 1); |     Log_ErrorPrintf("File does not contain disc %u", index + 1); | ||||||
|     if (error) |     Error::SetString(error, fmt::format("File does not contain disc {}", index + 1)); | ||||||
|       error->SetMessage(TinyString::FromFormat("File does not contain disc %u", index + 1)); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -484,9 +473,7 @@ bool CDImagePBP::OpenDisc(u32 index, Common::Error* error) | ||||||
|   if (pgd_magic == 0x44475000) // "\0PGD"
 |   if (pgd_magic == 0x44475000) // "\0PGD"
 | ||||||
|   { |   { | ||||||
|     Log_ErrorPrintf("Encrypted PBP images are not supported, skipping %s", m_filename.c_str()); |     Log_ErrorPrintf("Encrypted PBP images are not supported, skipping %s", m_filename.c_str()); | ||||||
|     if (error) |     Error::SetString(error, "Encrypted PBP images are not supported"); | ||||||
|       error->SetMessage("Encrypted PBP images are not supported"); |  | ||||||
| 
 |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -867,7 +854,7 @@ u32 CDImagePBP::GetCurrentSubImage() const | ||||||
|   return m_current_disc; |   return m_current_disc; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CDImagePBP::SwitchSubImage(u32 index, Common::Error* error) | bool CDImagePBP::SwitchSubImage(u32 index, Error* error) | ||||||
| { | { | ||||||
|   if (index >= m_disc_offsets.size()) |   if (index >= m_disc_offsets.size()) | ||||||
|     return false; |     return false; | ||||||
|  | @ -895,7 +882,7 @@ std::string CDImagePBP::GetSubImageMetadata(u32 index, const std::string_view& t | ||||||
|   return CDImage::GetSubImageMetadata(index, type); |   return CDImage::GetSubImageMetadata(index, type); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<CDImage> CDImage::OpenPBPImage(const char* filename, Common::Error* error) | std::unique_ptr<CDImage> CDImage::OpenPBPImage(const char* filename, Error* error) | ||||||
| { | { | ||||||
|   std::unique_ptr<CDImagePBP> image = std::make_unique<CDImagePBP>(); |   std::unique_ptr<CDImagePBP> image = std::make_unique<CDImagePBP>(); | ||||||
|   if (!image->Open(filename, error)) |   if (!image->Open(filename, error)) | ||||||
|  |  | ||||||
|  | @ -45,7 +45,7 @@ Track* File::GetMutableTrack(u32 n) | ||||||
|   return nullptr; |   return nullptr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool File::Parse(std::FILE* fp, Common::Error* error) | bool File::Parse(std::FILE* fp, Error* error) | ||||||
| { | { | ||||||
|   char line[1024]; |   char line[1024]; | ||||||
|   u32 line_number = 1; |   u32 line_number = 1; | ||||||
|  | @ -66,7 +66,7 @@ bool File::Parse(std::FILE* fp, Common::Error* error) | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void File::SetError(u32 line_number, Common::Error* error, const char* format, ...) | void File::SetError(u32 line_number, Error* error, const char* format, ...) | ||||||
| { | { | ||||||
|   std::va_list ap; |   std::va_list ap; | ||||||
|   SmallString str; |   SmallString str; | ||||||
|  | @ -75,9 +75,7 @@ void File::SetError(u32 line_number, Common::Error* error, const char* format, . | ||||||
|   va_end(ap); |   va_end(ap); | ||||||
| 
 | 
 | ||||||
|   Log_ErrorPrintf("Cue parse error at line %u: %s", line_number, str.GetCharArray()); |   Log_ErrorPrintf("Cue parse error at line %u: %s", line_number, str.GetCharArray()); | ||||||
| 
 |   Error::SetString(error, fmt::format("Cue parse error at line {}: {}", line_number, str)); | ||||||
|   if (error) |  | ||||||
|     error->SetFormattedMessage("Cue parse error at line %u: %s", line_number, str.GetCharArray()); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string_view File::GetToken(const char*& line) | std::string_view File::GetToken(const char*& line) | ||||||
|  | @ -166,7 +164,7 @@ std::optional<MSF> File::GetMSF(const std::string_view& token) | ||||||
|   return ret; |   return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool File::ParseLine(const char* line, u32 line_number, Common::Error* error) | bool File::ParseLine(const char* line, u32 line_number, Error* error) | ||||||
| { | { | ||||||
|   const std::string_view command(GetToken(line)); |   const std::string_view command(GetToken(line)); | ||||||
|   if (command.empty()) |   if (command.empty()) | ||||||
|  | @ -210,7 +208,7 @@ bool File::ParseLine(const char* line, u32 line_number, Common::Error* error) | ||||||
|   return false; |   return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool File::HandleFileCommand(const char* line, u32 line_number, Common::Error* error) | bool File::HandleFileCommand(const char* line, u32 line_number, Error* error) | ||||||
| { | { | ||||||
|   const std::string_view filename(GetToken(line)); |   const std::string_view filename(GetToken(line)); | ||||||
|   const std::string_view mode(GetToken(line)); |   const std::string_view mode(GetToken(line)); | ||||||
|  | @ -232,7 +230,7 @@ bool File::HandleFileCommand(const char* line, u32 line_number, Common::Error* e | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool File::HandleTrackCommand(const char* line, u32 line_number, Common::Error* error) | bool File::HandleTrackCommand(const char* line, u32 line_number, Error* error) | ||||||
| { | { | ||||||
|   if (!CompleteLastTrack(line_number, error)) |   if (!CompleteLastTrack(line_number, error)) | ||||||
|     return false; |     return false; | ||||||
|  | @ -288,7 +286,7 @@ bool File::HandleTrackCommand(const char* line, u32 line_number, Common::Error* | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool File::HandleIndexCommand(const char* line, u32 line_number, Common::Error* error) | bool File::HandleIndexCommand(const char* line, u32 line_number, Error* error) | ||||||
| { | { | ||||||
|   if (!m_current_track.has_value()) |   if (!m_current_track.has_value()) | ||||||
|   { |   { | ||||||
|  | @ -334,7 +332,7 @@ bool File::HandleIndexCommand(const char* line, u32 line_number, Common::Error* | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool File::HandlePregapCommand(const char* line, u32 line_number, Common::Error* error) | bool File::HandlePregapCommand(const char* line, u32 line_number, Error* error) | ||||||
| { | { | ||||||
|   if (!m_current_track.has_value()) |   if (!m_current_track.has_value()) | ||||||
|   { |   { | ||||||
|  | @ -366,7 +364,7 @@ bool File::HandlePregapCommand(const char* line, u32 line_number, Common::Error* | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool File::HandleFlagCommand(const char* line, u32 line_number, Common::Error* error) | bool File::HandleFlagCommand(const char* line, u32 line_number, Error* error) | ||||||
| { | { | ||||||
|   if (!m_current_track.has_value()) |   if (!m_current_track.has_value()) | ||||||
|   { |   { | ||||||
|  | @ -395,7 +393,7 @@ bool File::HandleFlagCommand(const char* line, u32 line_number, Common::Error* e | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool File::CompleteLastTrack(u32 line_number, Common::Error* error) | bool File::CompleteLastTrack(u32 line_number, Error* error) | ||||||
| { | { | ||||||
|   if (!m_current_track.has_value()) |   if (!m_current_track.has_value()) | ||||||
|     return true; |     return true; | ||||||
|  | @ -436,7 +434,7 @@ bool File::CompleteLastTrack(u32 line_number, Common::Error* error) | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool File::SetTrackLengths(u32 line_number, Common::Error* error) | bool File::SetTrackLengths(u32 line_number, Error* error) | ||||||
| { | { | ||||||
|   for (const Track& track : m_tracks) |   for (const Track& track : m_tracks) | ||||||
|   { |   { | ||||||
|  |  | ||||||
|  | @ -60,26 +60,26 @@ public: | ||||||
| 
 | 
 | ||||||
|   const Track* GetTrack(u32 n) const; |   const Track* GetTrack(u32 n) const; | ||||||
| 
 | 
 | ||||||
|   bool Parse(std::FILE* fp, Common::Error* error); |   bool Parse(std::FILE* fp, Error* error); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|   Track* GetMutableTrack(u32 n); |   Track* GetMutableTrack(u32 n); | ||||||
| 
 | 
 | ||||||
|   void SetError(u32 line_number, Common::Error* error, const char* format, ...); |   void SetError(u32 line_number, Error* error, const char* format, ...); | ||||||
| 
 | 
 | ||||||
|   static std::string_view GetToken(const char*& line); |   static std::string_view GetToken(const char*& line); | ||||||
|   static std::optional<MSF> GetMSF(const std::string_view& token); |   static std::optional<MSF> GetMSF(const std::string_view& token); | ||||||
| 
 | 
 | ||||||
|   bool ParseLine(const char* line, u32 line_number, Common::Error* error); |   bool ParseLine(const char* line, u32 line_number, Error* error); | ||||||
| 
 | 
 | ||||||
|   bool HandleFileCommand(const char* line, u32 line_number, Common::Error* error); |   bool HandleFileCommand(const char* line, u32 line_number, Error* error); | ||||||
|   bool HandleTrackCommand(const char* line, u32 line_number, Common::Error* error); |   bool HandleTrackCommand(const char* line, u32 line_number, Error* error); | ||||||
|   bool HandleIndexCommand(const char* line, u32 line_number, Common::Error* error); |   bool HandleIndexCommand(const char* line, u32 line_number, Error* error); | ||||||
|   bool HandlePregapCommand(const char* line, u32 line_number, Common::Error* error); |   bool HandlePregapCommand(const char* line, u32 line_number, Error* error); | ||||||
|   bool HandleFlagCommand(const char* line, u32 line_number, Common::Error* error); |   bool HandleFlagCommand(const char* line, u32 line_number, Error* error); | ||||||
| 
 | 
 | ||||||
|   bool CompleteLastTrack(u32 line_number, Common::Error* error); |   bool CompleteLastTrack(u32 line_number, Error* error); | ||||||
|   bool SetTrackLengths(u32 line_number, Common::Error* error); |   bool SetTrackLengths(u32 line_number, Error* error); | ||||||
| 
 | 
 | ||||||
|   std::vector<Track> m_tracks; |   std::vector<Track> m_tracks; | ||||||
|   std::optional<std::string> m_current_file; |   std::optional<std::string> m_current_file; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Stenzek
						Stenzek