mirror of https://github.com/PCSX2/pcsx2.git
397 lines
16 KiB
C++
397 lines
16 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
|
*
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <memory>
|
|
#include <wx/string.h>
|
|
#include "common/Assertions.h"
|
|
#include "common/Dependencies.h"
|
|
|
|
// Because wxTrap isn't available on Linux builds of wxWidgets (non-Debug, typically)
|
|
void pxTrap();
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// DESTRUCTOR_CATCHALL - safe destructor helper
|
|
// --------------------------------------------------------------------------------------
|
|
// In C++ destructors *really* need to be "nothrow" garaunteed, otherwise you can have
|
|
// disasterous nested exception throws during the unwinding process of an originating
|
|
// exception. Use this macro to dispose of these dangerous exceptions, and generate a
|
|
// friendly error log in their wake.
|
|
//
|
|
// Note: Console can also fire an Exception::OutOfMemory
|
|
#define __DESTRUCTOR_CATCHALL(funcname) \
|
|
catch (BaseException & ex) \
|
|
{ \
|
|
try { \
|
|
Console.Error("Unhandled BaseException in %s (ignored!):", funcname); \
|
|
Console.Error(ex.FormatDiagnosticMessage()); \
|
|
} catch (...) { \
|
|
fprintf(stderr, "ERROR: (out of memory?)\n"); \
|
|
} \
|
|
} \
|
|
catch (std::exception & ex) \
|
|
{ \
|
|
try { \
|
|
Console.Error("Unhandled std::exception in %s (ignored!):", funcname); \
|
|
Console.Error(ex.what()); \
|
|
} catch (...) { \
|
|
fprintf(stderr, "ERROR: (out of memory?)\n"); \
|
|
} \
|
|
} \
|
|
catch (...) \
|
|
{ \
|
|
/* Unreachable code */ \
|
|
}
|
|
|
|
#define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL(__pxFUNCTION__)
|
|
|
|
namespace Exception
|
|
{
|
|
class BaseException;
|
|
|
|
int MakeNewType();
|
|
BaseException *FromErrno(const wxString &streamname, int errcode);
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// BaseException
|
|
// --------------------------------------------------------------------------------------
|
|
// std::exception sucks, and isn't entirely cross-platform reliable in its implementation,
|
|
// so I made a replacement. The internal messages are non-const, which means that a
|
|
// catch clause can optionally modify them and then re-throw to a top-level handler.
|
|
//
|
|
// Note, this class is "abstract" which means you shouldn't use it directly like, ever.
|
|
// Use Exception::RuntimeError instead for generic exceptions.
|
|
//
|
|
// Because exceptions are the (only!) really useful example of multiple inheritance,
|
|
// this class has only a trivial constructor, and must be manually initialized using
|
|
// InitBaseEx() or by individual member assignments. This is because C++ multiple inheritence
|
|
// is, by design, a lot of fail, especially when class initializers are mixed in.
|
|
//
|
|
// [TODO] : Add an InnerException component, and Clone() facility.
|
|
//
|
|
class BaseException
|
|
{
|
|
protected:
|
|
wxString m_message_diag; // (untranslated) a "detailed" message of what disastrous thing has occurred!
|
|
wxString m_message_user; // (translated) a "detailed" message of what disastrous thing has occurred!
|
|
|
|
public:
|
|
virtual ~BaseException() = default;
|
|
|
|
const wxString &DiagMsg() const { return m_message_diag; }
|
|
const wxString &UserMsg() const { return m_message_user; }
|
|
|
|
wxString &DiagMsg() { return m_message_diag; }
|
|
wxString &UserMsg() { return m_message_user; }
|
|
|
|
BaseException &SetBothMsgs(const wxChar *msg_diag);
|
|
BaseException &SetDiagMsg(const wxString &msg_diag);
|
|
BaseException &SetUserMsg(const wxString &msg_user);
|
|
|
|
// Returns a message suitable for diagnostic / logging purposes.
|
|
// This message is always in English, and includes a full stack trace.
|
|
virtual wxString FormatDiagnosticMessage() const;
|
|
|
|
// Returns a message suitable for end-user display.
|
|
// This message is usually meant for display in a user popup or such.
|
|
virtual wxString FormatDisplayMessage() const;
|
|
|
|
virtual void Rethrow() const = 0;
|
|
virtual BaseException *Clone() const = 0;
|
|
};
|
|
|
|
typedef std::unique_ptr<BaseException> ScopedExcept;
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// Ps2Generic Exception
|
|
// --------------------------------------------------------------------------------------
|
|
// This class is used as a base exception for things tossed by PS2 cpus (EE, IOP, etc).
|
|
//
|
|
// Implementation note: does not derive from BaseException, so that we can use different
|
|
// catch block hierarchies to handle them (if needed).
|
|
//
|
|
// Translation Note: Currently these exceptions are never translated. English/diagnostic
|
|
// format only. :)
|
|
//
|
|
class Ps2Generic
|
|
{
|
|
protected:
|
|
wxString m_message; // a "detailed" message of what disastrous thing has occurred!
|
|
|
|
public:
|
|
virtual ~Ps2Generic() = default;
|
|
|
|
virtual u32 GetPc() const = 0;
|
|
virtual bool IsDelaySlot() const = 0;
|
|
virtual wxString &Message() { return m_message; }
|
|
|
|
virtual void Rethrow() const = 0;
|
|
virtual Ps2Generic *Clone() const = 0;
|
|
};
|
|
|
|
// Some helper macros for defining the standard constructors of internationalized constructors
|
|
// Parameters:
|
|
// classname - Yeah, the name of this class being defined. :)
|
|
//
|
|
// defmsg - default message (in english), which will be used for both english and i18n messages.
|
|
// The text string will be passed through the translator, so if it's int he gettext database
|
|
// it will be optionally translated.
|
|
//
|
|
// BUGZ?? I'd rather use 'classname' on the Clone() prototype, but for some reason it generates
|
|
// ambiguity errors on virtual inheritance (it really shouldn't!). So I have to force it to the
|
|
// BaseException base class. Not sure if this is Stupid Standard Tricks or Stupid MSVC Tricks. --air
|
|
//
|
|
// (update: web searches indicate it's MSVC specific -- happens in 2008, not sure about 2010).
|
|
//
|
|
#define DEFINE_EXCEPTION_COPYTORS(classname, parent) \
|
|
private: \
|
|
typedef parent _parent; \
|
|
\
|
|
public: \
|
|
virtual ~classname() = default; \
|
|
virtual void Rethrow() const { throw * this; } \
|
|
virtual classname *Clone() const { return new classname(*this); }
|
|
|
|
#define DEFINE_EXCEPTION_MESSAGES(classname) \
|
|
public: \
|
|
classname &SetBothMsgs(const wxChar *msg_diag) \
|
|
{ \
|
|
BaseException::SetBothMsgs(msg_diag); \
|
|
return *this; \
|
|
} \
|
|
classname &SetDiagMsg(const wxString &msg_diag) \
|
|
{ \
|
|
m_message_diag = msg_diag; \
|
|
return *this; \
|
|
} \
|
|
classname &SetUserMsg(const wxString &msg_user) \
|
|
{ \
|
|
m_message_user = msg_user; \
|
|
return *this; \
|
|
}
|
|
|
|
#define DEFINE_RUNTIME_EXCEPTION(classname, parent, message) \
|
|
DEFINE_EXCEPTION_COPYTORS(classname, parent) \
|
|
classname() { SetDiagMsg(message); } \
|
|
DEFINE_EXCEPTION_MESSAGES(classname)
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// RuntimeError - Generalized Exceptions with Recoverable Traits!
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
class RuntimeError : public BaseException
|
|
{
|
|
DEFINE_EXCEPTION_COPYTORS(RuntimeError, BaseException)
|
|
DEFINE_EXCEPTION_MESSAGES(RuntimeError)
|
|
|
|
public:
|
|
bool IsSilent;
|
|
|
|
RuntimeError() { IsSilent = false; }
|
|
RuntimeError(const std::runtime_error &ex, const wxString &prefix = wxEmptyString);
|
|
RuntimeError(const std::exception &ex, const wxString &prefix = wxEmptyString);
|
|
};
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// CancelAppEvent - Exception for canceling an event in a non-verbose fashion
|
|
// --------------------------------------------------------------------------------------
|
|
// Typically the PCSX2 interface issues popup dialogs for runtime errors. This exception
|
|
// instead issues a "silent" cancelation that is handled by the app gracefully (generates
|
|
// log, and resumes messages queue processing).
|
|
//
|
|
// I chose to have this exception derive from RuntimeError, since if one is thrown from outside
|
|
// an App message loop we'll still want it to be handled in a reasonably graceful manner.
|
|
class CancelEvent : public RuntimeError
|
|
{
|
|
DEFINE_RUNTIME_EXCEPTION(CancelEvent, RuntimeError, pxLt("No reason given."))
|
|
|
|
public:
|
|
explicit CancelEvent(const wxString &logmsg)
|
|
{
|
|
m_message_diag = logmsg;
|
|
// overridden message formatters only use the diagnostic version...
|
|
}
|
|
|
|
virtual wxString FormatDisplayMessage() const;
|
|
virtual wxString FormatDiagnosticMessage() const;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// OutOfMemory
|
|
// ---------------------------------------------------------------------------------------
|
|
// This exception has a custom-formatted Diagnostic string. The parameter give when constructing
|
|
// the exception is a block/alloc name, which is used as a formatting parameter in the diagnostic
|
|
// output. The default diagnostic message is "Out of memory exception, while allocating the %s."
|
|
// where %s is filled in with the block name.
|
|
//
|
|
// The user string is not custom-formatted, and should contain *NO* %s tags.
|
|
//
|
|
class OutOfMemory : public RuntimeError
|
|
{
|
|
DEFINE_RUNTIME_EXCEPTION(OutOfMemory, RuntimeError, wxEmptyString)
|
|
|
|
public:
|
|
wxString AllocDescription;
|
|
|
|
public:
|
|
OutOfMemory(const wxString &allocdesc);
|
|
|
|
virtual wxString FormatDisplayMessage() const;
|
|
virtual wxString FormatDiagnosticMessage() const;
|
|
};
|
|
|
|
class ParseError : public RuntimeError
|
|
{
|
|
DEFINE_RUNTIME_EXCEPTION(ParseError, RuntimeError, pxL("Parse error"));
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Hardware/OS Exceptions:
|
|
// HardwareDeficiency / VirtualMemoryMapConflict
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
// This exception is a specific type of OutOfMemory error that isn't "really" an out of
|
|
// memory error. More likely it's caused by a plugin or driver reserving a range of memory
|
|
// we'd really like to have access to.
|
|
class VirtualMemoryMapConflict : public OutOfMemory
|
|
{
|
|
DEFINE_RUNTIME_EXCEPTION(VirtualMemoryMapConflict, OutOfMemory, wxEmptyString)
|
|
|
|
VirtualMemoryMapConflict(const wxString &allocdesc);
|
|
|
|
virtual wxString FormatDisplayMessage() const;
|
|
virtual wxString FormatDiagnosticMessage() const;
|
|
};
|
|
|
|
class HardwareDeficiency : public RuntimeError
|
|
{
|
|
public:
|
|
DEFINE_RUNTIME_EXCEPTION(HardwareDeficiency, RuntimeError, pxL("Your machine's hardware is incapable of running PCSX2. Sorry dood."));
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Streaming (file) Exceptions:
|
|
// Stream / BadStream / CannotCreateStream / FileNotFound / AccessDenied / EndOfStream
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
#define DEFINE_STREAM_EXCEPTION_ACCESSORS(classname) \
|
|
virtual classname &SetStreamName(const wxString &name) \
|
|
{ \
|
|
StreamName = name; \
|
|
return *this; \
|
|
} \
|
|
virtual classname &SetStreamName(const char *name) \
|
|
{ \
|
|
StreamName = fromUTF8(name); \
|
|
return *this; \
|
|
}
|
|
|
|
#define DEFINE_STREAM_EXCEPTION(classname, parent) \
|
|
DEFINE_RUNTIME_EXCEPTION(classname, parent, wxEmptyString) \
|
|
classname(const wxString &filename) \
|
|
{ \
|
|
StreamName = filename; \
|
|
} \
|
|
DEFINE_STREAM_EXCEPTION_ACCESSORS(classname)
|
|
|
|
// A generic base error class for bad streams -- corrupted data, sudden closures, loss of
|
|
// connection, or anything else that would indicate a failure to open a stream or read the
|
|
// data after the stream was successfully opened.
|
|
//
|
|
class BadStream : public RuntimeError
|
|
{
|
|
DEFINE_STREAM_EXCEPTION(BadStream, RuntimeError)
|
|
|
|
public:
|
|
wxString StreamName; // name of the stream (if applicable)
|
|
|
|
virtual wxString FormatDiagnosticMessage() const;
|
|
virtual wxString FormatDisplayMessage() const;
|
|
|
|
protected:
|
|
void _formatDiagMsg(FastFormatUnicode &dest) const;
|
|
void _formatUserMsg(FastFormatUnicode &dest) const;
|
|
};
|
|
|
|
// A generic exception for odd-ball stream creation errors.
|
|
//
|
|
class CannotCreateStream : public BadStream
|
|
{
|
|
DEFINE_STREAM_EXCEPTION(CannotCreateStream, BadStream)
|
|
|
|
virtual wxString FormatDiagnosticMessage() const;
|
|
virtual wxString FormatDisplayMessage() const;
|
|
};
|
|
|
|
// Exception thrown when an attempt to open a non-existent file is made.
|
|
// (this exception can also mean file permissions are invalid)
|
|
//
|
|
class FileNotFound : public CannotCreateStream
|
|
{
|
|
public:
|
|
DEFINE_STREAM_EXCEPTION(FileNotFound, CannotCreateStream)
|
|
|
|
virtual wxString FormatDiagnosticMessage() const;
|
|
virtual wxString FormatDisplayMessage() const;
|
|
};
|
|
|
|
class AccessDenied : public CannotCreateStream
|
|
{
|
|
public:
|
|
DEFINE_STREAM_EXCEPTION(AccessDenied, CannotCreateStream)
|
|
|
|
virtual wxString FormatDiagnosticMessage() const;
|
|
virtual wxString FormatDisplayMessage() const;
|
|
};
|
|
|
|
// EndOfStream can be used either as an error, or used just as a shortcut for manual
|
|
// feof checks.
|
|
//
|
|
class EndOfStream : public BadStream
|
|
{
|
|
public:
|
|
DEFINE_STREAM_EXCEPTION(EndOfStream, BadStream)
|
|
|
|
virtual wxString FormatDiagnosticMessage() const;
|
|
virtual wxString FormatDisplayMessage() const;
|
|
};
|
|
|
|
#ifdef __WXMSW__
|
|
// --------------------------------------------------------------------------------------
|
|
// Exception::WinApiError
|
|
// --------------------------------------------------------------------------------------
|
|
class WinApiError : public RuntimeError
|
|
{
|
|
DEFINE_EXCEPTION_COPYTORS(WinApiError, RuntimeError)
|
|
DEFINE_EXCEPTION_MESSAGES(WinApiError)
|
|
|
|
public:
|
|
int ErrorId;
|
|
|
|
public:
|
|
WinApiError();
|
|
|
|
wxString GetMsgFromWindows() const;
|
|
virtual wxString FormatDisplayMessage() const;
|
|
virtual wxString FormatDiagnosticMessage() const;
|
|
};
|
|
#endif
|
|
}
|
|
|
|
using Exception::BaseException;
|
|
using Exception::ScopedExcept;
|