Index: ps/trunk/build/errorlist/errorlist.exe =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: ps/trunk/build/errorlist/errorlist.exe =================================================================== --- ps/trunk/build/errorlist/errorlist.exe (revision 7258) +++ ps/trunk/build/errorlist/errorlist.exe (nonexistent) Property changes on: ps/trunk/build/errorlist/errorlist.exe ___________________________________________________________________ Deleted: svn:executable ## -1 +0,0 ## -* \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -application/octet-stream \ No newline at end of property Index: ps/trunk/source/scriptinterface/NativeWrapperDecls.h =================================================================== --- ps/trunk/source/scriptinterface/NativeWrapperDecls.h (nonexistent) +++ ps/trunk/source/scriptinterface/NativeWrapperDecls.h (revision 7259) @@ -0,0 +1,70 @@ +/* Copyright (C) 2009 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#include +#include + +// Define lots of useful macros: + +// Varieties of comma-separated list to fit on the head/tail/whole of another comma-separated list +#define NUMBERED_LIST_HEAD(z, i, data) data##i, +#define NUMBERED_LIST_TAIL(z, i, data) ,data##i +#define NUMBERED_LIST_BALANCED(z, i, data) BOOST_PP_COMMA_IF(i) data##i +// Some other things +#define TYPED_ARGS(z, i, data) , T##i a##i +#define CONVERT_ARG(z, i, data) T##i a##i; if (! ScriptInterface::FromJSVal(cx, argv[i], a##i)) return JS_FALSE; + +// List-generating macros, named roughly after their first list item +#define TYPENAME_T0_HEAD(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_HEAD, typename T) // "typename T0, typename T1, " +#define TYPENAME_T0_TAIL(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_TAIL, typename T) // ", typename T0, typename T1" +#define T0(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_BALANCED, T) // "T0, T1" +#define T0_HEAD(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_HEAD, T) // "T0, T1, " +#define T0_TAIL(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_TAIL, T) // ", T0, T1" +#define T0_A0(z, i) BOOST_PP_REPEAT_##z (i, TYPED_ARGS, ~) // "T0 a0, T1 a1" +#define A0(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_BALANCED, a) // "a0, a1" +#define A0_TAIL(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_TAIL, a) // ", a0, a1" + +// Define RegisterFunction +#define OVERLOADS(z, i, data) \ + template \ + void RegisterFunction(const char* name) { \ + Register(name, call, nargs<0 T0_TAIL(z,i)>()); \ + } +BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) +#undef OVERLOADS + +// JSNative-compatible function that wraps the function identified in the template argument list +// (Definition comes later, since it depends on some things we haven't defined yet) +#define OVERLOADS(z, i, data) \ + template \ + static JSBool call(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval); +BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) +#undef OVERLOADS + +// Similar, for class methods +#define OVERLOADS(z, i, data) \ + template \ + static JSBool callMethod(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval); +BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) +#undef OVERLOADS + +// Argument-number counter +#define OVERLOADS(z, i, data) \ + template /* add a dummy parameter so we still compile with 0 template args */ \ + static size_t nargs() { return i; } +BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) +#undef OVERLOADS Property changes on: ps/trunk/source/scriptinterface/NativeWrapperDecls.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/scriptinterface/NativeWrapperDefns.h =================================================================== --- ps/trunk/source/scriptinterface/NativeWrapperDefns.h (nonexistent) +++ ps/trunk/source/scriptinterface/NativeWrapperDefns.h (revision 7259) @@ -0,0 +1,116 @@ +/* Copyright (C) 2009 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +// (NativeWrapperDecls.h set up a lot of the macros we use here) + + +// ScriptInterface_NativeWrapper::call(cx, rval, fptr, args...) will call fptr(cbdata, args...), +// and if T != void then it will store the result in rval: + +// Templated on the return type so void can be handled separately +template +struct ScriptInterface_NativeWrapper { + #define OVERLOADS(z, i, data) \ + template \ + static void call(JSContext* cx, jsval& rval, F fptr T0_A0(z,i)) { \ + rval = ScriptInterface::ToJSVal(cx, fptr(ScriptInterface::GetCallbackData(cx) A0_TAIL(z,i))); \ + } + + BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) + #undef OVERLOADS +}; + +// Overloaded to ignore the return value from void functions +template <> +struct ScriptInterface_NativeWrapper { + #define OVERLOADS(z, i, data) \ + template \ + static void call(JSContext* cx, jsval& /*rval*/, F fptr T0_A0(z,i)) { \ + fptr(ScriptInterface::GetCallbackData(cx) A0_TAIL(z,i)); \ + } + BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) + #undef OVERLOADS +}; + +// Same idea but for method calls: + +template +struct ScriptInterface_NativeMethodWrapper { + #define OVERLOADS(z, i, data) \ + template \ + static void call(JSContext* cx, jsval& rval, TC* c, F fptr T0_A0(z,i)) { \ + rval = ScriptInterface::ToJSVal(cx, (c->*fptr)( A0(z,i) )); \ + } + + BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) + #undef OVERLOADS +}; + +template +struct ScriptInterface_NativeMethodWrapper { + #define OVERLOADS(z, i, data) \ + template \ + static void call(JSContext* /*cx*/, jsval& /*rval*/, TC* c, F fptr T0_A0(z,i)) { \ + (c->*fptr)( A0(z,i) ); \ + } + BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) + #undef OVERLOADS +}; + + + +// JSNative-compatible function that wraps the function identified in the template argument list +#define OVERLOADS(z, i, data) \ + template \ + JSBool ScriptInterface::call(JSContext* cx, JSObject* /*obj*/, uintN /*argc*/, jsval* argv, jsval* rval) { \ + (void)argv; /* avoid 'unused parameter' warnings */ \ + BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \ + ScriptInterface_NativeWrapper::call(cx, *rval, fptr A0_TAIL(z,i)); \ + return (ScriptInterface::IsExceptionPending(cx) ? JS_FALSE : JS_TRUE); \ + } +BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) +#undef OVERLOADS + +// Same idea but for methods +#define OVERLOADS(z, i, data) \ + template \ + JSBool ScriptInterface::callMethod(JSContext* cx, JSObject* obj, uintN /*argc*/, jsval* argv, jsval* rval) { \ + (void)argv; /* avoid 'unused parameter' warnings */ \ + if (ScriptInterface::GetClass(cx, obj) != CLS) return JS_FALSE; \ + TC* c = static_cast(ScriptInterface::GetPrivate(cx, obj)); \ + if (! c) return JS_FALSE; \ + BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \ + ScriptInterface_NativeMethodWrapper::call(cx, *rval, c, fptr A0_TAIL(z,i)); \ + return (ScriptInterface::IsExceptionPending(cx) ? JS_FALSE : JS_TRUE); \ + } +BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) +#undef OVERLOADS + +// Clean up our mess +#undef NUMBERED_LIST_HEAD +#undef NUMBERED_LIST_TAIL +#undef NUMBERED_LIST_BALANCED +#undef TYPED_ARGS +#undef CONVERT_ARG +#undef TYPENAME_T0_HEAD +#undef TYPENAME_T0_TAIL +#undef T0 +#undef T0_HEAD +#undef T0_TAIL +#undef T0_A0 +#undef A0 +#undef A0_TAIL Property changes on: ps/trunk/source/scriptinterface/NativeWrapperDefns.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/gui/GUIManager.h =================================================================== --- ps/trunk/source/gui/GUIManager.h (revision 7258) +++ ps/trunk/source/gui/GUIManager.h (revision 7259) @@ -1,89 +1,116 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + #ifndef INCLUDED_GUIMANAGER #define INCLUDED_GUIMANAGER #include "lib/input.h" #include "lib/file/vfs/vfs_path.h" #include "ps/CStr.h" -#include "scripting/SpiderMonkey.h" +#include "scriptinterface/ScriptVal.h" #include class CGUI; struct JSObject; class IGUIObject; struct CColor; struct SGUIIcon; /** * External interface to the GUI system. * * The GUI consists of a set of pages. Each page is constructed from a * series of XML files, and is independent from any other page. * Only one page is active at a time. All events and render requests etc * will go to the active page. This lets the GUI switch between pre-game menu * and in-game UI. */ class CGUIManager { + NONCOPYABLE(CGUIManager); public: - CGUIManager(); + CGUIManager(ScriptInterface& scriptInterface); ~CGUIManager(); - // Load a new GUI page and make it active, All current pages will be destroyed. - void SwitchPage(const CStrW& name, jsval initData); + ScriptInterface& GetScriptInterface() { return m_ScriptInterface; } + + // Load a new GUI page and make it active. All current pages will be destroyed. + void SwitchPage(const CStrW& name, CScriptVal initData); // Load a new GUI page and make it active. All current pages will be retained, // and will still be drawn and receive tick events, but will not receive // user inputs. - void PushPage(const CStrW& name, jsval initData); + void PushPage(const CStrW& name, CScriptVal initData); // Unload the currently active GUI page, and make the previous page active. // (There must be at least two pages when you call this.) void PopPage(); // Hotload pages when their .xml files have changed LibError ReloadChangedFiles(const VfsPath& path); + // Handle input events + InReaction HandleEvent(const SDL_Event_* ev); + // These functions are all equivalent to the CGUI functions of the same // name, applied to the currently active GUI page: bool GetPreDefinedColor(const CStr& name, CColor& output); bool IconExists(const CStr& str) const; SGUIIcon GetIcon(const CStr& str) const; IGUIObject* FindObjectByName(const CStr& name) const; void SendEventToAll(const CStr& eventName); void TickObjects(); void Draw(); void UpdateResolution(); JSObject* GetScriptObject(); - InReaction HandleEvent(const SDL_Event_* ev); - private: struct SGUIPage { SGUIPage(); SGUIPage(const SGUIPage&); ~SGUIPage(); + CStrW name; std::set inputs; // for hotloading - jsval initData; - shared_ptr gui; + + JSContext* cx; + CScriptVal initData; // data to be passed to the init() function + + shared_ptr gui; // the actual GUI page }; void LoadPage(SGUIPage& page); shared_ptr top() const; typedef std::vector PageStackType; PageStackType m_PageStack; + + ScriptInterface& m_ScriptInterface; }; extern CGUIManager* g_GUI; extern InReaction gui_handler(const SDL_Event_* ev); #endif // INCLUDED_GUIMANAGER Index: ps/trunk/source/gui/IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/IGUIObject.cpp (revision 7258) +++ ps/trunk/source/gui/IGUIObject.cpp (revision 7259) @@ -1,569 +1,566 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /* IGUIObject */ #include "precompiled.h" #include "GUI.h" #include "ps/Parser.h" #include "gui/scripting/JSInterface_IGUIObject.h" #include "gui/scripting/JSInterface_GUITypes.h" #include "ps/CLogger.h" #define LOG_CATEGORY L"gui" extern int g_xres, g_yres; //------------------------------------------------------------------- // Implementation Macros //------------------------------------------------------------------- //------------------------------------------------------------------- // Constructor / Destructor //------------------------------------------------------------------- IGUIObject::IGUIObject() : m_pGUI(NULL), m_pParent(NULL), m_MouseHovering(false), m_JSObject(NULL) { AddSetting(GUIST_bool, "enabled"); AddSetting(GUIST_bool, "hidden"); AddSetting(GUIST_CClientArea, "size"); AddSetting(GUIST_CStr, "style"); AddSetting(GUIST_CStr, "hotkey" ); AddSetting(GUIST_float, "z"); AddSetting(GUIST_bool, "absolute"); AddSetting(GUIST_bool, "ghost"); // Setup important defaults GUI::SetSetting(this, "hidden", false); GUI::SetSetting(this, "ghost", false); GUI::SetSetting(this, "enabled", true); GUI::SetSetting(this, "absolute", true); for (int i=0; i<6; i++) m_LastClickTime[i]=0; bool hidden=true; GUI::GetSetting(this, "hidden", hidden); } IGUIObject::~IGUIObject() { { std::map::iterator it; for (it = m_Settings.begin(); it != m_Settings.end(); ++it) { switch (it->second.m_Type) { // delete() needs to know the type of the variable - never delete a void* #define TYPE(t) case GUIST_##t: delete (t*)it->second.m_pSetting; break; #include "GUItypes.h" #undef TYPE default: debug_warn(L"Invalid setting type"); } } } { std::map::iterator it; for (it = m_ScriptHandlers.begin(); it != m_ScriptHandlers.end(); ++it) { JS_RemoveRoot(g_ScriptingHost.getContext(), it->second); delete it->second; } } if (m_JSObject) JS_RemoveRoot(g_ScriptingHost.getContext(), &m_JSObject); } //------------------------------------------------------------------- // Functions //------------------------------------------------------------------- void IGUIObject::AddChild(IGUIObject *pChild) { // // debug_assert(pChild); pChild->SetParent(this); m_Children.push_back(pChild); // If this (not the child) object is already attached // to a CGUI, it pGUI pointer will be non-null. // This will mean we'll have to check if we're using // names already used. if (pChild->GetGUI()) { try { // Atomic function, if it fails it won't // have changed anything //UpdateObjects(); pChild->GetGUI()->UpdateObjects(); } catch (PSERROR_GUI&) { // If anything went wrong, reverse what we did and throw // an exception telling it never added a child m_Children.erase( m_Children.end()-1 ); throw; } } // else do nothing } void IGUIObject::AddToPointersMap(map_pObjects &ObjectMap) { // Just don't do anything about the top node if (m_pParent == NULL) return; // Now actually add this one // notice we won't add it if it's doesn't have any parent // (i.e. being the base object) if (m_Name.empty()) { throw PSERROR_GUI_ObjectNeedsName(); } if (ObjectMap.count(m_Name) > 0) { throw PSERROR_GUI_NameAmbiguity(); } else { ObjectMap[m_Name] = this; } } void IGUIObject::Destroy() { // Is there anything besides the children to destroy? } // Notice if using this, the naming convention of GUIST_ should be strict. #define TYPE(type) \ case GUIST_##type: \ m_Settings[Name].m_pSetting = new type(); \ break; void IGUIObject::AddSetting(const EGUISettingType &Type, const CStr& Name) { // Is name already taken? if (m_Settings.count(Name) >= 1) return; // Construct, and set type m_Settings[Name].m_Type = Type; switch (Type) { // Construct the setting. #include "GUItypes.h" default: debug_warn(L"IGUIObject::AddSetting failed, type not recognized!"); break; } } #undef TYPE bool IGUIObject::MouseOver() { if(!GetGUI()) throw PSERROR_GUI_OperationNeedsGUIObject(); return m_CachedActualSize.PointInside(GetMousePos()); } CPos IGUIObject::GetMousePos() const { return ((GetGUI())?(GetGUI()->m_MousePos):CPos()); } void IGUIObject::UpdateMouseOver(IGUIObject * const &pMouseOver) { // Check if this is the object being hovered. if (pMouseOver == this) { if (!m_MouseHovering) { // It wasn't hovering, so that must mean it just entered HandleMessage(GUIM_MOUSE_ENTER); ScriptEvent("mouseenter"); } // Either way, set to true m_MouseHovering = true; // call mouse over HandleMessage(GUIM_MOUSE_OVER); ScriptEvent("mousemove"); } else // Some other object (or none) is hovered { if (m_MouseHovering) { m_MouseHovering = false; HandleMessage(GUIM_MOUSE_LEAVE); ScriptEvent("mouseleave"); } } } bool IGUIObject::SettingExists(const CStr& Setting) const { // Because GetOffsets will direct dynamically defined // classes with polymorphism to respective ms_SettingsInfo // we need to make no further updates on this function // in derived classes. //return (GetSettingsInfo().count(Setting) >= 1); return (m_Settings.count(Setting) >= 1); } #define TYPE(type) \ else \ if (set.m_Type == GUIST_##type) \ { \ type _Value; \ if (!GUI::ParseString(Value, _Value)) \ return PSRETURN_GUI_UnableToParse; \ \ GUI::SetSetting(this, Setting, _Value, SkipMessage); \ } PSRETURN IGUIObject::SetSetting(const CStr& Setting, const CStr& Value, const bool& SkipMessage) { if (!SettingExists(Setting)) { return PSRETURN_GUI_InvalidSetting; } // Get setting SGUISetting set = m_Settings[Setting]; if (0); // else... #include "GUItypes.h" else { // Why does it always fail? //return PS_FAIL; return LogInvalidSettings(Setting); } return PSRETURN_OK; } #undef TYPE PSRETURN IGUIObject::GetSettingType(const CStr& Setting, EGUISettingType &Type) const { if (!SettingExists(Setting)) { return LogInvalidSettings(Setting); } if (m_Settings.find(Setting) == m_Settings.end()) { return LogInvalidSettings(Setting); } Type = m_Settings.find(Setting)->second.m_Type; return PSRETURN_OK; } void IGUIObject::ChooseMouseOverAndClosest(IGUIObject* &pObject) { if (MouseOver()) { // Check if we've got competition at all if (pObject == NULL) { pObject = this; return; } // Or if it's closer if (GetBufferedZ() >= pObject->GetBufferedZ()) { pObject = this; return; } } } IGUIObject *IGUIObject::GetParent() const { // Important, we're not using GetParent() for these // checks, that could screw it up if (m_pParent) { if (m_pParent->m_pParent == NULL) return NULL; } return m_pParent; } void IGUIObject::UpdateCachedSize() { bool absolute; GUI::GetSetting(this, "absolute", absolute); CClientArea ca; GUI::GetSetting(this, "size", ca); // If absolute="false" and the object has got a parent, // use its cached size instead of the screen. Notice // it must have just been cached for it to work. if (absolute == false && m_pParent && !IsRootObject()) m_CachedActualSize = ca.GetClientArea(m_pParent->m_CachedActualSize); else m_CachedActualSize = ca.GetClientArea(CRect(0.f, 0.f, (float)g_xres, (float)g_yres)); } void IGUIObject::LoadStyle(CGUI &GUIinstance, const CStr& StyleName) { // Fetch style if (GUIinstance.m_Styles.count(StyleName)==1) { LoadStyle(GUIinstance.m_Styles[StyleName]); } else { debug_warn(L"IGUIObject::LoadStyle failed"); } } void IGUIObject::LoadStyle(const SGUIStyle &Style) { // Iterate settings, it won't be able to set them all probably, but that doesn't matter std::map::const_iterator cit; for (cit = Style.m_SettingsDefaults.begin(); cit != Style.m_SettingsDefaults.end(); ++cit) { // Try set setting in object SetSetting(cit->first, cit->second); // It doesn't matter if it fail, it's not suppose to be able to set every setting. // since it's generic. // The beauty with styles is that it can contain more settings // than exists for the objects using it. So if the SetSetting // fails, don't care. } } float IGUIObject::GetBufferedZ() const { bool absolute; GUI::GetSetting(this, "absolute", absolute); float Z; GUI::GetSetting(this, "z", Z); if (absolute) return Z; else { if (GetParent()) return GetParent()->GetBufferedZ() + Z; else { // In philosophy, a parentless object shouldn't be able to have a relative sizing, // but we'll accept it so that absolute can be used as default without a complaint. // Also, you could consider those objects children to the screen resolution. return Z; } } } // TODO Gee: keep this function and all??? void IGUIObject::CheckSettingsValidity() { bool hidden; GUI::GetSetting(this, "hidden", hidden); // If we hide an object, reset many of its parts if (hidden) { // Simulate that no object is hovered for this object and all its children // why? because it's try { GUI::RecurseObject(0, this, &IGUIObject::UpdateMouseOver, NULL); } catch (PSERROR_GUI&) { } } try { // Send message to itself HandleMessage(GUIM_SETTINGS_UPDATED); ScriptEvent("update"); } catch (PSERROR_GUI&) { } } void IGUIObject::RegisterScriptHandler(const CStr& Action, const CStr& Code, CGUI* pGUI) { const int paramCount = 1; const char* paramNames[paramCount] = { "mouse" }; // Location to report errors from CStr CodeName = GetName()+" "+Action; // Generate a unique name static int x=0; char buf[64]; sprintf_s(buf, ARRAY_SIZE(buf), "__eventhandler%d", x++); JSFunction* func = JS_CompileFunction(g_ScriptingHost.getContext(), pGUI->m_ScriptObject, - buf, paramCount, paramNames, (const char*)Code, Code.length(), CodeName, 0); + buf, paramCount, paramNames, Code.c_str(), Code.length(), CodeName, 0); debug_assert(func); // TODO: Handle errors if (func) SetScriptHandler(Action, JS_GetFunctionObject(func)); } void IGUIObject::SetScriptHandler(const CStr& Action, JSObject* Function) { JSObject** obj = new JSObject*; *obj = Function; JS_AddRoot(g_ScriptingHost.getContext(), obj); if (m_ScriptHandlers[Action]) { JS_RemoveRoot(g_ScriptingHost.getContext(), m_ScriptHandlers[Action]); delete m_ScriptHandlers[Action]; } m_ScriptHandlers[Action] = obj; } void IGUIObject::ScriptEvent(const CStr& Action) { std::map::iterator it = m_ScriptHandlers.find(Action); if (it == m_ScriptHandlers.end()) return; // PRIVATE_TO_JSVAL assumes two-byte alignment, // so make sure that's always true debug_assert(! ((jsval)this & JSVAL_INT)); // The IGUIObject needs to be stored inside the script's object jsval guiObject = PRIVATE_TO_JSVAL(this); // Make a 'this', allowing access to the IGUIObject JSObject* jsGuiObject = JS_ConstructObjectWithArguments(g_ScriptingHost.getContext(), &JSI_IGUIObject::JSI_class, NULL, m_pGUI->m_ScriptObject, 1, &guiObject); debug_assert(jsGuiObject); // TODO: Handle errors // Prevent it from being garbage-collected before it's passed into the function JS_AddRoot(g_ScriptingHost.getContext(), &jsGuiObject); // Set up the 'mouse' parameter jsval mouseParams[3]; mouseParams[0] = INT_TO_JSVAL(m_pGUI->m_MousePos.x); mouseParams[1] = INT_TO_JSVAL(m_pGUI->m_MousePos.y); mouseParams[2] = INT_TO_JSVAL(m_pGUI->m_MouseButtons); JSObject* mouseObj = JS_ConstructObjectWithArguments(g_ScriptingHost.getContext(), &JSI_GUIMouse::JSI_class, NULL, m_pGUI->m_ScriptObject, 3, mouseParams); debug_assert(mouseObj); // TODO: Handle errors // Don't garbage collect the mouse JS_AddRoot(g_ScriptingHost.getContext(), &mouseObj); - const int paramCount = 1; - jsval paramData[paramCount]; - paramData[0] = OBJECT_TO_JSVAL(mouseObj); + jsval paramData[] = { OBJECT_TO_JSVAL(mouseObj) }; jsval result; - - JSBool ok = JS_CallFunctionValue(g_ScriptingHost.getContext(), jsGuiObject, OBJECT_TO_JSVAL(*it->second), 1, paramData, &result); + JSBool ok = JS_CallFunctionValue(g_ScriptingHost.getContext(), jsGuiObject, OBJECT_TO_JSVAL(*it->second), ARRAY_SIZE(paramData), paramData, &result); if (!ok) { JS_ReportError(g_ScriptingHost.getContext(), "Errors executing script action \"%s\"", Action.c_str()); } // Allow the temporary parameters to be garbage-collected JS_RemoveRoot(g_ScriptingHost.getContext(), &mouseObj); JS_RemoveRoot(g_ScriptingHost.getContext(), &jsGuiObject); } JSObject* IGUIObject::GetJSObject() { // Cache the object when somebody first asks for it, because otherwise // we end up doing far too much object allocation. TODO: Would be nice to // not have these objects hang around forever using up memory, though. if (! m_JSObject) { m_JSObject = JS_NewObject(g_ScriptingHost.getContext(), &JSI_IGUIObject::JSI_class, NULL, NULL); JS_AddRoot(g_ScriptingHost.getContext(), &m_JSObject); JS_SetPrivate(g_ScriptingHost.getContext(), m_JSObject, this); } return m_JSObject; } CStr IGUIObject::GetPresentableName() const { // __internal(), must be at least 13 letters to be able to be // an internal name if (m_Name.length() <= 12) return m_Name; if (m_Name.substr(0, 10) == "__internal") return CStr("[unnamed object]"); else return m_Name; } void IGUIObject::SetFocus() { GetGUI()->m_FocusedObject = this; } bool IGUIObject::IsFocused() const { return GetGUI()->m_FocusedObject == this; } bool IGUIObject::IsRootObject() const { return (GetGUI() != 0 && m_pParent == GetGUI()->m_BaseObject); } PSRETURN IGUIObject::LogInvalidSettings(const CStr8 &Setting) const { LOG(CLogger::Warning, LOG_CATEGORY, L"IGUIObject: setting %hs was not found on an object", Setting.c_str()); return PSRETURN_GUI_InvalidSetting; } Index: ps/trunk/source/scriptinterface/ScriptTypes.h =================================================================== --- ps/trunk/source/scriptinterface/ScriptTypes.h (nonexistent) +++ ps/trunk/source/scriptinterface/ScriptTypes.h (revision 7259) @@ -0,0 +1,39 @@ +/* Copyright (C) 2009 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#ifndef INCLUDED_SCRIPTTYPES +#define INCLUDED_SCRIPTTYPES + +#ifdef _WIN32 +# define XP_WIN +#else +# define XP_UNIX +#endif +// (we don't support XP_OS2 or XP_BEOS) + +#include "js/jspubtd.h" + +#if JS_VERSION <= 160 +// 1.6 doesn't support the standards-conforming globals behaviour, +// so we can't use this flag there +# define JSCLASS_GLOBAL_FLAGS 0 +#endif + +class ScriptInterface; +class CScriptVal; + +#endif // INCLUDED_SCRIPTTYPES Property changes on: ps/trunk/source/scriptinterface/ScriptTypes.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/scriptinterface/tests/test_ScriptVal.h =================================================================== --- ps/trunk/source/scriptinterface/tests/test_ScriptVal.h (nonexistent) +++ ps/trunk/source/scriptinterface/tests/test_ScriptVal.h (revision 7259) @@ -0,0 +1,50 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#include "lib/self_test.h" + +#include "scriptinterface/ScriptInterface.h" +#include "scriptinterface/ScriptVal.h" + +#include "js/jsapi.h" + +class TestScriptVal : public CxxTest::TestSuite +{ +public: + void test_rooting() + { + ScriptInterface script("Test"); + JSContext* cx = script.GetContext(); + + JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL); + TS_ASSERT(obj); + + CScriptValRooted root(cx, OBJECT_TO_JSVAL(obj)); + + JS_GC(cx); + + jsval val = INT_TO_JSVAL(123); + TS_ASSERT(JS_SetProperty(cx, obj, "test", &val)); + + JS_GC(cx); + + jsval rval; + TS_ASSERT(JS_GetProperty(cx, obj, "test", &rval)); + TS_ASSERT(JSVAL_IS_INT(rval)); + TS_ASSERT(JSVAL_TO_INT(rval) == 123); + } +}; Property changes on: ps/trunk/source/scriptinterface/tests/test_ScriptVal.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/scriptinterface/ScriptVal.cpp =================================================================== --- ps/trunk/source/scriptinterface/ScriptVal.cpp (nonexistent) +++ ps/trunk/source/scriptinterface/ScriptVal.cpp (revision 7259) @@ -0,0 +1,50 @@ +/* Copyright (C) 2009 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#include "precompiled.h" + +#include "ScriptVal.h" + +#include "js/jsapi.h" + +struct Unrooter +{ + Unrooter(JSContext* cx) : cx(cx) { } + void operator()(jsval* p) { JS_RemoveRoot(cx, p); delete p; } + JSContext* cx; +}; + +CScriptValRooted::CScriptValRooted(JSContext* cx, jsval val) +{ + jsval* p = new jsval(val); + JS_AddNamedRoot(cx, p, "CScriptValRooted"); + m_Val = boost::shared_ptr(p, Unrooter(cx)); +} + +CScriptValRooted::CScriptValRooted(JSContext* cx, CScriptVal val) +{ + jsval* p = new jsval(val.get()); + JS_AddNamedRoot(cx, p, "CScriptValRooted"); + m_Val = boost::shared_ptr(p, Unrooter(cx)); +} + +jsval CScriptValRooted::get() const +{ + if (!m_Val) + return JSVAL_VOID; + return *m_Val; +} Property changes on: ps/trunk/source/scriptinterface/ScriptVal.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/scriptinterface/ScriptVal.h =================================================================== --- ps/trunk/source/scriptinterface/ScriptVal.h (nonexistent) +++ ps/trunk/source/scriptinterface/ScriptVal.h (revision 7259) @@ -0,0 +1,54 @@ +/* Copyright (C) 2009 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#ifndef INCLUDED_SCRIPTVAL +#define INCLUDED_SCRIPTVAL + +#include "ScriptTypes.h" +#include + +/** + * A trivial wrapper around a jsval. Used to avoid template overload ambiguities + * with jsval (which is just an integer), for any code that uses + * ScriptInterface::ToJSVal or ScriptInterface::FromJSVal + */ +class CScriptVal +{ +public: + CScriptVal() : m_Val(0) { } + CScriptVal(jsval val) : m_Val(val) { } + + jsval get() const { return m_Val; } + +private: + jsval m_Val; +}; + +class CScriptValRooted +{ +public: + CScriptValRooted() { } + CScriptValRooted(JSContext* cx, jsval val); + CScriptValRooted(JSContext* cx, CScriptVal val); + + jsval get() const; + +private: + boost::shared_ptr m_Val; +}; + +#endif // INCLUDED_SCRIPTVAL Property changes on: ps/trunk/source/scriptinterface/ScriptVal.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/network/SocketBase.cpp =================================================================== --- ps/trunk/source/network/SocketBase.cpp (revision 7258) +++ ps/trunk/source/network/SocketBase.cpp (revision 7259) @@ -1,999 +1,1001 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #if OS_WIN #include "lib/sysdep/os/win/win.h" #include "lib/sysdep/os/win/wposix/wsock.h" #include "lib/sysdep/os/win/wposix/wsock_internal.h" #endif +#include "lib/sysdep/cpu.h" + #include "Network.h" #include "NetworkInternal.h" #include "ps/CStr.h" // ERROR is defined by some windows header. Undef it #undef ERROR #include "ps/CLogger.h" #include "NetLog.h" #if !OS_WIN # include # include # include #endif #define LOG_CATEGORY L"net" // Record global transfer statistics (sent/recvd bytes). This will put a lock // /unlock pair in all read and write operations. #define RECORD_GLOBAL_STATS 1 #define GLOBAL_LOCK() pthread_mutex_lock(&g_SocketSetInternal.m_Mutex) #define GLOBAL_UNLOCK() pthread_mutex_unlock(&g_SocketSetInternal.m_Mutex) CSocketSetInternal g_SocketSetInternal; DEFINE_ERROR(NO_SUCH_HOST, "Host not found"); DEFINE_ERROR(CONNECT_TIMEOUT, "The connection attempt timed out"); DEFINE_ERROR(CONNECT_REFUSED, "The connection attempt was refused"); DEFINE_ERROR(NO_ROUTE_TO_HOST, "No route to host"); DEFINE_ERROR(CONNECTION_BROKEN, "The connection has been closed"); DEFINE_ERROR(CONNECT_IN_PROGRESS, "The connect attempt has started, but is not yet complete"); DEFINE_ERROR(PORT_IN_USE, "The port is already in use by another process"); DEFINE_ERROR(INVALID_PORT, "The port specified is either invalid, or forbidden by system or firewall policy"); DEFINE_ERROR(INVALID_PROTOCOL, "The socket type or protocol is not supported by the operating system. Make sure that the TCP/IP protocol is installed and activated"); // Map an OS error number to a PS_RESULT PS_RESULT GetPS_RESULT(int error) { switch (error) { case EWOULDBLOCK: case EINPROGRESS: return PS_OK; case ENETUNREACH: case ENETDOWN: case EADDRNOTAVAIL: return NO_ROUTE_TO_HOST; case ETIMEDOUT: return CONNECT_TIMEOUT; case ECONNREFUSED: return CONNECT_REFUSED; default: char buf[256]; Network_GetErrorString(error, buf, sizeof(buf)); LOG(CLogger::Error, LOG_CATEGORY, L"SocketBase.cpp::GetPS_RESULT(): Unrecognized error %hs[%d]", buf, error); return PS_FAIL; } } CSocketAddress::CSocketAddress(int port, ESocketProtocol proto) { memset(&m_Union, 0, sizeof(m_Union)); switch (proto) { case IPv4: m_Union.m_IPv4.sin_family=PF_INET; m_Union.m_IPv4.sin_addr.s_addr=htonl(INADDR_ANY); m_Union.m_IPv4.sin_port=htons(port); break; case IPv6: m_Union.m_IPv6.sin6_family=PF_INET6; cpu_memcpy(&m_Union.m_IPv6.sin6_addr, &in6addr_any, sizeof(in6addr_any)); m_Union.m_IPv6.sin6_port=htons(port); break; default: debug_warn(L"CSocketAddress::CSocketAddress: Bad proto"); } } CSocketAddress CSocketAddress::Loopback(int port, ESocketProtocol proto) { CSocketAddress ret; switch (proto) { case IPv4: ret.m_Union.m_IPv4.sin_family=PF_INET; ret.m_Union.m_IPv4.sin_addr.s_addr=htonl(INADDR_LOOPBACK); ret.m_Union.m_IPv4.sin_port=htons(port); break; case IPv6: ret.m_Union.m_IPv6.sin6_family=PF_INET6; cpu_memcpy(&ret.m_Union.m_IPv6.sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)); ret.m_Union.m_IPv6.sin6_port=htons(port); break; default: debug_warn(L"CSocketAddress::CSocketAddress: Bad proto"); } return ret; } PS_RESULT CSocketAddress::Resolve(const char *name, int port, CSocketAddress &addr) { // Use IPV4 by default, ignoring address type. memset(&addr.m_Union, 0, sizeof(addr.m_Union)); hostent *he; addr.m_Union.m_IPv4.sin_family=AF_INET; addr.m_Union.m_IPv4.sin_port=htons(port); // Try to parse dot-notation IP addr.m_Union.m_IPv4.sin_addr.s_addr=inet_addr(name); if (addr.m_Union.m_IPv4.sin_addr.s_addr==INADDR_NONE) // Not a dotted IP, try name resolution { he=gethostbyname(name); if (!he) return NO_SUCH_HOST; addr.m_Union.m_IPv4.sin_addr=*(struct in_addr *)(he->h_addr_list[0]); } return PS_OK; } CStr CSocketAddress::GetString() const { char convBuf[NI_MAXHOST]; int res=getnameinfo((struct sockaddr *)&m_Union, sizeof(struct sockaddr_in), convBuf, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (res == 0) return CStr(convBuf); // getnameinfo won't return a string for the IPv6 unspecified address else if (m_Union.m_Family == IPv6 && res==EAI_NONAME) return "::"; // supported, but failed else if (errno != ENOSYS) return ""; // else: IPv6 not supported, fall back to IPv4 if (m_Union.m_Family == IPv4) { sprintf_s(convBuf, ARRAY_SIZE(convBuf), "%d.%d.%d.%d", m_Union.m_IPv4.sin_addr.s_addr&0xff, (m_Union.m_IPv4.sin_addr.s_addr>>8)&0xff, (m_Union.m_IPv4.sin_addr.s_addr>>16)&0xff, (m_Union.m_IPv4.sin_addr.s_addr>>24)&0xff); return CStr(convBuf); } else return CStr(); } int CSocketAddress::GetPort() const { switch (m_Union.m_Family) { case IPv4: case IPv6: return ntohs(m_Union.m_IPv4.sin_port); } return -1; } CSocketBase::CSocketBase() { m_pInternal=new CSocketInternal; m_Proto=UNSPEC; m_NonBlocking=true; m_State=SS_UNCONNECTED; m_Error=PS_OK; } CSocketBase::CSocketBase(CSocketInternal *pInt) { m_pInternal=pInt; m_Proto=pInt->m_RemoteAddr.GetProtocol(); m_State=SS_CONNECTED; m_Error=PS_OK; SetNonBlocking(true); NET_LOG2( "CSocketBase::CSocketBase(): Created socket from fd %d", pInt->m_fd ); } CSocketBase::~CSocketBase() { NET_LOG4( "CSocketBase::~CSocketBase(): fd is %d. " "Received: %lu bytes. Sent: %lu bytes.", m_pInternal->m_fd, (unsigned long)m_pInternal->m_RecvBytes, (unsigned long)m_pInternal->m_SentBytes ); Destroy(); delete m_pInternal; } void CSocketBase::Shutdown() { GLOBAL_LOCK(); if (g_SocketSetInternal.m_NumSockets) { NET_LOG2( "CSocketBase::Shutdown(): %lu sockets still open! (forcing network shutdown)", (unsigned long)g_SocketSetInternal.m_NumSockets ); } #if RECORD_GLOBAL_STATS NET_LOG3( "GLOBAL SOCKET STATISTICS: " "Received: %lu bytes. Sent: %lu bytes.", (unsigned long)g_SocketSetInternal.m_GlobalRecvBytes, (unsigned long)g_SocketSetInternal.m_GlobalSentBytes); #endif GLOBAL_UNLOCK(); if (g_SocketSetInternal.m_Thread) { AbortWaitLoop(); pthread_join(g_SocketSetInternal.m_Thread, NULL); } } void *WaitLoopThreadMain(void *) { debug_SetThreadName("net_wait"); GLOBAL_LOCK(); CSocketBase::RunWaitLoop(); g_SocketSetInternal.m_Thread=0; GLOBAL_UNLOCK(); return NULL; } PS_RESULT CSocketBase::Initialize(ESocketProtocol proto) { // Use IPV4 by default, ignoring address type. int res=socket(AF_INET, SOCK_STREAM, 0); NET_LOG2("CSocketBase::Initialize(): socket() res: %d", res); if (res == -1) { return INVALID_PROTOCOL; } m_pInternal->m_fd=res; m_Proto=proto; GLOBAL_LOCK(); if (g_SocketSetInternal.m_NumSockets == 0) pthread_create(&g_SocketSetInternal.m_Thread, NULL, WaitLoopThreadMain, NULL); g_SocketSetInternal.m_NumSockets++; GLOBAL_UNLOCK(); SetNonBlocking(m_NonBlocking); return PS_OK; } void CSocketBase::Close() { shutdown(m_pInternal->m_fd, SHUT_WR); m_State=SS_CLOSED_LOCALLY; } void CSocketBase::Destroy() { if (m_pInternal->m_fd == -1) { m_State=SS_UNCONNECTED; return; } // Remove any data associated with the file descriptor GLOBAL_LOCK(); debug_assert(g_SocketSetInternal.m_NumSockets > 0); g_SocketSetInternal.m_NumSockets--; g_SocketSetInternal.m_HandleMap.erase(m_pInternal->m_fd); if (!g_SocketSetInternal.m_NumSockets) AbortWaitLoop(); GLOBAL_UNLOCK(); // Disconnect the socket, if it is still connected if (m_State == SS_CONNECTED || m_State == SS_CLOSED_LOCALLY) { // This makes the other end receive a RST, but since // we've had no chance to close cleanly and the socket must // be destroyed immediately, we've got no choice shutdown(m_pInternal->m_fd, SHUT_RDWR); m_State=SS_UNCONNECTED; } // Destroy the socket closesocket(m_pInternal->m_fd); m_pInternal->m_fd=-1; } void CSocketBase::SetNonBlocking(bool nonblocking) { m_NonBlocking=nonblocking; #if OS_WIN unsigned long nb=nonblocking; if(!nonblocking) SendWaitLoopUpdate(); // Need to call WSAAsyncSelect with event=0 before ioctlsocket int res=ioctlsocket(m_pInternal->m_fd, FIONBIO, &nb); if (res != 0) NET_LOG2("SetNonBlocking: res %d", res); #else int oldflags=fcntl(m_pInternal->m_fd, F_GETFL, 0); if (oldflags != -1) { if (nonblocking) oldflags |= O_NONBLOCK; else oldflags &= ~O_NONBLOCK; fcntl(m_pInternal->m_fd, F_SETFL, oldflags); } #endif } void CSocketBase::SetTcpNoDelay(bool tcpNoDelay) { // Disable Nagle's Algorithm int data=tcpNoDelay; setsockopt(m_pInternal->m_fd, SOL_SOCKET, TCP_NODELAY, (const char *)&data, sizeof(data)); } PS_RESULT CSocketBase::Read(void *buf, size_t len, size_t *bytesRead) { int res; char errbuf[256]; res=recv(m_pInternal->m_fd, (char *)buf, len, 0); if (res < 0) { *bytesRead=0; int error=Network_LastError; switch (error) { case EWOULDBLOCK: return PS_OK; /*case ENETDOWN: case ENETRESET: case ENOTCONN: case ESHUTDOWN: case ECONNABORTED: case ECONNRESET: case ETIMEDOUT:*/ default: Network_GetErrorString(error, errbuf, sizeof(errbuf)); NET_LOG3("Read error %s [%d]", errbuf, error); m_State=SS_UNCONNECTED; m_Error=GetPS_RESULT(error); return m_Error; } } if (res == 0 && len > 0) // EOF - Cleanly closed socket { *bytesRead=0; m_State=SS_UNCONNECTED; m_Error=PS_OK; return CONNECTION_BROKEN; } *bytesRead=res; m_pInternal->m_RecvBytes += res; #if RECORD_GLOBAL_STATS GLOBAL_LOCK(); g_SocketSetInternal.m_GlobalRecvBytes += res; GLOBAL_UNLOCK(); #endif return PS_OK; } PS_RESULT CSocketBase::Write(void *buf, size_t len, size_t *bytesWritten) { int res; char errbuf[256]; res=send(m_pInternal->m_fd, (char *)buf, len, 0); if (res < 0) { *bytesWritten=0; int err=Network_LastError; switch (err) { case EWOULDBLOCK: return PS_OK; /*case ENETDOWN: case ENETRESET: case ENOTCONN: case ESHUTDOWN: case ECONNABORTED: case ECONNRESET: case ETIMEDOUT: case EHOSTUNREACH:*/ default: Network_GetErrorString(err, errbuf, sizeof(errbuf)); NET_LOG3("Write error %s [%d]", errbuf, err); m_State=SS_UNCONNECTED; return CONNECTION_BROKEN; } } *bytesWritten=res; m_pInternal->m_SentBytes += res; #if RECORD_GLOBAL_STATS GLOBAL_LOCK(); g_SocketSetInternal.m_GlobalSentBytes += res; GLOBAL_UNLOCK(); #endif return PS_OK; } PS_RESULT CSocketBase::Connect(const CSocketAddress &addr) { int res = connect(m_pInternal->m_fd, (struct sockaddr *)(&addr.m_Union), sizeof(struct sockaddr_in)); NET_LOG3("connect returned %d [%d]", res, m_NonBlocking); if (res != 0) { int error=Network_LastError; NET_LOG2("last error was %d", error); if (m_NonBlocking && error == EWOULDBLOCK) { m_State=SS_CONNECT_STARTED; } else { m_State=SS_UNCONNECTED; m_Error=GetPS_RESULT(error); } } else { m_State=SS_CONNECTED; m_Error=PS_OK; } return m_Error; } PS_RESULT CSocketBase::Bind(const CSocketAddress &address) { char errBuf[256]; int res; Initialize(address.GetProtocol()); SetOpMask(READ); res=bind(m_pInternal->m_fd, (const struct sockaddr*)&address, (socklen_t)sizeof(struct sockaddr_in)); if (res == -1) { PS_RESULT ret=PS_FAIL; int err=Network_LastError; switch (err) { case EADDRINUSE: ret=PORT_IN_USE; break; case EACCES: case EADDRNOTAVAIL: ret=INVALID_PORT; break; default: Network_GetErrorString(err, errBuf, sizeof(errBuf)); LOG(CLogger::Error, LOG_CATEGORY, L"CServerSocket::Bind(): bind: %hs [%d] => PS_FAIL", errBuf, err); } m_State=SS_UNCONNECTED; m_Error=ret; return ret; } res=listen(m_pInternal->m_fd, 5); if (res == -1) { int err=Network_LastError; Network_GetErrorString(err, errBuf, sizeof(errBuf)); LOG(CLogger::Error, LOG_CATEGORY, L"CServerSocket::Bind(): listen: %hs [%d] => PS_FAIL", errBuf, err); m_State=SS_UNCONNECTED; return PS_FAIL; } m_State=SS_CONNECTED; m_Error=PS_OK; return PS_OK; } PS_RESULT CSocketBase::PreAccept(CSocketAddress &addr) { socklen_t addrLen=sizeof(struct sockaddr_in); int fd=accept(m_pInternal->m_fd, (struct sockaddr *)&addr.m_Union, &addrLen); m_pInternal->m_AcceptFd=fd; m_pInternal->m_AcceptAddr=addr; if (fd != -1) return PS_OK; else { PS_RESULT res=GetPS_RESULT(Network_LastError); // GetPS_RESULT considers some errors non-failures if (res == PS_OK) return PS_FAIL; else return res; } } CSocketInternal *CSocketBase::Accept() { if (m_pInternal->m_AcceptFd != -1) { CSocketInternal *pInt=new CSocketInternal(); pInt->m_fd=m_pInternal->m_AcceptFd; pInt->m_RemoteAddr=m_pInternal->m_AcceptAddr; GLOBAL_LOCK(); g_SocketSetInternal.m_NumSockets++; GLOBAL_UNLOCK(); m_pInternal->m_AcceptFd=-1; return pInt; } else return NULL; } void CSocketBase::Reject() { shutdown(m_pInternal->m_AcceptFd, SHUT_RDWR); closesocket(m_pInternal->m_AcceptFd); } // UNIX select loop #if !OS_WIN // ConnectError is called on a socket the first time it selects as ready // after the BeginConnect, to check errors on the socket and update the // connection status information // // Returns: true if error callback should be called, false if it should not bool CSocketBase::ConnectError(CSocketBase *pSocket) { CSocketInternal *pInt=pSocket->m_pInternal; size_t buf; int res; if (pSocket->m_State==SS_CONNECT_STARTED) { res=read(pInt->m_fd, &buf, 0); // read of zero bytes should be a successful no-op, unless // there was an error if (res == -1) { pSocket->m_State=SS_UNCONNECTED; PS_RESULT connErr=GetPS_RESULT(errno); NET_LOG4("Connect error: %s [%d:%s]", connErr, errno, strerror(errno)); pSocket->m_Error=connErr; return true; } else { pSocket->m_State=SS_CONNECTED; pSocket->m_Error=PS_OK; } } return false; } // SocketWritable is called whenever a socket selects as writable in the unix // select loop. This will call the callback after checking for a connect error // if there's a connect in progress, as well as update the socket's state. // // Locking: The global mutex must be held when entering this function, and it // will be held upon return. void CSocketBase::SocketWritable(CSocketBase *pSock) { //CSocketInternal *pInt=pSock->m_pInternal; bool isConnectError=false; if (pSock->m_State != SS_CONNECTED) isConnectError=ConnectError(pSock); GLOBAL_UNLOCK(); if (isConnectError) pSock->OnClose(pSock->m_Error); else pSock->OnWrite(); GLOBAL_LOCK(); } // SocketReadable is called whenever a socket selects as writable in the unix // select loop. This will call the callback after checking for a connect error // if there's a connect in progress, as well as update the socket's state. // // Locking: The global mutex must be held when entering this function, and it // will be held upon return. void CSocketBase::SocketReadable(CSocketBase *pSock) { bool isError=false; if (pSock->m_State == SS_CONNECT_STARTED) isError=ConnectError(pSock); else if (pSock->m_State == SS_UNCONNECTED) { // UNCONNECTED sockets don't get callbacks // Note that server sockets that are bound have state==SS_CONNECTED return; } else if (pSock->m_State != SS_UNCONNECTED) { size_t nRead; errno=0; int res=ioctl(pSock->m_pInternal->m_fd, FIONREAD, &nRead); // failure, errno=EINVAL means server socket // success, nRead != 0 means alive stream socket if (res == -1 && errno != EINVAL) { NET_LOG3("RunWaitLoop:ioctl: Connection broken [%d:%s]", errno, strerror(errno)); // Don't use API function - we both hold a lock and // it is unnecessary to SendWaitLoopUpdate at this // stage pSock->m_pInternal->m_Ops=0; pSock->m_State=SS_UNCONNECTED; if (errno) pSock->m_Error=GetPS_RESULT(errno); else pSock->m_Error=PS_OK; isError=true; } } GLOBAL_UNLOCK(); if (isError) pSock->OnClose(pSock->m_Error); else pSock->OnRead(); GLOBAL_LOCK(); } void CSocketBase::RunWaitLoop() { int res; signal(SIGPIPE, SIG_IGN); // Create Control Pipe res=pipe(g_SocketSetInternal.m_Pipe); if (res != 0) return; // The lock is held upon entry and exit of this loop. There are a few places // where the lock is released and then re-acquired: when calling callbacks // and when calling select(). while (true) { std::map::iterator it; fd_set rfds; fd_set wfds; int fd_max=g_SocketSetInternal.m_Pipe[0]; // Prepare fd_set: Read FD_ZERO(&rfds); FD_SET(g_SocketSetInternal.m_Pipe[0], &rfds); // Prepare fd_set: Write FD_ZERO(&wfds); it=g_SocketSetInternal.m_HandleMap.begin(); while (it != g_SocketSetInternal.m_HandleMap.end()) { size_t ops=it->second->m_pInternal->m_Ops; if (ops && it->first > fd_max) fd_max=it->first; if (ops & READ) FD_SET(it->first, &rfds); if (ops & WRITE) FD_SET(it->first, &wfds); ++it; } GLOBAL_UNLOCK(); // select, timeout infinite res=select(fd_max+1, &rfds, &wfds, NULL, NULL); GLOBAL_LOCK(); // Check select error if (res == -1) { // It is possible for a socket to be deleted between preparing the // fd_sets and actually performing the select - in which case it // will fire a Bad file descriptor error. Simply retry. if (Network_LastError == EBADF) continue; perror("CSocketSet::RunWaitLoop(), select"); continue; } // Check Control Pipe if (FD_ISSET(g_SocketSetInternal.m_Pipe[0], &rfds)) { char bt; if (read(g_SocketSetInternal.m_Pipe[0], &bt, 1) == 1) { if (bt=='q') break; else if (bt=='r') { // Reload sockets - just skip to the beginning of the loop continue; } } FD_CLR(g_SocketSetInternal.m_Pipe[0], &rfds); } // Go through sockets int i=-1; while (++i <= fd_max) { if (!FD_ISSET(i, &rfds) && !FD_ISSET(i, &wfds)) continue; it=g_SocketSetInternal.m_HandleMap.find(i); if (it == g_SocketSetInternal.m_HandleMap.end()) continue; CSocketBase *pSock=it->second; if (FD_ISSET(i, &wfds)) { SocketWritable(pSock); } // After the callback is called, we must check if the socket // still exists (sockets may delete themselves in the callback) it=g_SocketSetInternal.m_HandleMap.find(i); if (it == g_SocketSetInternal.m_HandleMap.end()) continue; if (FD_ISSET(i, &rfds)) { SocketReadable(pSock); } } } // Close control pipe close(g_SocketSetInternal.m_Pipe[0]); close(g_SocketSetInternal.m_Pipe[1]); return; } void CSocketBase::SendWaitLoopAbort() { char msg='q'; write(g_SocketSetInternal.m_Pipe[1], &msg, 1); } void CSocketBase::SendWaitLoopUpdate() { // NET_LOG("SendWaitLoopUpdate: fd %d, ops %u\n", m_pInternal->m_fd, m_pInternal->m_Ops); char msg='r'; write(g_SocketSetInternal.m_Pipe[1], &msg, 1); } // Windows WindowProc for async event notification #else // i.e. #if OS_WIN void WaitLoop_SocketUpdateProc(int fd, int error, int event) { GLOBAL_LOCK(); CSocketBase *pSock=g_SocketSetInternal.m_HandleMap[fd]; GLOBAL_UNLOCK(); // FIXME What if the fd isn't in the handle map? if (error) { PS_RESULT res=GetPS_RESULT(error); pSock->m_Error=res; pSock->m_State=SS_UNCONNECTED; if (res == PS_FAIL) pSock->OnClose(CONNECTION_BROKEN); return; } if (pSock->m_State==SS_CONNECT_STARTED) { pSock->m_Error=PS_OK; pSock->m_State=SS_CONNECTED; } switch (event) { case FD_ACCEPT: case FD_READ: pSock->OnRead(); break; case FD_CONNECT: case FD_WRITE: pSock->OnWrite(); break; case FD_CLOSE: // If FD_CLOSE and error, OnClose has already been called above // with the appropriate PS_RESULT pSock->m_State=SS_UNCONNECTED; pSock->OnClose(PS_OK); break; } } LRESULT WINAPI WaitLoop_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { //printf("WaitLoop_WindowProc(): Windows message: %d:%d:%d\n", msg, wParam, lParam); switch (msg) { case MSG_SOCKET_READY: { int event=LOWORD(lParam); int error=HIWORD(lParam); WaitLoop_SocketUpdateProc((int)wParam, error?error-WSABASEERR:0, event); return FALSE; } default: return DefWindowProc(hWnd, msg, wParam, lParam); } } // Locked on entry, must be locked on exit void CSocketBase::RunWaitLoop() { int ret; char errBuf[256] = {0}; MSG msg; WNDCLASS wc; ATOM atom; memset(&wc, 0, sizeof(WNDCLASS)); wc.lpszClassName="Network Event WindowClass"; wc.lpfnWndProc=WaitLoop_WindowProc; atom=RegisterClass(&wc); if (!atom) { ret=GetLastError(); Network_GetErrorString(ret, (LPSTR)&errBuf, 256); NET_LOG3("RegisterClass: %s [%d]", errBuf, ret); return; } // Create message window g_SocketSetInternal.m_hWnd=CreateWindow((LPCTSTR)atom, "Network Event Window", WS_POPUP, 0, 0, 0, 0, NULL, NULL, NULL, NULL); if (!g_SocketSetInternal.m_hWnd) { ret=GetLastError(); Network_GetErrorString(ret, errBuf, sizeof(errBuf)); NET_LOG3("CreateWindowEx: %s [%d]", errBuf, ret); return; } // If OpMasks where set in another thread before we got this far, // WSAAsyncSelect will need to be called again std::map::iterator it; it=g_SocketSetInternal.m_HandleMap.begin(); while (it != g_SocketSetInternal.m_HandleMap.end()) { it->second->SetOpMask(it->second->GetOpMask()); ++it; } NET_LOG2("Commencing message loop. hWnd %p", g_SocketSetInternal.m_hWnd); GLOBAL_UNLOCK(); while ((ret=GetMessage(&msg, g_SocketSetInternal.m_hWnd, 0, 0))!=0) { //printf("RunWaitLoop(): Windows message: %d:%d:%d\n", msg.message, msg.wParam, msg.lParam); if (ret == -1) { ret=GetLastError(); Network_GetErrorString(ret, errBuf, sizeof(errBuf)); NET_LOG3("GetMessage: %s [%d]", errBuf, ret); } { TranslateMessage(&msg); DispatchMessage(&msg); } } GLOBAL_LOCK(); g_SocketSetInternal.m_Thread=0; // FIXME Leak: Destroy window g_SocketSetInternal.m_hWnd=0; NET_LOG("RunWaitLoop returning"); return; } void CSocketBase::SendWaitLoopAbort() { if (g_SocketSetInternal.m_hWnd) { PostMessage(g_SocketSetInternal.m_hWnd, WM_QUIT, 0, 0); } else NET_LOG("SendWaitLoopUpdate: No WaitLoop Running."); } void CSocketBase::SendWaitLoopUpdate() { GLOBAL_LOCK(); if (g_SocketSetInternal.m_hWnd) { if(m_NonBlocking == false) { GLOBAL_UNLOCK(); WSAAsyncSelect(m_pInternal->m_fd, g_SocketSetInternal.m_hWnd, MSG_SOCKET_READY, 0); return; } long wsaOps=FD_CLOSE; if (m_pInternal->m_Ops & READ) wsaOps |= FD_READ|FD_ACCEPT; if (m_pInternal->m_Ops & WRITE) wsaOps |= FD_WRITE|FD_CONNECT; GLOBAL_UNLOCK(); //printf("SendWaitLoopUpdate: %d: %u %x -> %p\n", m_pInternal->m_fd, m_pInternal->m_Ops, wsaOps, g_SocketSetInternal.m_hWnd); WSAAsyncSelect(m_pInternal->m_fd, g_SocketSetInternal.m_hWnd, MSG_SOCKET_READY, wsaOps); } else { //printf("SendWaitLoopUpdate: No WaitLoop Running.\n"); GLOBAL_UNLOCK(); } } #endif // #if OS_WIN void CSocketBase::AbortWaitLoop() { SendWaitLoopAbort(); // pthread_join(g_SocketSetInternal.m_Thread); } int CSocketBase::GetOpMask() { return m_pInternal->m_Ops; } void CSocketBase::SetOpMask(int ops) { GLOBAL_LOCK(); g_SocketSetInternal.m_HandleMap[m_pInternal->m_fd]=this; m_pInternal->m_Ops=ops; /*printf("SetOpMask(fd %d, ops %u) %u\n", m_pInternal->m_fd, ops, g_SocketSetInternal.m_HandleMap[m_pInternal->m_fd]->m_pInternal->m_Ops);*/ GLOBAL_UNLOCK(); SendWaitLoopUpdate(); } Index: ps/trunk/build/premake/premake.lua =================================================================== --- ps/trunk/build/premake/premake.lua (revision 7258) +++ ps/trunk/build/premake/premake.lua (revision 7259) @@ -1,1106 +1,1135 @@ addoption("atlas", "Include Atlas scenario editor packages") addoption("collada", "Include COLLADA packages (requires FCollada library)") addoption("coverage", "Enable code coverage data collection (GCC only)") addoption("aoe3ed", "Include AoE3Ed") addoption("icc", "Use Intel C++ Compiler (Linux only; should use either \"--cc icc\" or --without-pch too, and then set CXX=icpc before calling make)") addoption("outpath", "Location for generated project files") addoption("without-tests", "Disable generation of test projects") addoption("without-pch", "Disable generation and usage of precompiled headers") addoption("with-valgrind", "Enable using valgrind for debugging functionality") addoption("with-spidermonkey-tip", "Use SpiderMonkey from mozilla-central tip instead of the 1.6 release") use_dcdt = false -- disable it since it's a non-Free library dofile("functions.lua") dofile("extern_libs.lua") -- detect CPU architecture (simplistic, currently only supports x86 and amd64 arch = "x86" if OS == "windows" then if os.getenv("PROCESSOR_ARCHITECTURE") == "amd64" or os.getenv("PROCESSOR_ARCHITEW6432") == "amd64" then arch = "amd64" end else arch = os.getenv("HOSTTYPE") if arch == "x86_64" then arch = "amd64" end if not arch then os.execute("gcc -dumpmachine > .gccmachine.tmp") local f = io.open(".gccmachine.tmp", "r") local machine = f:read("*line") f:close() if string.find(machine, "x86_64") == 1 then arch = "amd64" elseif string.find(machine, "i.86") == 1 then arch = "x86" else print("WARNING: Cannot determine architecture from GCC, assuming x86") end end end -- Set up the Project project.name = "pyrogenesis" project.bindir = "../../binaries/system" project.libdir = "../../binaries/system" project.debugdir = "../../binaries/system" if not options["outpath"] then error("You must specify the 'outpath' parameter") end project.path = options["outpath"] project.configs = { "Debug", "Release", "Testing" } if OS == "windows" then project.nasmpath = "../../build/bin/nasm.exe" project.cxxtestpath = "../../build/bin/cxxtestgen.exe" has_broken_pch = false else project.cxxtestpath = "../../build/bin/cxxtestgen.pl" if OS == "linux" and arch == "amd64" then -- Hack for amd64 linux - tell nasm to product 64-bit elf. project.nasmformat = "elf64" end -- GCC bug (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10591) - PCH breaks anonymous namespaces -- Fixed in 4.2.0, but we have to disable PCH for earlier versions, else -- it conflicts annoyingly with wx 2.8 headers. -- It's too late to do this test by the time we start compiling the PCH file, so -- do the test in this build script instead (which is kind of ugly - please fix if -- you have a better idea) if not options["icc"] then os.execute("gcc -dumpversion > .gccver.tmp") local f = io.open(".gccver.tmp", "r") major, dot, minor = f:read(1, 1, 1) f:close() major = 0+major -- coerce to number minor = 0+minor has_broken_pch = (major < 4 or (major == 4 and minor < 2)) if has_broken_pch then print("WARNING: Detected GCC <4.2 -- disabling PCH for Atlas (will increase build times)") end end end source_root = "../../../source/" -- default for most projects - overridden by local in others -- Rationale: packages should not have any additional include paths except for -- those required by external libraries. Instead, we should always write the -- full relative path, e.g. #include "maths/Vector3d.h". This avoids confusion -- ("which file is meant?") and avoids enormous include path lists. -- packages: engine static libs, main exe, atlas, atlas frontends, test. -------------------------------------------------------------------------------- -- package helper functions -------------------------------------------------------------------------------- function package_set_target(package_name) -- Note: On Windows, ".exe" is added on the end, on unices the name is used directly package.config["Debug" ].target = package_name.."_dbg" package.config["Testing"].target = package_name.."_test" package.config["Release"].target = package_name local obj_dir_prefix = "obj/"..package_name.."_" package.config["Debug" ].objdir = obj_dir_prefix.."Debug" package.config["Testing"].objdir = obj_dir_prefix.."Test" package.config["Release"].objdir = obj_dir_prefix.."Release" end function package_set_build_flags() package.buildflags = { "with-symbols", "no-edit-and-continue" } if not options["icc"] then -- adds the -Wall compiler flag tinsert(package.buildflags, "extra-warnings") -- this causes far too many warnings/remarks on ICC end -- PremakeWiki says with-symbols and optimize are automatically set for -- Debug and Release builds, respectively. doesn't happen though, so do it manually. package.config["Debug"].defines = { "DEBUG" } package.config["Testing"].buildflags = { "no-runtime-checks" } package.config["Testing"].defines = { "TESTING" } package.config["Release"].buildflags = { "no-runtime-checks", "optimize-speed" } package.config["Release"].defines = { "NDEBUG" } -- required for the lowlevel library. must be set from all packages that use it, otherwise it assumes it is -- being used as a DLL (which is currently not the case in 0ad) tinsert(package.defines, "LIB_STATIC_LINK") -- various platform-specific build flags if OS == "windows" then -- use native wchar_t type (not typedef to unsigned short) tinsert(package.buildflags, "native-wchar_t") else -- *nix if options["icc"] then tinsert(package.buildoptions, { "-w1", -- "-Wabi", -- "-Wp64", -- complains about OBJECT_TO_JSVAL which is annoying "-Wpointer-arith", "-Wreturn-type", -- "-Wshadow", "-Wuninitialized", "-Wunknown-pragmas", "-Wunused-function", "-wd1292", -- avoid lots of 'attribute "__nonnull__" ignored' }) tinsert(package.config["Debug"].buildoptions, { "-O0", -- ICC defaults to -O2 }) if OS == "macosx" then tinsert(package.linkoptions, {"-multiply_defined","suppress"}) end else tinsert(package.buildoptions, { -- enable most of the standard warnings "-Wno-switch", -- enumeration value not handled in switch (this is sometimes useful, but results in lots of noise) "-Wno-reorder", -- order of initialization list in constructors (lots of noise) "-Wno-invalid-offsetof", -- offsetof on non-POD types (see comment in renderer/PatchRData.cpp) "-Wextra", "-Wno-missing-field-initializers", -- (this is common in external headers we can't fix) -- add some other useful warnings that need to be enabled explicitly "-Wunused-parameter", "-Wredundant-decls", -- (useful for finding some multiply-included header files) -- "-Wformat=2", -- (useful sometimes, but a bit noisy, so skip it by default) -- "-Wcast-qual", -- (useful for checking const-correctness, but a bit noisy, so skip it by default) -- enable security features (stack checking etc) that shouldn't have -- a significant effect on performance and can catch bugs "-fstack-protector-all", "-D_FORTIFY_SOURCE=2", -- always enable strict aliasing (useful in debug builds because of the warnings) "-fstrict-aliasing", -- do something (?) so that ccache can handle compilation with PCH enabled "-fpch-preprocess", -- enable SSE intrinsics "-msse", -- don't omit frame pointers (for now), because performance will be impacted -- negatively by the way this breaks profilers more than it will be impacted -- positively by the optimisation "-fno-omit-frame-pointer", }) if OS == "linux" then tinsert(package.linkoptions, { "-Wl,--no-undefined", "-Wl,--as-needed", }) end if options["coverage"] then tinsert(package.buildoptions, {"-fprofile-arcs", "-ftest-coverage"}) tinsert(package.links, "gcov") end end -- To use our local SpiderMonkey library, it needs to be part of the runtime dynamic linker - -- path. So try to add the cwd (assuming it'll be binaries/system/) with -rpath + -- path. So try to add the cwd (assuming it'll be binaries/system/) with -rpath: -- (TODO: is this a sane way to do it?) tinsert(package.linkoptions, {"-Wl,-rpath=."}) tinsert(package.buildoptions, { -- Hide symbols in dynamic shared objects by default, for efficiency and for equivalence with -- Windows - they should be exported explicitly with __attribute__ ((visibility ("default"))) "-fvisibility=hidden", }) if OS == "macosx" then -- MacPorts uses /opt/local as its prefix package.includepaths = { "/opt/local/include" } package.libpaths = { "/opt/local/lib" } else -- X11 includes may be installed in one of a gadzillion of three places -- Famous last words: "You can't include too much! ;-)" package.includepaths = { "/usr/X11R6/include/X11", "/usr/X11R6/include", "/usr/include/X11" } package.libpaths = { "/usr/X11R6/lib" } end if OS == "linux" and options["icc"] then tinsert(package.libpaths, "/usr/i686-pc-linux-gnu/lib") -- needed for ICC to find libbfd end package.defines = { -- "CONFIG_USE_MMGR", } if use_dcdt then tinsert(package.defines, "USE_DCDT") end if options["with-valgrind"] then tinsert(package.defines, "USE_VALGRIND") end end end -- create a package and set the attributes that are common to all packages. function package_create(package_name, target_type) -- Note: don't store in local variable. A global variable needs to -- be set for Premake's use; it is implicitly used in e.g. matchfiles() package = newpackage() package.path = project.path package.language = "c++" package.name = package_name package.kind = target_type package.includepaths = {} package_set_target(package_name) package_set_build_flags() return package end -- extra_params: table including zero or more of the following: -- * no_default_pch: (any type) prevents adding the PCH include dir. -- see setup_static_lib_package() for explanation of this scheme and rationale. -- * extra_files: table of filenames (relative to source_root) to add to project -- * extra_links: table of library names to add to link step function package_add_contents(source_root, rel_source_dirs, rel_include_dirs, extra_params) -- We don't want the VC project to be deeply nested (once for each -- folder in source_root). Therefore, remove the source_root -- directory from the filenames (where those -- names are used by Premake to construct the project tree), but set -- 'trimprefix' (with Premake altered to recognise that) so the project -- will still point to the correct filenames. package.trimprefix = source_root package.files = sourcesfromdirs(source_root, rel_source_dirs) -- Put the project-specific PCH directory at the start of the -- include path, so '#include "precompiled.h"' will look in -- there first if not extra_params["no_default_pch"] then tinsert(package.includepaths, source_root .. "pch/" .. package.name) end -- next is source root dir, for absolute (nonrelative) includes -- (e.g. "lib/precompiled.h") tinsert(package.includepaths, source_root) for i,v in pairs(rel_include_dirs) do tinsert(package.includepaths, source_root .. v) end if extra_params["extra_files"] then for i,v in pairs(extra_params["extra_files"]) do tinsert(package.files, source_root .. v) end end if extra_params["extra_links"] then listconcat(package.links, extra_params["extra_links"]) end end -- Detect and set up PCH for the current package function package_setup_pch(pch_dir, header, source) if OS == "windows" or not options["without-pch"] then package.pchheader = header -- "precompiled.h" package.pchsource = source -- "precompiled.cpp" if pch_dir then tinsert(package.files, { pch_dir..header, pch_dir..source }) end for i,v in pairs(project.configs) do tinsert(package.config[v].defines, "USING_PCH") end end end -------------------------------------------------------------------------------- -- engine static libraries -------------------------------------------------------------------------------- -- the engine is split up into several static libraries. this eases separate -- distribution of those components, reduces dependencies a bit, and can -- also speed up builds. -- more to the point, it is necessary to efficiently support a separate -- test executable that also includes much of the game code. -- names of all static libs created. automatically added to the -- main app project later (see explanation at end of this file) static_lib_names = {} -- set up one of the static libraries into which the main engine code is split. -- extra_params: see package_add_contents(). -- note: rel_source_dirs and rel_include_dirs are relative to global source_root. function setup_static_lib_package (package_name, rel_source_dirs, extern_libs, extra_params) package_create(package_name, "lib") package_add_contents(source_root, rel_source_dirs, {}, extra_params) package_add_extern_libs(extern_libs) if not extra_params["no_default_link"] then tinsert(static_lib_names, package_name) end if OS == "windows" then tinsert(package.buildflags, "no-rtti") end -- Precompiled Headers -- rationale: we need one PCH per static lib, since one global header would -- increase dependencies. To that end, we can either include them as -- "packagedir/precompiled.h", or add "source/PCH/packagedir" to the -- include path and put the PCH there. The latter is better because -- many packages contain several dirs and it's unclear where there the -- PCH should be stored. This way is also a bit easier to use in that -- source files always include "precompiled.h". -- Notes: -- * Visual Assist manages to use the project include path and can -- correctly open these files from the IDE. -- * precompiled.cpp (needed to "Create" the PCH) also goes in -- the abovementioned dir. if not extra_params["no_default_pch"] then pch_dir = source_root.."pch/"..package_name.."/" package_setup_pch(pch_dir, "precompiled.h", "precompiled.cpp") end end -- this is where the source tree is chopped up into static libs. -- can be changed very easily; just copy+paste a new setup_static_lib_package, -- or remove existing ones. static libs are automagically added to -- main_exe link step. function setup_all_libs () -- relative to global source_root. local source_dirs = {} -- names of external libraries used (see libraries_dir comment) local extern_libs = {} source_dirs = { "network", } extern_libs = { "spidermonkey", "enet", "boost", -- dragged in via server->simulation.h->random } setup_static_lib_package("network", source_dirs, extern_libs, {}) + + source_dirs = { + "simulation2", + "simulation2/components", + "simulation2/helpers", + "simulation2/scripting", + "simulation2/serialization", + "simulation2/system", + "simulation2/testcomponents", + } + extern_libs = { + "boost", + "cryptopp", + "spidermonkey", + } + setup_static_lib_package("simulation2", source_dirs, extern_libs, {}) + + + source_dirs = { + "scriptinterface", + } + extern_libs = { + "boost", + "spidermonkey", + } + setup_static_lib_package("scriptinterface", source_dirs, extern_libs, {}) + + source_dirs = { "ps", "ps/scripting", "ps/Network", "ps/GameSetup", "ps/XML", "simulation", "simulation/scripting", "sound", "scripting", "maths", "maths/scripting", } if use_dcdt then tinsert(source_dirs, "dcdt/se") end extern_libs = { "spidermonkey", "sdl", -- key definitions "libxml2", "opengl", "zlib", "boost", "enet", } setup_static_lib_package("engine", source_dirs, extern_libs, {}) source_dirs = { "graphics", "graphics/scripting", "renderer" } extern_libs = { "opengl", "sdl", -- key definitions "spidermonkey", -- for graphics/scripting "boost" } setup_static_lib_package("graphics", source_dirs, extern_libs, {}) -- internationalization = i18n -- note: this package isn't large, but is separate because it may be -- useful for other projects. source_dirs = { "i18n" } extern_libs = { "spidermonkey", "boost" } setup_static_lib_package("i18n", source_dirs, extern_libs, {}) source_dirs = { "tools/atlas/GameInterface", "tools/atlas/GameInterface/Handlers" } extern_libs = { "boost", "sdl", -- key definitions "opengl", "spidermonkey" } setup_static_lib_package("atlas", source_dirs, extern_libs, {}) source_dirs = { "gui", "gui/scripting" } if OS == "windows" then -- add JS files to allow VS find-in-solution and VA open-file-in-workspace tinsert(source_dirs, "../binaries/data/mods/public/gui/test") end extern_libs = { "spidermonkey", "sdl", -- key definitions "opengl", "boost" } setup_static_lib_package("gui", source_dirs, extern_libs, {}) source_dirs = { "lib", "lib/allocators", "lib/external_libraries", "lib/file", "lib/file/archive", "lib/file/common", "lib/file/io", "lib/file/vfs", "lib/posix", "lib/res", "lib/res/graphics", "lib/res/sound", "lib/sysdep", "lib/tex" } extern_libs = { "boost", "sdl", "opengl", "libpng", "zlib", "openal", "vorbis", "libjpg", "cryptopp", "valgrind", "cxxtest", } -- CPU architecture-specific if arch == "amd64" then tinsert(source_dirs, "lib/sysdep/arch/amd64"); tinsert(source_dirs, "lib/sysdep/arch/x86_x64"); else tinsert(source_dirs, "lib/sysdep/arch/ia32"); tinsert(source_dirs, "lib/sysdep/arch/x86_x64"); end -- OS-specific sysdep_dirs = { linux = { "lib/sysdep/os/linux", "lib/sysdep/os/unix", "lib/sysdep/os/unix/x" }, -- note: RC file must be added to main_exe package. -- note: don't add "lib/sysdep/os/win/aken.cpp" because that must be compiled with the DDK. windows = { "lib/sysdep/os/win", "lib/sysdep/os/win/wposix", "lib/sysdep/os/win/whrt" }, macosx = { "lib/sysdep/os/osx", "lib/sysdep/os/unix" }, } for i,v in pairs(sysdep_dirs[OS]) do tinsert(source_dirs, v); end -- runtime-library-specific if options["target"] == "gnu" then tinsert(source_dirs, "lib/sysdep/rtl/gcc"); else tinsert(source_dirs, "lib/sysdep/rtl/msc"); end setup_static_lib_package("lowlevel", source_dirs, extern_libs, {}) -- CxxTest mock function support extern_libs = { "boost", "cxxtest", } -- 'real' implementations, to be linked against the main executable setup_static_lib_package("mocks_real", {}, extern_libs, { no_default_link = 1, no_default_pch = 1 }) listconcat(package.files, matchfiles(source_root.."mocks/*.h", source_root.."mocks/*_real.cpp")) -- 'test' implementations, to be linked against the test executable setup_static_lib_package("mocks_test", {}, extern_libs, { no_default_link = 1, no_default_pch = 1 }) listconcat(package.files, matchfiles(source_root.."mocks/*.h", source_root.."mocks/*_test.cpp")) end -------------------------------------------------------------------------------- -- main EXE -------------------------------------------------------------------------------- -- used for main EXE as well as test used_extern_libs = { "opengl", "sdl", "libjpg", "libpng", "zlib", "spidermonkey", "libxml2", "openal", "vorbis", "boost", "cxxtest", "comsuppw", "enet", + "cryptopp", } -- Bundles static libs together with main.cpp and builds game executable. function setup_main_exe () package_create("pyrogenesis", "winexe") -- For VS2005, tell the linker to use the libraries' .obj files instead of -- the .lib, to allow incremental linking. -- (Reduces re-link time from ~20 seconds to ~2 secs) tinsert(package.buildflags, "use-library-dep-inputs") local extra_params = { extra_files = { "main.cpp" }, } package_add_contents(source_root, {}, {}, extra_params) package_add_extern_libs(used_extern_libs) tinsert(package.links, "mocks_real") -- Platform Specifics if OS == "windows" then tinsert(package.files, source_root.."lib/sysdep/os/win/icon.rc") -- from "lowlevel" static lib; must be added here to be linked in tinsert(package.files, source_root.."lib/sysdep/os/win/error_dialog.rc") -- VS2005 generates its own manifest, but earlier ones need us to add it manually if (options["target"] == "vs2002" or options["target"] == "vs2003") then tinsert(package.files, source_root.."lib/sysdep/os/win/manifest.rc") end tinsert(package.buildflags, "no-rtti") package.linkoptions = { -- wraps main thread in a __try block(see wseh.cpp). replace with mainCRTStartup if that's undesired. "/ENTRY:wseh_EntryPoint", -- see wstartup.h "/INCLUDE:_wstartup_InitAndRegisterShutdown", -- delay loading of various Windows DLLs (not specific to any of the -- external libraries; those are handled separately) "/DELAYLOAD:ws2_32.dll", "/DELAYLOAD:version.dll", -- allow manual unload of delay-loaded DLLs "/DELAY:UNLOAD" } elseif OS == "linux" then -- Libraries tinsert(package.links, { "fam", -- Utilities "rt", -- Debugging "bfd", "iberty", -- Dynamic libraries (needed for linking for gold) "dl", }) -- Threading support tinsert(package.buildoptions, "-pthread") tinsert(package.linkoptions, "-pthread") -- For debug_resolve_symbol package.config["Debug"].linkoptions = { "-rdynamic" } package.config["Testing"].linkoptions = { "-rdynamic" } elseif OS == "macosx" then -- Libraries tinsert(package.links, { -- Utilities "pthread" }) end end -------------------------------------------------------------------------------- -- atlas -------------------------------------------------------------------------------- -- setup a typical Atlas component package -- extra_params: as in package_add_contents; also zero or more of the following: -- * pch: (any type) set precompiled.h and .cpp as PCH function setup_atlas_package(package_name, target_type, rel_source_dirs, rel_include_dirs, extern_libs, extra_params) local source_root = "../../../source/tools/atlas/" .. package_name .. "/" package_create(package_name, target_type) -- Don't add the default 'sourceroot/pch/projectname' for finding PCH files extra_params["no_default_pch"] = 1 package_add_contents(source_root, rel_source_dirs, rel_include_dirs, extra_params) package_add_extern_libs(extern_libs) if extra_params["pch"] then package_setup_pch(nil, "precompiled.h", "precompiled.cpp"); end if options["aoe3ed"] then tinsert(package.defines, "USE_AOE3ED") end -- Platform Specifics if OS == "windows" then tinsert(package.defines, "_UNICODE") -- Link to required libraries package.links = { "winmm", "comctl32", "rpcrt4", "delayimp", "ws2_32" } -- required to use WinMain() on Windows, otherwise will default to main() tinsert(package.buildflags, "no-main") if extra_params["extra_links"] then listconcat(package.links, extra_params["extra_links"]) end elseif OS == "linux" then tinsert(package.buildoptions, "-rdynamic") tinsert(package.buildoptions, "-fPIC") tinsert(package.linkoptions, "-fPIC") tinsert(package.linkoptions, "-rdynamic") if extra_params["no_unused_warnings"] then if not options["icc"] then tinsert(package.buildoptions, "-Wno-unused-parameter") end end end end -- build all Atlas component packages function setup_atlas_packages() setup_atlas_package("AtlasObject", "lib", { -- src "" },{ -- include },{ -- extern_libs "libxml2", "wxwidgets" },{ -- extra_params }) setup_atlas_package("AtlasScript", "lib", { -- src "" },{ -- include ".." },{ -- extern_libs "boost", "spidermonkey", "wxwidgets" },{ -- extra_params }) setup_atlas_package("wxJS", "lib", { -- src "", "common", "ext", "gui", "gui/control", "gui/event", "gui/misc", "io", },{ -- include },{ -- extern_libs "spidermonkey", "wxwidgets" },{ -- extra_params pch = (not has_broken_pch), no_unused_warnings = 1, -- wxJS has far too many and we're never going to fix them, so just ignore them }) atlas_src = { "ActorEditor", "ActorViewer", "ColourTester", "CustomControls/Buttons", "CustomControls/Canvas", "CustomControls/ColourDialog", "CustomControls/DraggableListCtrl", "CustomControls/EditableListCtrl", "CustomControls/FileHistory", "CustomControls/HighResTimer", "CustomControls/SnapSplitterWindow", "CustomControls/VirtualDirTreeCtrl", "CustomControls/Windows", "ErrorReporter", "General", "General/VideoRecorder", "Misc", "ScenarioEditor", "ScenarioEditor/Sections/Common", "ScenarioEditor/Sections/Cinematic", "ScenarioEditor/Sections/Environment", "ScenarioEditor/Sections/Map", "ScenarioEditor/Sections/Object", "ScenarioEditor/Sections/Terrain", "ScenarioEditor/Sections/Trigger", "ScenarioEditor/Tools", "ScenarioEditor/Tools/Common", } atlas_extra_links = { "AtlasObject", "AtlasScript", "wxJS", } if options["aoe3ed"] then tinsert(atlas_src, "ArchiveViewer") tinsert(atlas_src, "FileConverter") tinsert(atlas_extra_links, "DatafileIO") end setup_atlas_package("AtlasUI", "dll", atlas_src, { -- include "..", "CustomControls", "Misc" },{ -- extern_libs "boost", "devil", --"ffmpeg", -- disabled for now because it causes too many build difficulties "libxml2", "spidermonkey", "wxwidgets", "comsuppw", "zlib", "x11", },{ -- extra_params pch = (not has_broken_pch), extra_links = atlas_extra_links, extra_files = { "Misc/atlas.rc" } }) if options["aoe3ed"] then setup_atlas_package("DatafileIO", "lib", { -- src "", "BAR", "DDT", "SCN", "Stream", "XMB" },{ -- include },{ -- extern_libs "devil", "xerces", "zlib" },{ -- extra_params pch = 1, }) end end -- Atlas 'frontend' tool-launching packages function setup_atlas_frontend_package (package_name) package_create(package_name, "winexe") package_add_extern_libs({ "spidermonkey", }) local source_root = "../../../source/tools/atlas/AtlasFrontends/" package.files = { source_root..package_name..".cpp", source_root..package_name..".rc" } package.trimprefix = source_root package.includepaths = { source_root .. ".." } -- Platform Specifics if OS == "windows" then tinsert(package.defines, "_UNICODE") -- required to use WinMain() on Windows, otherwise will default to main() tinsert(package.buildflags, "no-main") else -- Non-Windows, = Unix if options["aoe3ed"] then tinsert(package.links, "DatafileIO") end tinsert(package.links, "AtlasObject") end tinsert(package.links, "AtlasUI") end function setup_atlas_frontends() setup_atlas_frontend_package("ActorEditor") setup_atlas_frontend_package("ColourTester") if options["aoe3ed"] then setup_atlas_frontend_package("ArchiveViewer") setup_atlas_frontend_package("FileConverter") end end -------------------------------------------------------------------------------- -- collada -------------------------------------------------------------------------------- function setup_collada_package(package_name, target_type, rel_source_dirs, rel_include_dirs, extern_libs, extra_params) package_create(package_name, target_type) -- Don't add the default 'sourceroot/pch/projectname' for finding PCH files extra_params["no_default_pch"] = 1 package_add_contents(source_root, rel_source_dirs, rel_include_dirs, extra_params) package_add_extern_libs(extern_libs) if extra_params["pch"] then package_setup_pch(nil, "precompiled.h", "precompiled.cpp"); end -- Platform Specifics if OS == "windows" then -- required to use WinMain() on Windows, otherwise will default to main() tinsert(package.buildflags, "no-main") if extra_params["extra_links"] then listconcat(package.links, extra_params["extra_links"]) end end if OS == "linux" then tinsert(package.defines, "LINUX"); -- FCollada is not aliasing-safe, so disallow dangerous optimisations -- (TODO: It'd be nice to fix FCollada, but that looks hard) tinsert(package.buildoptions, "-fno-strict-aliasing") tinsert(package.buildoptions, "-rdynamic") tinsert(package.linkoptions, "-rdynamic") elseif OS == "macosx" then -- define MACOS-something? -- On OSX, fcollada uses a few utility functions from coreservices tinsert(package.linkoptions, "-framework CoreServices") end end -- build all Collada component packages function setup_collada_packages() setup_collada_package("Collada", "dll", { -- src "collada" },{ -- include },{ -- extern_libs "fcollada", "dl", "libxml2", },{ -- extra_params pch = 1, }) end -------------------------------------------------------------------------------- -- tests -------------------------------------------------------------------------------- function get_all_test_files(root, src_files, hdr_files) -- note: lua doesn't have any directory handling functions at all, ugh. -- premake's matchrecursive on patterns like *tests*.h doesn't work - -- apparently it applies them to filenames, not the complete path. -- our workaround is to enumerate all files and manually filter out the -- desired */tests/* files. this is a bit slow, but hey. local all_files = matchrecursive(root .. "*.h") for i,v in pairs(all_files) do -- header file in subdirectory test if string.sub(v, -2) == ".h" and string.find(v, "/tests/") then -- don't include sysdep tests on the wrong sys -- don't include Atlas tests unless Atlas is being built if not (string.find(v, "/sysdep/os/win/") and OS ~= "windows") and not (string.find(v, "/tools/atlas/") and not options["atlas"]) then tinsert(hdr_files, v) -- add the corresponding source file immediately, instead of -- waiting for it to appear after cxxtestgen. this avoids -- having to recreate workspace 2x after adding a test. tinsert(src_files, string.sub(v, 1, -3) .. ".cpp") end end end end function setup_tests() local src_files = {} local hdr_files = {} get_all_test_files(source_root, src_files, hdr_files) package_create("test_gen", "cxxtestgen") package.files = hdr_files package.rootfile = source_root .. "test_root.cpp" package.testoptions = "--have-std" package.rootoptions = "--have-std" if OS == "windows" then package.rootoptions = package.rootoptions .. " --gui=PsTestWrapper --runner=Win32ODSPrinter" else package.rootoptions = package.rootoptions .. " --gui=PsTestWrapper --runner=ErrorPrinter" end -- precompiled headers - the header is added to all generated .cpp files -- note that the header isn't actually precompiled here, only #included -- so that the build stage can use it as a precompiled header. include = " --include=precompiled.h" package.rootoptions = package.rootoptions .. include package.testoptions = package.testoptions .. include tinsert(package.buildflags, "no-manifest") package_create("test", "winexe") links = static_lib_names tinsert(links, "test_gen") if options["atlas"] then tinsert(links, "AtlasObject") end extra_params = { extra_files = { "test_root.cpp", "test_setup.cpp" }, extra_links = links, } package_add_contents(source_root, {}, {}, extra_params) -- note: these are not relative to source_root and therefore can't be included via package_add_contents. listconcat(package.files, src_files) package_add_extern_libs(used_extern_libs) tinsert(package.links, "mocks_test") if OS == "windows" then -- from "lowlevel" static lib; must be added here to be linked in tinsert(package.files, source_root.."lib/sysdep/os/win/error_dialog.rc") -- see wstartup.h tinsert(package.linkoptions, "/INCLUDE:_wstartup_InitAndRegisterShutdown") elseif OS == "linux" then tinsert(package.links, { "fam", -- Utilities "rt", -- Debugging "bfd", "iberty", -- Dynamic libraries (needed for linking for gold) "dl", }) -- Threading support tinsert(package.buildoptions, "-pthread") tinsert(package.linkoptions, "-pthread") -- For debug_resolve_symbol package.config["Debug"].linkoptions = { "-rdynamic" } package.config["Testing"].linkoptions = { "-rdynamic" } tinsert(package.includepaths, source_root .. "pch/test/") end package_setup_pch( source_root .. "pch/test/", "precompiled.h", "precompiled.cpp"); tinsert(package.buildflags, "use-library-dep-inputs") end -- must come first, so that VC sets it as the default project and therefore -- allows running via F5 without the "where is the EXE" dialog. setup_main_exe() -- save package global variable for later (will be overwritten by setup_all_libs) main_exe_package = package setup_all_libs() -- HACK: add the static libs to the main EXE project. only now (after -- setup_all_libs has run) are the lib names known. cannot move -- setup_main_exe to run after setup_all_libs (see comment above). -- we also don't want to hardcode the names - that would require more -- work when changing the static lib breakdown. listconcat(main_exe_package.links, static_lib_names) if options["atlas"] then setup_atlas_packages() setup_atlas_frontends() end if options["collada"] then setup_collada_packages() end if not options["without-tests"] then setup_tests() end Index: ps/trunk/build/premake/extern_libs.lua =================================================================== --- ps/trunk/build/premake/extern_libs.lua (revision 7258) +++ ps/trunk/build/premake/extern_libs.lua (revision 7259) @@ -1,348 +1,350 @@ -- this file provides package_add_extern_libs, which takes care of the -- dirty details of adding the libraries' include and lib paths. -- -- TYPICAL TASK: add new library. Instructions: -- 1) add a new extern_lib_defs entry -- 2) add library name to extern_libs tables in premake.lua for all 'packages' that want to use it -- directory in which all library subdirectories reside. libraries_dir = "../../../libraries/" local function add_extern_lib_paths(extern_lib) -- Add '//lib' and '/include' to the includepaths and libpaths -- Often, the headers in libraries/ are windows-specific (always, except -- for cxxtest and fcollada). So don't add the include dir unless on -- windows or processing one of those libs. if OS == "windows" or extern_lib == "cxxtest" or extern_lib == "fcollada" or extern_lib == "valgrind" or extern_lib == "spidermonkey" then tinsert(package.includepaths, libraries_dir .. extern_lib .. "/include") -- Insert libs at pos=1 (i.e. the front of the list) so they take -- precedence over system libraries tinsert(package.libpaths, 1, libraries_dir .. extern_lib .. "/lib") end end -- For unixes: add buildflags and linkflags from the given pkg-config module. local function pkgconfig(lib) tinsert(package.buildoptions, "`pkg-config "..lib.." --cflags`") tinsert(package.gnu_external, "`pkg-config "..lib.." --libs`") end -- library definitions -- in a perfect world, libraries would have a common installation template, -- i.e. location of include directory, naming convention for .lib, etc. -- this table provides a means of working around each library's differences. -- -- the default assumptions are: -- * extern_lib (name of library [string]) is subdirectory of libraries_dir; -- * this directory contains include and lib subdirectories which hold the -- appendant files -- * debug import library and DLL are distinguished with a "d" suffix -- * the library should be marked for delay-loading. -- -- the following options can override these: -- * win_names: table of import library / DLL names (no extension) when -- running on Windows. -- * unix_names: as above; shared object names when running on non-Windows. -- * osx_names: as above; for OS X specificall (overrides unix_names if both are -- specified) -- * linux_names: ditto for Linux (overrides unix_names if both given) -- -- win- and unix-names are 'required'; if not specified, no linking against the -- library happens on platforms whose *_names are missing. -- (rationale: this allows for libraries that do not link against anything, -- e.g. Boost). -- -- * dbg_suffix: changes the debug suffix from the above default. -- can be "" to indicate the library doesn't have a debug build; -- in that case, the same library (without suffix) is used in -- all build configurations. -- * no_delayload: indicate the library is not to be delay-loaded. -- this is necessary for some libraries that do not support it, -- e.g. Xerces (which is so stupid as to export variables). -- * add_func: a function that overrides everything else. responsible for -- setting include and library paths, adding .links (linker input), and -- arranging for delay-loading. this is necessary e.g. for wxWidgets, -- which is unfortunately totally incompatible with our -- library installation rules. -- * depends: a table of external libraries that this library depends on -- * defines: a table of symbols to define extern_lib_defs = { boost = { unix_names = { "boost_signals-mt", "boost_filesystem-mt", "boost_system-mt" }, osx_names = { "boost_signals-mt", "boost_filesystem-mt", "boost_system-mt" } }, cryptopp = { + win_names = { "cryptopp" }, + unix_names = { "cryptopp" }, }, cxxtest = { }, misc = { }, comsuppw = { win_names = { "comsuppw" }, dbg_suffix = "d", no_delayload = 1 }, devil = { unix_names = { "IL", "ILU" }, }, dl = { win_names = { }, unix_names = { "dl" }, }, -- rationale: see libraries_dir..enet/lib/rationale.txt enet = { add_func = function() if OS == "windows" then tinsert(package.includepaths, libraries_dir.."enet/include") tinsert(package.config["Debug" ].libpaths, libraries_dir.."enet/lib/debug") tinsert(package.config["Testing"].libpaths, libraries_dir.."enet/lib/debug") tinsert(package.config["Release"].libpaths, libraries_dir.."enet/lib/release") tinsert(package.config["Debug" ].links, "enet_dbg") tinsert(package.config["Testing"].links, "enet_dbg") tinsert(package.config["Release"].links, "enet") else -- We should be using pkgconfig, but (at least on ubuntu) that adds /usr/include/enet which contains a time.h that gets used before the system header... tinsert(package.links, "enet") end end, }, fcollada = { win_names = { "FCollada" }, unix_names = { "FColladaSD" }, dbg_suffix = "D", no_delayload = 1, }, ffmpeg = { win_names = { "avcodec-51", "avformat-51", "avutil-49", "swscale-0" }, unix_names = { "avcodec", "avformat", "avutil" }, dbg_suffix = "", }, libjpg = { win_names = { "jpeg-6b" }, unix_names = { "jpeg" }, }, libpng = { win_names = { "libpng13" }, unix_names = { "png" }, }, libxml2 = { add_func = function() if OS == "windows" then tinsert(package.includepaths, libraries_dir.."libxml2/include") tinsert(package.libpaths, libraries_dir.."libxml2/lib") tinsert(package.config["Debug" ].links, "libxml2") tinsert(package.config["Testing"].links, "libxml2") tinsert(package.config["Release"].links, "libxml2") else pkgconfig("libxml-2.0") -- libxml2 needs _REENTRANT or __MT__ for thread support; -- OS X doesn't get either set by default, so do it manually if OS == "macosx" then tinsert(package.defines, "_REENTRANT") end end end, }, nspr = { -- On windows, this is somehow baked into the js library (ask philip) -- but on unix we need to explicitly include this (it's used by libjs). add_func = function() if OS ~= "windows" then pkgconfig("nspr") end end, }, openal = { win_names = { "openal32" }, unix_names = { "openal" }, osx_frameworks = { "OpenAL" }, dbg_suffix = "", }, opengl = { win_names = { "opengl32", "glu32", "gdi32" }, unix_names = { "GL", "GLU", "X11" }, osx_frameworks = { "OpenGL" }, dbg_suffix = "", }, sdl = { add_func = function() add_extern_lib_paths("sdl") if OS ~= "windows" then pkgconfig("sdl") end end }, spidermonkey = options["with-spidermonkey-tip"] and { add_func = function() tinsert(package.config["Debug" ].includepaths, libraries_dir.."spidermonkey-tip/include/debug") tinsert(package.config["Testing"].includepaths, libraries_dir.."spidermonkey-tip/include/debug") tinsert(package.config["Release"].includepaths, libraries_dir.."spidermonkey-tip/include/release") tinsert(package.libpaths, libraries_dir.."spidermonkey-tip/lib") tinsert(package.config["Debug" ].links, "mozjs-debug") tinsert(package.config["Testing"].links, "mozjs-release") tinsert(package.config["Release"].links, "mozjs-release") end, } or -- SpiderMonkey 1.6 { win_names = { "js32" }, unix_names = { "js" }, depends = { "nspr" }, defines = { "JS_THREADSAFE" }, }, valgrind = { }, vorbis = { win_names = { "vorbisfile" }, unix_names = { "vorbisfile" }, dbg_suffix = "_d", }, wxwidgets = { add_func = function() if OS == "windows" then tinsert(package.includepaths, libraries_dir.."wxwidgets/include/msvc") tinsert(package.includepaths, libraries_dir.."wxwidgets/include") tinsert(package.libpaths, libraries_dir.."wxwidgets/lib/vc_lib") tinsert(package.config["Debug" ].links, "wxmsw28ud_gl") tinsert(package.config["Testing"].links, "wxmsw28ud_gl") tinsert(package.config["Release"].links, "wxmsw28u_gl") else tinsert(package.buildoptions, "`wx-config --unicode=yes --cxxflags`") tinsert(package.gnu_external, "`wx-config --unicode=yes --libs std,gl`") end end, }, x11 = { win_names = { }, unix_names = { "X11" }, }, xerces = { win_names = { "xerces-c_2" }, unix_names = { "xerces-c" }, no_delayload = 1, }, zlib = { win_names = { "zlib1" }, unix_names = { "z" }, }, } local function add_delayload(name, suffix, def) if def["no_delayload"] then return end -- currently only supported by VC; nothing to do on other platforms. if OS ~= "windows" then return end -- no extra debug version; use same library in all configs if suffix == "" then tinsert(package.linkoptions, "/DELAYLOAD:"..name..".dll") -- extra debug version available; use in debug/testing config else local dbg_cmd = "/DELAYLOAD:" .. name .. suffix .. ".dll" local cmd = "/DELAYLOAD:" .. name .. ".dll" tinsert(package.config["Debug" ].linkoptions, dbg_cmd) -- 'Testing' config uses 'Debug' DLLs tinsert(package.config["Testing"].linkoptions, dbg_cmd) tinsert(package.config["Release"].linkoptions, cmd) end end local function add_extern_lib(extern_lib, def) add_extern_lib_paths(extern_lib) -- careful: make sure to only use *_names when on the correct platform. local names = {} if OS == "windows" then if def.win_names then names = def.win_names end elseif OS == "linux" and def.linux_names then names = def.linux_names elseif OS == "macosx" and def.osx_names then names = def.osx_names elseif def.unix_names then names = def.unix_names end local suffix = "d" -- library is overriding default suffix (typically "" to indicate there is none) if def["dbg_suffix"] then suffix = def["dbg_suffix"] end -- non-Windows doesn't have the distinction of debug vs. release libraries -- (to be more specific, they do, but the two are binary compatible; -- usually only one type - debug or release - is installed at a time). if OS ~= "windows" then suffix = "" end if def.defines then tinsert(package.defines, def.defines) end -- OS X "Frameworks" need to be added in a special way to the link -- i.e. by linkoptions += "-framework ..." if OS == "macosx" and def.osx_frameworks then for i,name in pairs(def.osx_frameworks) do tinsert(package.linkoptions, "-framework " .. name) end else for i,name in pairs(names) do tinsert(package.config["Debug" ].links, name .. suffix) -- 'Testing' config uses 'Debug' DLLs tinsert(package.config["Testing"].links, name .. suffix) tinsert(package.config["Release"].links, name) add_delayload(name, suffix, def) end end end -- add a set of external libraries to the package; takes care of -- include / lib path and linking against the import library. -- extern_libs: table of library names [string] function package_add_extern_libs(extern_libs) local function add_with_deps(libs, lib) local def = extern_lib_defs[lib] assert(def, "external library " .. lib .. " not defined") tinsert(libs, lib) if def.depends then for i,dep in pairs(def.depends) do add_with_deps(libs, dep) end end end local libs = {} for i,extern_lib in pairs(extern_libs) do add_with_deps(libs, extern_lib) end for i,extern_lib in pairs(libs) do local def = extern_lib_defs[extern_lib] if def.add_func then def.add_func() else add_extern_lib(extern_lib, def) end end end Index: ps/trunk/build/errorlist/errorlist.pl =================================================================== --- ps/trunk/build/errorlist/errorlist.pl (revision 7258) +++ ps/trunk/build/errorlist/errorlist.pl (revision 7259) @@ -1,243 +1,253 @@ -#!perl -w +#!/usr/bin/perl -w ++$|; END { print "\n\nPress enter to exit.\n"; } use strict; use warnings; my ($source, $output); for (@ARGV) { # Note to self: "perl errorlist.pl --source=../../source/i18n --output=../../source/i18n/tests/ps/Errors.cpp" if (/[-\/]\?|--\?|--help/) { print <) { if (/^ERROR_/) { if (/^ERROR_GROUP\((.+?)\)/) { $topgroups{$1} = 1; } elsif (/^ERROR_SUBGROUP\((.+?)\)/) { $groups{join '~', split /,\s*/, $1} = 1; } elsif (/^ERROR_TYPE\((.+?)\)/) { $types{join '~', split /,\s*/, $1} = 1; } } ++$loc; } } # Add commas to number in groups of three 1 while $loc =~ s/(\d+)(\d{3})/$1,$2/; print "(".@files." files read - $loc lines of code)\n"; print "Generating $output... "; # Add "PSERROR_Error_InvalidError", so that an error to throw when being # told to throw an error that doesn't exist exists. $topgroups{Error} = 1; $types{'Error~InvalidError'} = 1; open my $out, '>', "$output" or die "Error opening $output ($!)"; print $out <<'.'; // Auto-generated by errorlist.pl - do not edit. #include "precompiled.h" #include "Errors.h" . for (sort keys %topgroups) { - print $out "class PSERROR_$_ : public PSERROR {};\n"; + print $out "class PSERROR_$_ : public PSERROR { protected: PSERROR_$_(const char* msg); };\n"; } print $out "\n"; for (sort { $a->[1] cmp $b->[1] } map [$_, do{(my $c=$_)=~s/~/_/;$c} ], keys %groups) { my ($base, $name) = split /~/, $_->[0]; - print $out "class PSERROR_${base}_$name : public PSERROR_$base {};\n"; + print $out "class PSERROR_${base}_$name : public PSERROR_$base { protected: PSERROR_${base}_$name(const char* msg); };\n"; } print $out "\n"; for (sort { $a->[1] cmp $b->[1] } map [$_, do{(my $c=$_)=~s/~/_/;$c} ], keys %types) { my ($base, $name) = split /~/, $_->[0]; - print $out "class PSERROR_${base}_$name : public PSERROR_$base { public: PSRETURN getCode() const; };\n"; + print $out "class PSERROR_${base}_$name : public PSERROR_$base { public: PSERROR_${base}_$name(); PSERROR_${base}_$name(const char* msg); PSRETURN getCode() const; };\n"; } print $out "\n"; # The difficult bit: =pod mask **** PSERROR 0001 PSERROR_ Err1 1*** PSERROR_Sec1 1001 PSERROR_Sec1_ Err1 1002 PSERROR_Sec1_ Err2 1003 PSERROR_Sec1_ Err3 11** PSERROR_Sec1_Sec1 1101 PSERROR_Sec1_Sec1_Err1 1102 PSERROR_Sec1_Sec1_Err2 2*** PSERROR_Sec2 2001 PSERROR_Sec2_ Err1 ...so split into three sections (0 if null) plus final code... =cut my @sec_codes; $sec_codes[$_]{''} = 1 for 0..2; for (keys %types) { my (@secs) = split /[~_]/; my $err = pop @secs; $sec_codes[$_]{$secs[$_] || ''} = 1 for 0..2; } for my $n (0..2) { @{$sec_codes[$n]}{sort keys %{$sec_codes[$n]}} = 0 .. keys(%{$sec_codes[$n]})-1; } my ($last_sec, $last_err) = ('', 0); for (sort keys %types) { my (@secs) = split /[~_]/; my $err = pop @secs; my $id = join '', map chr $sec_codes[$_]{$secs[$_] || ''}, 0..2; if ($id eq $last_sec) { $id .= chr(++$last_err); } else { $last_sec = $id; $id .= chr($last_err=1); } $types{$_} = $id; } for (sort keys %types) { my ($base, $name) = split /~/; print $out "extern const PSRETURN PSRETURN_${base}_${name} = 0x".unpack('H*', $types{$_}).";\n"; } print $out "\n"; for (sort keys %topgroups) { my (@secs) = $_; my $id = join '', map chr $sec_codes[$_]{$secs[$_] || ''}, 0..2; my $code = unpack 'H*', $id; (my $mask = $code) =~ s/(\d\d)/$1+0 ? 'ff' : '00'/ge; print $out "extern const PSRETURN MASK__PSRETURN_".join('_', @secs)." = 0x${mask}00;\n"; print $out "extern const PSRETURN CODE__PSRETURN_".join('_', @secs)." = 0x${code}00;\n"; } for (sort keys %groups) { my (@secs) = split /[_~]/; my $id = join '', map chr $sec_codes[$_]{$secs[$_] || ''}, 0..2; my $code = unpack 'H*', $id; (my $mask = $code) =~ s/(\d\d)/$1+0 ? 'ff' : '00'/ge; print $out "extern const PSRETURN MASK__PSRETURN_".join('_', @secs)." = 0x${mask}00;\n"; print $out "extern const PSRETURN CODE__PSRETURN_".join('_', @secs)." = 0x${code}00;\n"; } print $out "\n"; for (sort keys %types) { my $code = unpack 'H*', $types{$_}; s/~/_/; print $out "extern const PSRETURN MASK__PSRETURN_$_ = 0xffffffff;\n"; print $out "extern const PSRETURN CODE__PSRETURN_$_ = 0x$code;\n"; } # End of difficult bit. print $out "\n"; +for (sort keys %topgroups) { + print $out "PSERROR_${_}::PSERROR_${_}(const char* msg) : PSERROR(msg) { }\n"; +} + +for (sort keys %groups) { + my ($base, $name) = split /~/; + print $out "PSERROR_${base}_${name}::PSERROR_${base}_${name}(const char* msg) : PSERROR_$base(msg) { }\n"; +} + +print $out "\n"; for (sort keys %types) { my ($base, $name) = split /~/; - print $out qq~PSRETURN PSERROR_${base}_${name}::getCode() const { return 0x~.unpack('H*',$types{$_}).qq~; }\n~; + print $out "PSERROR_${base}_${name}::PSERROR_${base}_${name}() : PSERROR_$base(NULL) { }\n"; + print $out "PSERROR_${base}_${name}::PSERROR_${base}_${name}(const char* msg) : PSERROR_$base(msg) { }\n"; + print $out "PSRETURN PSERROR_${base}_${name}::getCode() const { return 0x".unpack('H*',$types{$_})."; }\n"; + print $out "\n"; } print $out <<"."; -const char* PSERROR::what() const throw () -{ - return GetErrorString(getCode()); -} +PSERROR::PSERROR(const char* msg) : m_msg(msg) { } -const char* GetErrorString(const PSERROR& err) +const char* PSERROR::what() const throw () { - return GetErrorString(err.getCode()); + return m_msg ? m_msg : GetErrorString(getCode()); } const char* GetErrorString(PSRETURN code) { switch (code) { . for (sort keys %types) { (my $name = $_) =~ s/~/_/; print $out qq{\tcase 0x}.unpack('H*',$types{$_}).qq{: return "$name";\n}; } print $out <<"."; default: return "Unrecognised error"; } } void ThrowError(PSRETURN code) { switch (code) // Use 'break' in case someone tries to continue from the exception { . for (sort keys %types) { (my $name = $_) =~ s/~/_/; print $out qq{\tcase 0x}.unpack('H*',$types{$_}).qq{: throw PSERROR_$name(); break;\n}; } print $out <<"."; default: throw PSERROR_Error_InvalidError(); // Hmm... } } . print "Finished.\n"; sub cpp_files { opendir my $d, $_[0] or die "Error opening directory '$_[0]' ($!)"; my @f = readdir $d; my @files = map "$_[0]/$_", grep /\.(?:cpp|h)$/, @f; push @files, cpp_files("$_[0]/$_") for grep { !/^(?:workspaces|tools)$/ and /^[a-zA-Z0-9]+$/ and -d "$_[0]/$_" } @f; return @files; } Index: ps/trunk/build/docs/builddoc.bat =================================================================== --- ps/trunk/build/docs/builddoc.bat (revision 7258) +++ ps/trunk/build/docs/builddoc.bat (revision 7259) @@ -1 +1 @@ -"cppdoc_cmd.exe" -overwrite -autoview -autoquit -title="0 A.D." -company="Wildfire Games" -hier -include-const -include-anonymous -document-preprocessor -comment-format="/**;*;*/;AFTER///;//;///" -classdir=projects -module="cppdoc-standard" -extensions="cpp,h" -languages="cpp=cpp,h=cpp" -enable-author=true -enable-deprecations=true -enable-since=true -enable-version=true -file-links-for-globals=true -generate-deprecations-list=true -generate-hierarchy=true -header-background-dark="#ccccff" -header-background-light="#eeeeff" -include-private=true -include-protected=true -index-file-base=index -overview-html=overview.html -reduce-summary-font=true -selected-text-background=navy -selected-text-foreground=white -separate-index-pages=true -show-cppdoc-version=true -show-timestamp=true -summary-html=project.html -suppress-details=false -suppress-frames-links=false -table-background=white -wrap-long-lines=true "..\..\source" #"..\..\source\tools" #"..\..\source\i18n\tests2" "..\..\docs\generated\index.html" +"cppdoc_cmd.exe" -overwrite -autoview -autoquit -title="0 A.D." -company="Wildfire Games" -hier -include-const -include-anonymous -document-preprocessor -comment-format="/**;*;*/;AFTER///;//;///" -classdir=projects -module="cppdoc-standard" -extensions="cpp,h" -languages="cpp=cpp,h=cpp" -enable-author=true -enable-deprecations=true -enable-since=true -enable-version=true -file-links-for-globals=true -generate-deprecations-list=true -generate-hierarchy=true -header-background-dark="#ccccff" -header-background-light="#eeeeff" -include-private=true -include-protected=true -index-file-base=index -overview-html=overview.html -reduce-summary-font=true -selected-text-background=navy -selected-text-foreground=white -separate-index-pages=true -show-cppdoc-version=true -show-timestamp=true -summary-html=project.html -suppress-details=false -suppress-frames-links=false -table-background=white -wrap-long-lines=true "..\..\source" #"..\..\source\tools" #"..\..\source\i18n\tests2" #"..\..\source\dcdt" "..\..\docs\generated\index.html" Index: ps/trunk/build/docs/builddoc.sh =================================================================== --- ps/trunk/build/docs/builddoc.sh (revision 7258) +++ ps/trunk/build/docs/builddoc.sh (revision 7259) @@ -1 +1 @@ -./cppdoc -overwrite -autoview -autoquit -title="0 A.D." -company="Wildfire Games" -hier -include-const -include-anonymous -document-preprocessor -comment-format="/**;*;*/;AFTER///;//;///" -classdir=projects -module="cppdoc-standard" -extensions="cpp,h" -languages="cpp=cpp,h=cpp" -enable-author=true -enable-deprecations=true -enable-since=true -enable-version=true -file-links-for-globals=true -generate-deprecations-list=true -generate-hierarchy=true -header-background-dark="#ccccff" -header-background-light="#eeeeff" -include-private=true -include-protected=true -index-file-base=index -overview-html=overview.html -reduce-summary-font=true -selected-text-background=navy -selected-text-foreground=white -separate-index-pages=true -show-cppdoc-version=true -show-timestamp=true -summary-html=project.html -suppress-details=false -suppress-frames-links=false -table-background=white -wrap-long-lines=true "../../source" \#"../../source/tools" \#"../../source/i18n/tests2" "../../docs/generated/index.html" \ No newline at end of file +./cppdoc -overwrite -autoview -autoquit -title="0 A.D." -company="Wildfire Games" -hier -include-const -include-anonymous -document-preprocessor -comment-format="/**;*;*/;AFTER///;//;///" -classdir=projects -module="cppdoc-standard" -extensions="cpp,h" -languages="cpp=cpp,h=cpp" -enable-author=true -enable-deprecations=true -enable-since=true -enable-version=true -file-links-for-globals=true -generate-deprecations-list=true -generate-hierarchy=true -header-background-dark="#ccccff" -header-background-light="#eeeeff" -include-private=true -include-protected=true -index-file-base=index -overview-html=overview.html -reduce-summary-font=true -selected-text-background=navy -selected-text-foreground=white -separate-index-pages=true -show-cppdoc-version=true -show-timestamp=true -summary-html=project.html -suppress-details=false -suppress-frames-links=false -table-background=white -wrap-long-lines=true "../../source" \#"../../source/tools" \#"../../source/i18n/tests2" \#"../../source/dcdt" "../../docs/generated/index.html" \ No newline at end of file Index: ps/trunk/source/i18n/Interface.cpp =================================================================== --- ps/trunk/source/i18n/Interface.cpp (revision 7258) +++ ps/trunk/source/i18n/Interface.cpp (revision 7259) @@ -1,93 +1,93 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /* Vague overview of the i18n code: (TODO: Improve the documentation) CLocale stores all locale-specific (locale==language, roughly) data. Usually there's only going to be one in existence. A language needs to define: * String files that define translations for phrases. * Dictionary files that translate individual words (usually names of objects), including extra information that the grammar requires (whether to use 'a' or 'an' in English, gender in lots of European languages, etc) * .js files containing functions that apply grammatical rules (e.g. choosing singular vs plural depending on a number) CLocale::LoadStrings / LoadDictionary / LoadFunctions are used to input the data from the appropriate files. Call multiple times if desired. CLocale::Translate is the primary interface. Pass it a unique identifier string, and it'll read the appropriate translated data from the loaded data files. A StringBuffer is returned. To allow variables embedded in text, StringBuffer::operator<< is used in a similar way to in cout, storing the variable in the StringBuffer and returning the StringBuffer again. StringBuffer::operator Str() does the final insertion of variables into the translated phrase, either grabbing the final result from a cache or doing all the variable-to-string conversions. The strings read from disk are each stored as a TranslatedString, containing several TSComponents, which are either static strings or default-formatted variables or functions. TSComponentFunction is the most complex of the TSComponents, containing a list of ScriptValues -- these allow numbers, strings and variables to be passed as parameters into a JS function. StringBuffer::operator<< stores BufferVariable*s in the StringBuffer. These provide access to a hash of the variable (for caching), and can be converted into a string (for display). StrImW is used in various places, just as a more efficient alternative to std::wstring. */ #include "precompiled.h" #include "Interface.h" #include "CLocale.h" #include "ps/CLogger.h" #define LOG_CATEGORY L"i18n" using namespace I18n; struct JSContext; CLocale_interface* I18n::NewLocale(JSContext* cx, JSObject* scope) { try { return new CLocale(cx, scope); } catch (PSERROR_I18n& e) { - LOG(CLogger::Error, LOG_CATEGORY, L"Error creating locale object ('%hs')", GetErrorString(e)); + LOG(CLogger::Error, LOG_CATEGORY, L"Error creating locale object ('%hs')", e.what()); return NULL; } } Index: ps/trunk/source/test_setup.cpp =================================================================== --- ps/trunk/source/test_setup.cpp (revision 7258) +++ ps/trunk/source/test_setup.cpp (revision 7259) @@ -1,64 +1,109 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ // Got to be consistent with what the rest of the source files do before // including precompiled.h, so that the PCH works correctly #ifndef CXXTEST_RUNNING #define CXXTEST_RUNNING #endif #define _CXXTEST_HAVE_STD #include "precompiled.h" +#include + #include #if OS_WIN #include "lib/sysdep/os/win/wdbg_heap.h" #endif +#include "lib/sysdep/sysdep.h" +#include "scriptinterface/ScriptInterface.h" + class LeakReporter : public CxxTest::GlobalFixture { virtual bool tearDownWorld() { + // Shut down JS to prevent leak reports from it + ScriptInterface::ShutDown(); + // Enable leak reporting on exit. // (This is done in tearDownWorld so that it doesn't report 'leaks' // if the program is aborted before finishing cleanly.) #if OS_WIN wdbg_heap_Enable(true); #endif return true; } virtual bool setUpWorld() { #if MSC_VERSION // (Warning: the allocation numbers seem to differ by 3 when you // run in the build process vs the debugger) // _CrtSetBreakAlloc(1952); #endif return true; } }; static LeakReporter leakReporter; -// Definition of function from lib/self_test.h +// Definition of functions from lib/self_test.h + bool ts_str_contains(const std::wstring& str1, const std::wstring& str2) { return str1.find(str2) != str1.npos; } + +// we need the (version-controlled) binaries/data directory because it +// contains input files (it is assumed that developer's machines have +// write access to those directories). note that argv0 isn't +// available, so we use sys_get_executable_name. +fs::wpath DataDir() +{ + fs::wpath path; + TS_ASSERT_OK(sys_get_executable_name(path)); + return path.branch_path()/L"../data"; +} + +// Script-based testing setup: + +namespace +{ + void script_TS_FAIL(void*, std::wstring msg) + { + TS_FAIL(msg); + } +} + +void ScriptTestSetup(ScriptInterface& ifc) +{ + ifc.RegisterFunction("TS_FAIL"); + + // Load the TS_* function definitions + // (We don't use VFS because tests might not have the normal VFS paths loaded) + fs::wpath path = DataDir()/L"tests/test_setup.js"; + std::ifstream ifs(path.external_file_string().c_str()); + debug_assert(ifs.good()); + std::string content((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + std::wstring wcontent(content.begin(), content.end()); + bool ok = ifc.LoadScript(L"test_setup.js", wcontent); + debug_assert(ok); +} Index: ps/trunk/source/ps/ConfigDB.cpp =================================================================== --- ps/trunk/source/ps/ConfigDB.cpp (revision 7258) +++ ps/trunk/source/ps/ConfigDB.cpp (revision 7259) @@ -1,372 +1,373 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include "Pyrogenesis.h" #include "Parser.h" #include "ConfigDB.h" #include "CLogger.h" #include "Filesystem.h" #include "scripting/ScriptingHost.h" #define LOG_CATEGORY L"config" typedef std::map TConfigMap; TConfigMap CConfigDB::m_Map[CFG_LAST]; CStrW CConfigDB::m_ConfigFile[CFG_LAST]; bool CConfigDB::m_UseVFS[CFG_LAST]; #define GET_NS_PRIVATE(cx, obj) (EConfigNamespace)((intptr_t)JS_GetPrivate(cx, obj) >> 1) namespace ConfigNamespace_JS { JSBool GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) { EConfigNamespace cfgNs = GET_NS_PRIVATE(cx, obj); if (cfgNs < 0 || cfgNs >= CFG_LAST) return JS_FALSE; CStr propName = g_ScriptingHost.ValueToString(id); CConfigValue *val=g_ConfigDB.GetValue(cfgNs, propName); if (val) { JSString *js_str=JS_NewStringCopyN(cx, val->m_String.c_str(), val->m_String.size()); *vp = STRING_TO_JSVAL(js_str); } return JS_TRUE; } JSBool SetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) { EConfigNamespace cfgNs = GET_NS_PRIVATE(cx, obj); if (cfgNs < 0 || cfgNs >= CFG_LAST) return JS_FALSE; CStr propName = g_ScriptingHost.ValueToString(id); CConfigValue *val=g_ConfigDB.CreateValue(cfgNs, propName); char *str; if (JS_ConvertArguments(cx, 1, vp, "s", &str)) { val->m_String=str; return JS_TRUE; } else return JS_FALSE; } JSClass Class = { "ConfigNamespace", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, GetProperty, SetProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub }; JSBool Construct( JSContext* cx, JSObject* obj, uintN argc, jsval* UNUSED(argv), jsval* rval ) { if (argc != 0) return JS_FALSE; JSObject *newObj=JS_NewObject(cx, &Class, NULL, obj); *rval=OBJECT_TO_JSVAL(newObj); return JS_TRUE; } void SetNamespace(JSContext *cx, JSObject *obj, EConfigNamespace cfgNs) { JS_SetPrivate(cx, obj, (void *)((uintptr_t)cfgNs << 1)); // JS requires bottom bit = 0 } JSBool WriteFile( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) { EConfigNamespace cfgNs = GET_NS_PRIVATE(cx, obj); if (cfgNs < 0 || cfgNs >= CFG_LAST) return JS_FALSE; if (argc != 2) return JS_FALSE; JSBool useVFS; char *path; if (JS_ConvertArguments(cx, 2, argv, "bs", &useVFS, &path)) { JSBool res=g_ConfigDB.WriteFile(cfgNs, useVFS?true:false, CStrW(path)); *rval = BOOLEAN_TO_JSVAL(res); return JS_TRUE; } else return JS_FALSE; } JSBool Reload( JSContext* cx, JSObject* obj, uintN argc, jsval* UNUSED(argv), jsval* rval ) { if (argc != 0) return JS_FALSE; EConfigNamespace cfgNs = GET_NS_PRIVATE(cx, obj); if (cfgNs < 0 || cfgNs >= CFG_LAST) return JS_FALSE; JSBool ret=g_ConfigDB.Reload(cfgNs); *rval = BOOLEAN_TO_JSVAL(ret); return JS_TRUE; } JSBool SetFile( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* UNUSED(rval) ) { if (argc != 0) return JS_FALSE; EConfigNamespace cfgNs = GET_NS_PRIVATE(cx, obj); if (cfgNs < 0 || cfgNs >= CFG_LAST) return JS_FALSE; JSBool useVFS; char *path; if (JS_ConvertArguments(cx, 2, argv, "bs", &useVFS, &path)) { g_ConfigDB.SetConfigFile(cfgNs, useVFS?true:false, CStrW(path)); return JS_TRUE; } else return JS_FALSE; } JSFunctionSpec Funcs[] = { { "writeFile", WriteFile, 2, 0, 0}, { "reload", Reload, 0, 0, 0}, { "setFile", SetFile, 2, 0, 0}, {0} }; }; namespace ConfigDB_JS { JSClass Class = { "ConfigDB", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub }; JSPropertySpec Props[] = { {0} }; JSFunctionSpec Funcs[] = { {0} }; JSBool Construct( JSContext* cx, JSObject* obj, uintN argc, jsval* UNUSED(argv), jsval* rval ) { if (argc != 0) return JS_FALSE; JSObject *newObj=JS_NewObject(cx, &Class, NULL, obj); *rval=OBJECT_TO_JSVAL(newObj); int flags=JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT; #define cfg_ns(_propname, _enum) STMT (\ JSObject *nsobj=g_ScriptingHost.CreateCustomObject("ConfigNamespace"); \ debug_assert(nsobj); \ ConfigNamespace_JS::SetNamespace(cx, nsobj, _enum); \ debug_assert(JS_DefineProperty(cx, newObj, _propname, OBJECT_TO_JSVAL(nsobj), NULL, NULL, flags)); ) cfg_ns("default", CFG_DEFAULT); cfg_ns("system", CFG_SYSTEM); cfg_ns("user", CFG_USER); cfg_ns("mod", CFG_MOD); #undef cfg_ns return JS_TRUE; } }; CConfigDB::CConfigDB() { g_ScriptingHost.DefineCustomObjectType(&ConfigDB_JS::Class, ConfigDB_JS::Construct, 0, ConfigDB_JS::Props, ConfigDB_JS::Funcs, NULL, NULL); g_ScriptingHost.DefineCustomObjectType(&ConfigNamespace_JS::Class, ConfigNamespace_JS::Construct, 0, NULL, ConfigNamespace_JS::Funcs, NULL, NULL); JSObject *js_ConfigDB=g_ScriptingHost.CreateCustomObject("ConfigDB"); g_ScriptingHost.SetGlobal("g_ConfigDB", OBJECT_TO_JSVAL(js_ConfigDB)); } CConfigValue *CConfigDB::GetValue(EConfigNamespace ns, const CStr& name) { CConfigValueSet* values = GetValues( ns, name ); if( !values ) return( NULL ); return &( (*values)[0] ); } CConfigValueSet *CConfigDB::GetValues(EConfigNamespace ns, const CStr& name ) { if (ns < 0 || ns >= CFG_LAST) { debug_warn(L"CConfigDB: Invalid ns value"); return NULL; } TConfigMap::iterator it = m_Map[CFG_COMMAND].find( name ); if( it != m_Map[CFG_COMMAND].end() ) return &( it->second ); for( int search_ns = ns; search_ns >= 0; search_ns-- ) { TConfigMap::iterator it = m_Map[search_ns].find(name); if (it != m_Map[search_ns].end()) return &( it->second ); } return( NULL ); } CConfigValue *CConfigDB::CreateValue(EConfigNamespace ns, const CStr& name) { if (ns < 0 || ns >= CFG_LAST) { debug_warn(L"CConfigDB: Invalid ns value"); return NULL; } CConfigValue *ret=GetValue(ns, name); if (ret) return ret; TConfigMap::iterator it=m_Map[ns].insert(m_Map[ns].begin(), make_pair(name, CConfigValueSet( 1 ))); return &(it->second[0]); } void CConfigDB::SetConfigFile(EConfigNamespace ns, bool useVFS, const CStrW& path) { if (ns < 0 || ns >= CFG_LAST) { debug_warn(L"CConfigDB: Invalid ns value"); return; } m_ConfigFile[ns]=path; m_UseVFS[ns]=useVFS; } bool CConfigDB::Reload(EConfigNamespace ns) { // Set up CParser CParser parser; CParserLine parserLine; parser.InputTaskType("Assignment", "_$ident_=<_[-$arg(_minus)]_$value_,>_[-$arg(_minus)]_$value[[;]$rest]"); parser.InputTaskType("CommentOrBlank", "_[;[$rest]]"); // Open file with VFS shared_ptr buffer; size_t buflen; { // Handle missing files quietly if (g_VFS->GetFileInfo(m_ConfigFile[ns], NULL) < 0) { LOG(CLogger::Warning, LOG_CATEGORY, L"Cannot find config file \"%ls\" - ignoring", m_ConfigFile[ns].c_str()); return false; } else { + LOG(CLogger::Normal, LOG_CATEGORY, L"Loading config file \"%ls\"", m_ConfigFile[ns].c_str()); LibError ret = g_VFS->LoadFile(m_ConfigFile[ns], buffer, buflen); if (ret != INFO::OK) { LOG(CLogger::Error, LOG_CATEGORY, L"vfs_load for \"%ls\" failed: return was %ld", m_ConfigFile[ns].c_str(), ret); return false; } } } TConfigMap newMap; char *filebuf=(char *)buffer.get(); char *filebufend=filebuf+buflen; // Read file line by line char *next=filebuf-1; do { char *pos=next+1; next=(char *)memchr(pos, '\n', filebufend-pos); if (!next) next=filebufend; char *lend=next; if (*(lend-1) == '\r') lend--; // Send line to parser bool parseOk=parserLine.ParseString(parser, std::string(pos, lend)); // Get name and value from parser std::string name; std::string value; if (parseOk && parserLine.GetArgCount()>=2 && parserLine.GetArgString(0, name) && parserLine.GetArgString(1, value)) { // Add name and value to the map size_t argCount = parserLine.GetArgCount(); newMap[name].clear(); for( size_t t = 0; t < argCount; t++ ) { if( !parserLine.GetArgString( (int)t + 1, value ) ) continue; CConfigValue argument; argument.m_String = value; newMap[name].push_back( argument ); LOG(CLogger::Normal, LOG_CATEGORY, L"Loaded config string \"%hs\" = \"%hs\"", name.c_str(), value.c_str()); } } } while (next < filebufend); m_Map[ns].swap(newMap); return true; } bool CConfigDB::WriteFile(EConfigNamespace ns, bool useVFS, const CStrW& path) { debug_assert(useVFS); if (ns < 0 || ns >= CFG_LAST) { debug_warn(L"CConfigDB: Invalid ns value"); return false; } shared_ptr buf = io_Allocate(1*MiB); char* pos = (char*)buf.get(); TConfigMap &map=m_Map[ns]; for(TConfigMap::const_iterator it = map.begin(); it != map.end(); ++it) { pos += sprintf(pos, "%s = \"%s\"\n", it->first.c_str(), it->second[0].m_String.c_str()); } const size_t len = pos - (char*)buf.get(); LibError ret = g_VFS->CreateFile(path, buf, len); if(ret < 0) { LOG(CLogger::Error, LOG_CATEGORY, L"CConfigDB::WriteFile(): CreateFile \"%ls\" failed (error: %d)", path.c_str(), (int)ret); return false; } return true; } Index: ps/trunk/source/ps/Util.cpp =================================================================== --- ps/trunk/source/ps/Util.cpp (revision 7258) +++ ps/trunk/source/ps/Util.cpp (revision 7259) @@ -1,358 +1,359 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include "lib/posix/posix_utsname.h" #include "lib/posix/posix_sock.h" #include "lib/ogl.h" #include "lib/timer.h" #include "lib/bits.h" // round_up #include "lib/allocators/shared_ptr.h" #include "lib/sysdep/gfx.h" #include "lib/sysdep/snd.h" +#include "lib/sysdep/cpu.h" #include "lib/sysdep/os_cpu.h" #include "lib/sysdep/arch/x86_x64/topology.h" #include "lib/tex/tex.h" #include "lib/file/io/io_align.h" // BLOCK_SIZE #include "ps/GameSetup/Config.h" #include "ps/GameSetup/GameSetup.h" #include "ps/Game.h" #include "ps/Filesystem.h" #include "renderer/Renderer.h" #include "maths/MathUtil.h" #include "graphics/GameView.h" static std::string SplitExts(const char *exts) { std::string str = exts; std::string ret = ""; size_t idx = str.find_first_of(" "); while(idx != std::string::npos) { if(idx >= str.length() - 1) { ret += str; break; } ret += str.substr(0, idx); ret += "\n"; str = str.substr(idx + 1); idx = str.find_first_of(" "); } return ret; } void WriteSystemInfo() { TIMER(L"write_sys_info"); // get_cpu_info and gfx_detect already called during init - see call site snd_detect(); struct utsname un; uname(&un); fs::wpath pathname(psLogDir()/L"system_info.txt"); FILE* f; errno_t err = _wfopen_s(&f, pathname.string().c_str(), L"w"); if(err != 0) return; // current timestamp (redundant WRT OS timestamp, but that is not // visible when people are posting this file's contents online) { wchar_t timestampBuf[100] = {'\0'}; time_t seconds; time(&seconds); struct tm* t = gmtime(&seconds); const size_t charsWritten = wcsftime(timestampBuf, ARRAY_SIZE(timestampBuf), L"(generated %Y-%m-%d %H:%M:%S UTC)", t); debug_assert(charsWritten != 0); fprintf(f, "%ls\n\n", timestampBuf); } // OS fprintf(f, "OS : %s %s (%s)\n", un.sysname, un.release, un.version); // CPU const CpuTopology* topology = cpu_topology_Detect(); fprintf(f, "CPU : %s, %s (%dx%dx%d)", un.machine, cpu_IdentifierString(), (int)cpu_topology_NumPackages(topology), (int)cpu_topology_CoresPerPackage(topology), (int)cpu_topology_LogicalPerCore(topology)); const double cpu_freq = os_cpu_ClockFrequency(); if(cpu_freq != 0.0f) { if(cpu_freq < 1e9) fprintf(f, ", %.2f MHz\n", cpu_freq*1e-6); else fprintf(f, ", %.2f GHz\n", cpu_freq*1e-9); } else fprintf(f, "\n"); // memory fprintf(f, "Memory : %u MiB; %u MiB free\n", (unsigned)os_cpu_MemorySize(), (unsigned)os_cpu_MemoryAvailable()); // graphics fprintf(f, "Graphics Card : %ls\n", gfx_card); fprintf(f, "OpenGL Drivers : %s; %ls\n", glGetString(GL_VERSION), gfx_drv_ver); fprintf(f, "Video Mode : %dx%d:%d@%d\n", g_xres, g_yres, g_bpp, g_freq); // sound fprintf(f, "Sound Card : %ls\n", snd_card); fprintf(f, "Sound Drivers : %ls\n", snd_drv_ver); // // network name / ips // // note: can't use un.nodename because it is for an // "implementation-defined communications network". char hostname[128] = "(unknown)"; (void)gethostname(hostname, sizeof(hostname)-1); // -1 makes sure it's 0-terminated. if the function fails, // we display "(unknown)" and will skip IP output below. fprintf(f, "Network Name : %s", hostname); { // ignore exception here - see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=114032 hostent* host = gethostbyname(hostname); if(!host) goto no_ip; struct in_addr** ips = (struct in_addr**)host->h_addr_list; if(!ips) goto no_ip; // output all IPs (> 1 if using VMware or dual ethernet) fprintf(f, " ("); for(size_t i = 0; i < 256 && ips[i]; i++) // safety { // separate entries but avoid trailing comma if(i != 0) fprintf(f, ", "); fprintf(f, "%s", inet_ntoa(*ips[i])); } fprintf(f, ")"); } no_ip: fprintf(f, "\n"); // OpenGL extensions (write them last, since it's a lot of text) const char* exts = ogl_ExtensionString(); if (!exts) exts = "{unknown}"; fprintf(f, "\nOpenGL Extensions: \n%s\n", SplitExts(exts).c_str()); fclose(f); f = 0; } // not thread-safe! static const wchar_t* HardcodedErrorString(int err) { static wchar_t description[200]; error_description_r((LibError)err, description, ARRAY_SIZE(description)); return description; } // not thread-safe! const wchar_t* ErrorString(int err) { // language file not available (yet) return HardcodedErrorString(err); // TODO: load from language file } // write the specified texture to disk. // note: cannot be made const because the image may have to be // transformed to write it out in the format determined by 's extension. static LibError tex_write(Tex* t, const VfsPath& filename) { const std::wstring extension = fs::extension(filename); DynArray da; RETURN_ERR(tex_encode(t, extension, &da)); // write to disk LibError ret = INFO::OK; { (void)da_set_size(&da, round_up(da.cur_size, BLOCK_SIZE)); shared_ptr file = DummySharedPtr(da.base); const ssize_t bytes_written = g_VFS->CreateFile(filename, file, da.pos); if(bytes_written > 0) debug_assert(bytes_written == (ssize_t)da.pos); else ret = (LibError)bytes_written; } (void)da_free(&da); return ret; } static size_t s_nextScreenshotNumber; // identifies the file format that is to be written // (case-insensitive). examples: "bmp", "png", "jpg". // BMP is good for quick output at the expense of large files. void WriteScreenshot(const std::wstring& extension) { // get next available numbered filename // note: %04d -> always 4 digits, so sorting by filename works correctly. const VfsPath basenameFormat(L"screenshots/screenshot%04d"); const VfsPath filenameFormat = fs::change_extension(basenameFormat, extension); VfsPath filename; fs_util::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename); const size_t w = (size_t)g_xres, h = (size_t)g_yres; const size_t bpp = 24; GLenum fmt = GL_RGB; int flags = TEX_BOTTOM_UP; // we want writing BMP to be as fast as possible, // so read data from OpenGL in BMP format to obviate conversion. if(!wcscasecmp(extension.c_str(), L".bmp")) { fmt = GL_BGR; flags |= TEX_BGR; } const size_t img_size = w * h * bpp/8; const size_t hdr_size = tex_hdr_size(filename); shared_ptr buf = io_Allocate(hdr_size+img_size); GLvoid* img = buf.get() + hdr_size; Tex t; if(tex_wrap(w, h, bpp, flags, buf, hdr_size, &t) < 0) return; glReadPixels(0, 0, (GLsizei)w, (GLsizei)h, fmt, GL_UNSIGNED_BYTE, img); (void)tex_write(&t, filename); tex_free(&t); } // Similar to WriteScreenshot, but generates an image of size 640*tiles x 480*tiles. void WriteBigScreenshot(const std::wstring& extension, int tiles) { // If the game hasn't started yet then use WriteScreenshot to generate the image. if(g_Game == NULL){ WriteScreenshot(L".bmp"); return; } // get next available numbered filename // note: %04d -> always 4 digits, so sorting by filename works correctly. const VfsPath basenameFormat(L"screenshots/screenshot%04d"); const VfsPath filenameFormat = fs::change_extension(basenameFormat, extension); VfsPath filename; fs_util::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename); // Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and // hope the screen is actually large enough for that. const int tile_w = 640, tile_h = 480; debug_assert(g_xres >= tile_w && g_yres >= tile_h); const int img_w = tile_w*tiles, img_h = tile_h*tiles; const int bpp = 24; GLenum fmt = GL_RGB; int flags = TEX_BOTTOM_UP; // we want writing BMP to be as fast as possible, // so read data from OpenGL in BMP format to obviate conversion. if(!wcscasecmp(extension.c_str(), L".bmp")) { fmt = GL_BGR; flags |= TEX_BGR; } const size_t img_size = img_w * img_h * bpp/8; const size_t tile_size = tile_w * tile_h * bpp/8; const size_t hdr_size = tex_hdr_size(filename); void* tile_data = malloc(tile_size); if(!tile_data) WARN_ERR_RETURN(ERR::NO_MEM); shared_ptr img_buf = io_Allocate(hdr_size+img_size); Tex t; GLvoid* img = img_buf.get() + hdr_size; if(tex_wrap(img_w, img_h, bpp, flags, img_buf, hdr_size, &t) < 0) return; ogl_WarnIfError(); // Resize various things so that the sizes and aspect ratios are correct { g_Renderer.Resize(tile_w, tile_h); SViewPort vp = { 0, 0, tile_w, tile_h }; g_Game->GetView()->GetCamera()->SetViewPort(&vp); g_Game->GetView()->GetCamera()->SetProjection(CGameView::defaultNear, CGameView::defaultFar, CGameView::defaultFOV); } // Temporarily move everything onto the front buffer, so the user can // see the exciting progress as it renders (and can tell when it's finished). // (It doesn't just use SwapBuffers, because it doesn't know whether to // call the SDL version or the Atlas version.) GLint oldReadBuffer, oldDrawBuffer; glGetIntegerv(GL_READ_BUFFER, &oldReadBuffer); glGetIntegerv(GL_DRAW_BUFFER, &oldDrawBuffer); glDrawBuffer(GL_FRONT); glReadBuffer(GL_FRONT); // Render each tile for (int tile_y = 0; tile_y < tiles; ++tile_y) { for (int tile_x = 0; tile_x < tiles; ++tile_x) { // Adjust the camera to render the appropriate region g_Game->GetView()->GetCamera()->SetProjectionTile(tiles, tile_x, tile_y); RenderGui(false); Render(); RenderGui(true); // Copy the tile pixels into the main image glReadPixels(0, 0, tile_w, tile_h, fmt, GL_UNSIGNED_BYTE, tile_data); for (int y = 0; y < tile_h; ++y) { void* dest = (char*)img + ((tile_y*tile_h + y) * img_w + (tile_x*tile_w)) * bpp/8; void* src = (char*)tile_data + y * tile_w * bpp/8; cpu_memcpy(dest, src, tile_w * bpp/8); } } } // Restore the buffer settings glDrawBuffer(oldDrawBuffer); glReadBuffer(oldReadBuffer); // Restore the viewport settings { g_Renderer.Resize(g_xres, g_yres); SViewPort vp = { 0, 0, g_xres, g_yres }; g_Game->GetView()->GetCamera()->SetViewPort(&vp); g_Game->GetView()->GetCamera()->SetProjection(CGameView::defaultNear, CGameView::defaultFar, CGameView::defaultFOV); g_Game->GetView()->GetCamera()->SetProjectionTile(1, 0, 0); } (void)tex_write(&t, filename); tex_free(&t); free(tile_data); } Index: ps/trunk/source/ps/XML/Xeromyces.cpp =================================================================== --- ps/trunk/source/ps/XML/Xeromyces.cpp (revision 7258) +++ ps/trunk/source/ps/XML/Xeromyces.cpp (revision 7259) @@ -1,370 +1,394 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include #include #include #include #include #include "ps/CLogger.h" #include "ps/Filesystem.h" #include "Xeromyces.h" #include #define LOG_CATEGORY L"xml" static void errorHandler(void* UNUSED(userData), xmlErrorPtr error) { LOG(CLogger::Error, LOG_CATEGORY, L"CXeromyces: Parse %ls: %hs:%d: %hs", error->level == XML_ERR_WARNING ? L"warning" : L"error", error->file, error->line, error->message); // TODO: The (non-fatal) warnings and errors don't get stored in the XMB, // so the caching is less transparent than it should be } static bool g_XeromycesStarted = false; void CXeromyces::Startup() { debug_assert(!g_XeromycesStarted); xmlInitParser(); xmlSetStructuredErrorFunc(NULL, &errorHandler); g_XeromycesStarted = true; } void CXeromyces::Terminate() { debug_assert(g_XeromycesStarted); xmlCleanupParser(); xmlSetStructuredErrorFunc(NULL, NULL); g_XeromycesStarted = false; } // Find out write location of the XMB file corresponding to xmlFilename void CXeromyces::GetXMBPath(const PIVFS& vfs, const VfsPath& xmlFilename, const VfsPath& xmbFilename, VfsPath& xmbActualPath) { // rationale: // - it is necessary to write out XMB files into a subdirectory // corresponding to the mod from which the XML file is taken. // this avoids confusion when multiple mods are active - // their XMB files' VFS filename would otherwise be indistinguishable. // - we group files in the cache/ mount point first by mod, and only // then XMB. this is so that all output files for a given mod can // easily be deleted. the operation of deleting all old/unused // XMB files requires a program anyway (to find out which are no // longer needed), so it's not a problem that XMB files reside in // a subdirectory (which would make manually deleting all harder). // get real path of XML file (e.g. mods/official/entities/...) fs::wpath XMBRealPath_; vfs->GetRealPath(xmlFilename, XMBRealPath_); wchar_t XMBRealPath[PATH_MAX]; wcscpy_s(XMBRealPath, ARRAY_SIZE(XMBRealPath), XMBRealPath_.string().c_str()); // extract mod name from that const wchar_t* modPath = wcsstr(XMBRealPath, L"mods/"); debug_assert(modPath != 0); wchar_t modName[PATH_MAX]; // .. NOTE: can't use %ls, of course (keeps going beyond '/') int matches = swscanf(modPath, L"mods/%l[^/]", modName); debug_assert(matches == 1); // build full name: cache, then mod name, XMB subdir, original XMB path xmbActualPath = VfsPath(L"cache/mods") / modName / L"xmb" / xmbFilename; } PSRETURN CXeromyces::Load(const VfsPath& filename) { debug_assert(g_XeromycesStarted); // Make sure the .xml actually exists if (! FileExists(filename)) { LOG(CLogger::Error, LOG_CATEGORY, L"CXeromyces: Failed to find XML file %ls", filename.string().c_str()); return PSRETURN_Xeromyces_XMLOpenFailed; } // Get some data about the .xml file FileInfo fileInfo; if (g_VFS->GetFileInfo(filename, &fileInfo) < 0) { LOG(CLogger::Error, LOG_CATEGORY, L"CXeromyces: Failed to stat XML file %ls", filename.string().c_str()); return PSRETURN_Xeromyces_XMLOpenFailed; } /* XMBs are stored with a unique name, where the name is generated from characteristics of the XML file. If a file already exists with the generated name, it is assumed that that file is a valid conversion of the XML, and so it's loaded. Otherwise, the XMB is created with that filename. This means it's never necessary to overwrite existing XMB files; since the XMBs are often in archives, it's not easy to rewrite those files, and it's not possible to switch to using a loose file because the VFS has already decided that file is inside an archive. So each XMB is given a unique name, and old ones are somehow purged. */ // Generate the filename for the xmb: // _.xmb // with mtime/size as 8-digit hex, where mtime's lowest bit is // zeroed because zip files only have 2 second resolution. const int suffixLength = 22; wchar_t suffix[suffixLength+1]; int printed = swprintf_s(suffix, ARRAY_SIZE(suffix), L"_%08x%08xB.xmb", (int)(fileInfo.MTime() & ~1), (int)fileInfo.Size()); debug_assert(printed == suffixLength); VfsPath xmbFilename = change_extension(filename, suffix); VfsPath xmbPath; GetXMBPath(g_VFS, filename, xmbFilename, xmbPath); // If the file exists, use it if (FileExists(xmbPath)) { if (ReadXMBFile(xmbPath)) return PSRETURN_OK; // (no longer return PSRETURN_Xeromyces_XMLOpenFailed here because // failure legitimately happens due to partially-written XMB files.) } // XMB isn't up to date with the XML, so rebuild it: CVFSFile input; if (input.Load(filename)) { LOG(CLogger::Error, LOG_CATEGORY, L"CXeromyces: Failed to open XML file %ls", filename.string().c_str()); return PSRETURN_Xeromyces_XMLOpenFailed; } CStr8 filename8(filename.string()); xmlDocPtr doc = xmlReadMemory((const char*)input.GetBuffer(), (int)input.GetBufferSize(), filename8.c_str(), NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA); if (! doc) { LOG(CLogger::Error, LOG_CATEGORY, L"CXeromyces: Failed to parse XML file %ls", filename.string().c_str()); return PSRETURN_Xeromyces_XMLParseError; } WriteBuffer writeBuffer; CreateXMB(doc, writeBuffer); xmlFreeDoc(doc); // Save the file to disk, so it can be loaded quickly next time g_VFS->CreateFile(xmbPath, writeBuffer.Data(), writeBuffer.Size()); m_XMBBuffer = writeBuffer.Data(); // add a reference // Set up the XMBFile const bool ok = Initialise((const char*)m_XMBBuffer.get()); debug_assert(ok); return PSRETURN_OK; } bool CXeromyces::ReadXMBFile(const VfsPath& filename) { size_t size; if(g_VFS->LoadFile(filename, m_XMBBuffer, size) < 0) return false; debug_assert(size >= 4); // make sure it's at least got the initial header // Set up the XMBFile if(!Initialise((const char*)m_XMBBuffer.get())) return false; return true; } +PSRETURN CXeromyces::LoadString(const char* xml) +{ + debug_assert(g_XeromycesStarted); + + xmlDocPtr doc = xmlReadMemory(xml, strlen(xml), "", NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA); + if (! doc) + { + LOG(CLogger::Error, LOG_CATEGORY, L"CXeromyces: Failed to parse XML string"); + return PSRETURN_Xeromyces_XMLParseError; + } + + WriteBuffer writeBuffer; + CreateXMB(doc, writeBuffer); + + xmlFreeDoc(doc); + + m_XMBBuffer = writeBuffer.Data(); // add a reference + + // Set up the XMBFile + const bool ok = Initialise((const char*)m_XMBBuffer.get()); + debug_assert(ok); + + return PSRETURN_OK; +} static void FindNames(const xmlNodePtr node, std::set& elementNames, std::set& attributeNames) { elementNames.insert((const char*)node->name); for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) attributeNames.insert((const char*)attr->name); for (xmlNodePtr child = node->children; child; child = child->next) if (child->type == XML_ELEMENT_NODE) FindNames(child, elementNames, attributeNames); } static void OutputElement(const xmlNodePtr node, WriteBuffer& writeBuffer, std::map& elementIDs, std::map& attributeIDs ) { // Filled in later with the length of the element size_t posLength = writeBuffer.Size(); writeBuffer.Append("????", 4); writeBuffer.Append(&elementIDs[(const char*)node->name], 4); u32 attrCount = 0; for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) ++attrCount; writeBuffer.Append(&attrCount, 4); u32 childCount = 0; for (xmlNodePtr child = node->children; child; child = child->next) if (child->type == XML_ELEMENT_NODE) ++childCount; writeBuffer.Append(&childCount, 4); // Filled in later with the offset to the list of child elements size_t posChildrenOffset = writeBuffer.Size(); writeBuffer.Append("????", 4); // Trim excess whitespace in the entity's text, while counting // the number of newlines trimmed (so that JS error reporting // can give the correct line number within the script) std::string whitespace = " \t\r\n"; std::string text; for (xmlNodePtr child = node->children; child; child = child->next) { if (child->type == XML_TEXT_NODE) { xmlChar* content = xmlNodeGetContent(child); text += std::string((const char*)content); xmlFree(content); } } u32 linenum = xmlGetLineNo(node); // Find the start of the non-whitespace section size_t first = text.find_first_not_of(whitespace); if (first == text.npos) // Entirely whitespace - easy to handle text = ""; else { // Count the number of \n being cut off, // and add them to the line number std::string trimmed (text.begin(), text.begin()+first); linenum += std::count(trimmed.begin(), trimmed.end(), '\n'); // Find the end of the non-whitespace section, // and trim off everything else size_t last = text.find_last_not_of(whitespace); text = text.substr(first, 1+last-first); } // Output text, prefixed by length in bytes if (text.length() == 0) { // No text; don't write much writeBuffer.Append("\0\0\0\0", 4); } else { // Write length and line number and null-terminated text utf16string textW = CStr8(text).FromUTF8().utf16(); u32 nodeLen = u32(4 + 2*(textW.length()+1)); writeBuffer.Append(&nodeLen, 4); writeBuffer.Append(&linenum, 4); writeBuffer.Append((void*)textW.c_str(), nodeLen-4); } // Output attributes for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) { writeBuffer.Append(&attributeIDs[(const char*)attr->name], 4); xmlChar* value = xmlNodeGetContent(attr->children); utf16string textW = CStr8((const char*)value).FromUTF8().utf16(); xmlFree(value); u32 attrLen = u32(2*(textW.length()+1)); writeBuffer.Append(&attrLen, 4); writeBuffer.Append((void*)textW.c_str(), attrLen); } // Go back and fill in the child-element offset u32 childrenOffset = (u32)(writeBuffer.Size() - (posChildrenOffset+4)); writeBuffer.Overwrite(&childrenOffset, 4, posChildrenOffset); // Output all child elements for (xmlNodePtr child = node->children; child; child = child->next) if (child->type == XML_ELEMENT_NODE) OutputElement(child, writeBuffer, elementIDs, attributeIDs); // Go back and fill in the length u32 length = (u32)(writeBuffer.Size() - posLength); writeBuffer.Overwrite(&length, 4, posLength); } PSRETURN CXeromyces::CreateXMB(const xmlDocPtr doc, WriteBuffer& writeBuffer) { // Header writeBuffer.Append(UnfinishedHeaderMagicStr, 4); std::set::iterator it; u32 i; // Find the unique element/attribute names std::set elementNames; std::set attributeNames; FindNames(xmlDocGetRootElement(doc), elementNames, attributeNames); std::map elementIDs; std::map attributeIDs; // Output element names i = 0; u32 elementCount = (u32)elementNames.size(); writeBuffer.Append(&elementCount, 4); for (it = elementNames.begin(); it != elementNames.end(); ++it) { u32 textLen = (u32)it->length()+1; writeBuffer.Append(&textLen, 4); writeBuffer.Append((void*)it->c_str(), textLen); elementIDs[*it] = i++; } // Output attribute names i = 0; u32 attributeCount = (u32)attributeNames.size(); writeBuffer.Append(&attributeCount, 4); for (it = attributeNames.begin(); it != attributeNames.end(); ++it) { u32 textLen = (u32)it->length()+1; writeBuffer.Append(&textLen, 4); writeBuffer.Append((void*)it->c_str(), textLen); attributeIDs[*it] = i++; } OutputElement(xmlDocGetRootElement(doc), writeBuffer, elementIDs, attributeIDs); // file is now valid, so insert correct magic string writeBuffer.Overwrite(HeaderMagicStr, 4, 0); return PSRETURN_OK; } Index: ps/trunk/source/ps/XML/tests/test_Xeromyces.h =================================================================== --- ps/trunk/source/ps/XML/tests/test_Xeromyces.h (revision 7258) +++ ps/trunk/source/ps/XML/tests/test_Xeromyces.h (revision 7259) @@ -1,52 +1,68 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "lib/self_test.h" +#include "ps/CLogger.h" #include "ps/XML/Xeromyces.h" #include "lib/file/vfs/vfs.h" -#include "lib/sysdep/sysdep.h" - -// FIXME: copied from test_MeshManager -static fs::wpath DataDir() -{ - fs::wpath path; - TS_ASSERT_OK(sys_get_executable_name(path)); - return path.branch_path()/L"../data"; -} class TestXeromyces : public CxxTest::TestSuite { public: + void setUp() + { + CXeromyces::Startup(); + } + + void tearDown() + { + CXeromyces::Terminate(); + } + void test_paths() { PIVFS vfs = CreateVfs(20*MiB); TS_ASSERT_OK(vfs->Mount(L"", DataDir()/L"mods/_test.xero")); VfsPath xmbPath; CXeromyces::GetXMBPath(vfs, L"test1.xml", L"test1.xmb", xmbPath); TS_ASSERT_WSTR_EQUALS(xmbPath.string(), L"cache/mods/_test.xero/xmb/test1.xmb"); CXeromyces::GetXMBPath(vfs, L"a/b/test1.xml", L"a/b/test1.xmb", xmbPath); TS_ASSERT_WSTR_EQUALS(xmbPath.string(), L"cache/mods/_test.xero/xmb/a/b/test1.xmb"); } // TODO: Should test the reading/parsing/writing code, // and parse error handling + + void test_LoadString() + { + CXeromyces xero; + TS_ASSERT_EQUALS(xero.LoadString("bar"), PSRETURN_OK); + TS_ASSERT_STR_EQUALS(xero.GetElementString(xero.GetRoot().GetNodeName()), "test"); + } + + void test_LoadString_invalid() + { + TestLogger logger; + CXeromyces xero; + TS_ASSERT_EQUALS(xero.LoadString(""), PSRETURN_Xeromyces_XMLParseError); + } }; Index: ps/trunk/source/ps/XML/Xeromyces.h =================================================================== --- ps/trunk/source/ps/XML/Xeromyces.h (revision 7258) +++ ps/trunk/source/ps/XML/Xeromyces.h (revision 7259) @@ -1,92 +1,95 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /* Xeromyces file-loading interface. Automatically creates and caches relatively efficient binary representations of XML files. */ #ifndef INCLUDED_XEROMYCES #define INCLUDED_XEROMYCES #include "ps/Errors.h" ERROR_GROUP(Xeromyces); ERROR_TYPE(Xeromyces, XMLOpenFailed); ERROR_TYPE(Xeromyces, XMLParseError); #include "XeroXMB.h" #include "lib/file/vfs/vfs.h" class WriteBuffer; typedef struct _xmlDoc xmlDoc; typedef xmlDoc* xmlDocPtr; class CXeromyces : public XMBFile { friend class TestXeromyces; friend class TestXeroXMB; public: // Load from an XML file (with invisible XMB caching). PSRETURN Load(const VfsPath& filename); + // Load from an in-memory XML string (with no caching) + PSRETURN LoadString(const char* xml); + // Call once when initialising the program, to load libxml2. // This should be run in the main thread, before any thread // uses libxml2. static void Startup(); // Call once when shutting down the program, to unload libxml2. static void Terminate(); private: // Find out write location of the XMB file corresponding to xmlFilename static void GetXMBPath(const PIVFS& vfs, const VfsPath& xmlFilename, const VfsPath& xmbFilename, VfsPath& xmbActualPath); bool ReadXMBFile(const VfsPath& filename); static PSRETURN CreateXMB(const xmlDocPtr doc, WriteBuffer& writeBuffer); shared_ptr m_XMBBuffer; }; #define _XERO_MAKE_UID2__(p,l) p ## l #define _XERO_MAKE_UID1__(p,l) _XERO_MAKE_UID2__(p,l) #define _XERO_CHILDREN _XERO_MAKE_UID1__(_children_, __LINE__) #define _XERO_I _XERO_MAKE_UID1__(_i_, __LINE__) #define XERO_ITER_EL(parent_element, child_element) \ XMBElementList _XERO_CHILDREN = parent_element.GetChildNodes(); \ XMBElement child_element (0); \ for (int _XERO_I = 0; \ _XERO_I < _XERO_CHILDREN.Count \ && (child_element = _XERO_CHILDREN.Item(_XERO_I), 1); \ ++_XERO_I) #define XERO_ITER_ATTR(parent_element, attribute) \ XMBAttributeList _XERO_CHILDREN = parent_element.GetAttributes(); \ XMBAttribute attribute; \ for (int _XERO_I = 0; \ _XERO_I < _XERO_CHILDREN.Count \ && (attribute = _XERO_CHILDREN.Item(_XERO_I), 1); \ ++_XERO_I) #endif // INCLUDED_XEROMYCES Index: ps/trunk/source/ps/XML/XMLWriter.cpp =================================================================== --- ps/trunk/source/ps/XML/XMLWriter.cpp (revision 7258) +++ ps/trunk/source/ps/XML/XMLWriter.cpp (revision 7259) @@ -1,262 +1,268 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include "XMLWriter.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" +#include "lib/wchar.h" +#include "lib/sysdep/cpu.h" #define LOG_CATEGORY L"xml" // TODO (maybe): Write to the file frequently, instead of buffering // the entire file, so that large files get written faster. namespace { CStr escapeAttributeValue(const char* input) { // Spec says: // AttValue ::= '"' ([^<&"] | Reference)* '"' // so > is allowed in attribute values, so we don't bother escaping it. CStr ret = input; ret.Replace("&", "&"); ret.Replace("<", "<"); ret.Replace("\"", """); return ret; } CStr escapeCharacterData(const char* input) { // CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) CStr ret = input; ret.Replace("&", "&"); ret.Replace("<", "<"); ret.Replace("]]>", "]]>"); return ret; } CStr escapeComment(const char* input) { // Comment ::= '' // This just avoids double-hyphens, and doesn't enforce the no-hyphen-at-end // rule, since it's only used in contexts where there's already a space // between this data and the -->. CStr ret = input; ret.Replace("--", "\xE2\x80\x90\xE2\x80\x90"); // replace with U+2010 HYPHEN, because it's close enough and it's // probably nicer than inserting spaces or deleting hyphens or // any alternative return ret; } } enum { EL_ATTR, EL_TEXT, EL_SUBEL }; XMLWriter_File::XMLWriter_File() : m_Indent(0), m_LastElement(NULL), m_PrettyPrint(true) { // Encoding is always UTF-8 - that's one of the only two guaranteed to be // supported by XML parsers (along with UTF-16), and there's not much need // to let people choose another. m_Data = "\n"; } bool XMLWriter_File::StoreVFS(const VfsPath& pathname) { if (m_LastElement) debug_warn(L"ERROR: Saving XML while an element is still open"); const size_t size = m_Data.length(); shared_ptr data = io_Allocate(size); cpu_memcpy(data.get(), m_Data.data(), size); LibError ret = g_VFS->CreateFile(pathname, data, size); if (ret < 0) { LOG(CLogger::Error, LOG_CATEGORY, L"Error saving XML data through VFS: %ld", ret); return false; } return true; } const CStr& XMLWriter_File::GetOutput() { return m_Data; } void XMLWriter_File::Comment(const char* text) { ElementStart(NULL, "!-- "); m_Data += escapeComment(text); m_Data += " -->"; --m_Indent; } CStr XMLWriter_File::Indent() { return std::string(m_Indent, '\t'); } void XMLWriter_File::ElementStart(XMLWriter_Element* element, const char* name) { if (m_LastElement) m_LastElement->Close(EL_SUBEL); m_LastElement = element; if (m_PrettyPrint) { m_Data += "\n"; m_Data += Indent(); } m_Data += "<"; m_Data += name; ++m_Indent; } void XMLWriter_File::ElementClose() { m_Data += ">"; } void XMLWriter_File::ElementEnd(const char* name, int type) { --m_Indent; m_LastElement = NULL; switch (type) { case EL_ATTR: m_Data += "/>"; break; case EL_TEXT: m_Data += ""; break; case EL_SUBEL: if (m_PrettyPrint) { m_Data += "\n"; m_Data += Indent(); } m_Data += ""; break; default: debug_assert(0); } } void XMLWriter_File::ElementText(const char* text) { m_Data += escapeCharacterData(text); } XMLWriter_Element::XMLWriter_Element(XMLWriter_File& file, const char* name) : m_File(&file), m_Name(name), m_Type(EL_ATTR) { m_File->ElementStart(this, name); } XMLWriter_Element::~XMLWriter_Element() { m_File->ElementEnd(m_Name, m_Type); } void XMLWriter_Element::Close(int type) { if (m_Type == type) return; m_File->ElementClose(); m_Type = type; } // Template specialisations for various string types: template <> void XMLWriter_Element::Text(const char* text) { Close(EL_TEXT); m_File->ElementText(text); } template <> void XMLWriter_Element::Text(const wchar_t* text) { Text( CStrW(text).ToUTF8().c_str() ); } // template <> void XMLWriter_File::ElementAttribute(const char* name, const char* const& value, bool newelement) { if (newelement) { ElementStart(NULL, name); m_Data += ">"; ElementText(value); ElementEnd(name, EL_TEXT); } else { debug_assert(m_LastElement && m_LastElement->m_Type == EL_ATTR); m_Data += " "; m_Data += name; m_Data += "=\""; m_Data += escapeAttributeValue(value); m_Data += "\""; } } // Attribute/setting value-to-string template specialisations. // // These only deal with basic types. Anything more complicated should // be converted into a basic type by whatever is making use of XMLWriter, // to keep game-related logic out of the not-directly-game-related code here. template <> void XMLWriter_File::ElementAttribute(const char* name, const CStr& value, bool newelement) { ElementAttribute(name, value.c_str(), newelement); } // Use CStr's conversion for most types: #define TYPE2(ID_T, ARG_T) \ template <> void XMLWriter_File::ElementAttribute(const char* name, ARG_T value, bool newelement) \ { \ ElementAttribute(name, CStr(value).c_str(), newelement); \ } #define TYPE(T) TYPE2(T, const T &) TYPE(int) TYPE(unsigned int) TYPE(float) TYPE(double) // Encode Unicode strings as UTF-8 template <> void XMLWriter_File::ElementAttribute(const char* name, const CStrW& value, bool newelement) { ElementAttribute(name, value.ToUTF8(), newelement); } +template <> void XMLWriter_File::ElementAttribute(const char* name, const std::wstring& value, bool newelement) +{ + ElementAttribute(name, utf8_from_wstring(value).c_str(), newelement); +} Index: ps/trunk/source/ps/utf16string.h =================================================================== --- ps/trunk/source/ps/utf16string.h (revision 7258) +++ ps/trunk/source/ps/utf16string.h (revision 7259) @@ -1,112 +1,112 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /* A basic_string derivative that works with uint16_t as its underlying char type. */ #ifndef INCLUDED_UTF16STRING #define INCLUDED_UTF16STRING // On Linux, wchar_t is 32-bit, so define a new version of it. // We now use this code on Windows as well, because wchar_t is a // native type and distinct from utf16_t. #include - -#include "lib/sysdep/cpu.h" // cpu_memcpy +#include +#include typedef uint16_t utf16_t; // jw: this was originally defined in the std namespace, which is at // least frowned upon if not illegal. giving it a new name and passing it // as a template parameter is the "correct" and safe way. struct utf16_traits { typedef utf16_t char_type; typedef int int_type; typedef std::streampos pos_type; typedef std::streamoff off_type; typedef std::mbstate_t state_type; static void assign(char_type& c1, const char_type& c2) { c1 = c2; } static bool eq(const char_type& c1, const char_type& c2) { return c1 == c2; } static bool lt(const char_type& c1, const char_type& c2) { return c1 < c2; } static int compare(const char_type* s1, const char_type* s2, size_t n) { return memcmp(s1, s2, n*sizeof(char_type)); } static size_t length(const char_type* s) { const char_type* end=s; while (*end) end++; return end-s; } static const char_type* find(const char_type* s, size_t n, const char_type& a) { const char_type *end = s+n; const char_type *res = std::find(s, end, a); return (res != end)?res:NULL; } static char_type* move(char_type* s1, const char_type* s2, size_t n) { return (char_type *)memmove(s1, s2, n*sizeof(char_type)); } static char_type* copy(char_type* s1, const char_type* s2, size_t n) { - return (char_type *)cpu_memcpy(s1, s2, n*sizeof(char_type)); + return (char_type *)memcpy(s1, s2, n*sizeof(char_type)); } static char_type* assign(char_type* s, size_t n, char_type a) { while (n--) { s[n]=a; } return s; } static char_type to_char_type(const int_type& c) { return (char_type)c; } static int_type to_int_type(const char_type& c) { return (int_type)c; } static bool eq_int_type(const int_type& c1, const int_type& c2) { return c1 == c2; } static int_type eof() { return -1; } static int_type not_eof(const int_type& c) { return (c == -1) ? 0 : c; } }; typedef std::basic_string utf16string; typedef std::basic_stringstream utf16stringstream; #endif Index: ps/trunk/source/ps/Hotkey.h =================================================================== --- ps/trunk/source/ps/Hotkey.h (revision 7258) +++ ps/trunk/source/ps/Hotkey.h (revision 7259) @@ -1,152 +1,157 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ // Hotkey.h // // Constant definitions and a couple of exports for the hotkey processor // // Hotkeys can be mapped onto SDL events (for use internal to the engine), // or used to trigger activation of GUI buttons. // // Adding a hotkey (SDL event type): // // - Define your constant in the enum, just below; // - Add an entry to hotkeyInfo[], in Hotkey.cpp // first column is this constant, second is the config string (minus 'hotkey.') it maps to // third and fourth are the default keys, used if the config file doesn't contain that string. // - Create an input handler for SDL_HOTKEYDOWN, SDL_HOTKEYUP, or poll the hotkeys[] array. // For SDL_HOTKEYDOWN, SDL_HOTKEYUP, the constant is passed in as ev->ev.user.code. // - Add some bindings to the config file. #include "CStr.h" #include "lib/input.h" #include "lib/external_libraries/sdl.h" // see note below // note: we need the real SDL header - it defines SDL_USEREVENT, which is // required for our HOTKEY event type definition. this is OK since // hotkey.h is not included from any headers. const int SDL_HOTKEYDOWN = SDL_USEREVENT; const int SDL_HOTKEYUP = SDL_USEREVENT + 1; const int SDL_GUIHOTKEYPRESS = SDL_USEREVENT + 2; enum { HOTKEY_EXIT, HOTKEY_SCREENSHOT, HOTKEY_BIGSCREENSHOT, HOTKEY_WIREFRAME, HOTKEY_CAMERA_RESET, HOTKEY_CAMERA_RESET_ORIGIN, HOTKEY_CAMERA_ZOOM_IN, HOTKEY_CAMERA_ZOOM_OUT, HOTKEY_CAMERA_ZOOM_WHEEL_IN, HOTKEY_CAMERA_ZOOM_WHEEL_OUT, HOTKEY_CAMERA_ROTATE, HOTKEY_CAMERA_ROTATE_KEYBOARD, HOTKEY_CAMERA_ROTATE_ABOUT_TARGET, HOTKEY_CAMERA_ROTATE_ABOUT_TARGET_KEYBOARD, HOTKEY_CAMERA_PAN, HOTKEY_CAMERA_PAN_KEYBOARD, HOTKEY_CAMERA_LEFT, HOTKEY_CAMERA_RIGHT, HOTKEY_CAMERA_UP, HOTKEY_CAMERA_DOWN, HOTKEY_CAMERA_UNIT_VIEW, HOTKEY_CAMERA_UNIT_ATTACH, HOTKEY_CAMERA_BOOKMARK_0, HOTKEY_CAMERA_BOOKMARK_1, HOTKEY_CAMERA_BOOKMARK_2, HOTKEY_CAMERA_BOOKMARK_3, HOTKEY_CAMERA_BOOKMARK_4, HOTKEY_CAMERA_BOOKMARK_5, HOTKEY_CAMERA_BOOKMARK_6, HOTKEY_CAMERA_BOOKMARK_7, HOTKEY_CAMERA_BOOKMARK_8, HOTKEY_CAMERA_BOOKMARK_9, HOTKEY_CAMERA_BOOKMARK_SAVE, HOTKEY_CAMERA_BOOKMARK_SNAP, HOTKEY_CAMERA_CINEMA_ADD, HOTKEY_CAMERA_CINEMA_DELETE, HOTKEY_CAMERA_CINEMA_DELETE_ALL, HOTKEY_CAMERA_CINEMA_QUEUE, HOTKEY_CONSOLE_TOGGLE, HOTKEY_CONSOLE_COPY, HOTKEY_CONSOLE_PASTE, HOTKEY_SELECTION_ADD, HOTKEY_SELECTION_REMOVE, HOTKEY_SELECTION_GROUP_0, HOTKEY_SELECTION_GROUP_1, HOTKEY_SELECTION_GROUP_2, HOTKEY_SELECTION_GROUP_3, HOTKEY_SELECTION_GROUP_4, HOTKEY_SELECTION_GROUP_5, HOTKEY_SELECTION_GROUP_6, HOTKEY_SELECTION_GROUP_7, HOTKEY_SELECTION_GROUP_8, HOTKEY_SELECTION_GROUP_9, HOTKEY_SELECTION_GROUP_10, HOTKEY_SELECTION_GROUP_11, HOTKEY_SELECTION_GROUP_12, HOTKEY_SELECTION_GROUP_13, HOTKEY_SELECTION_GROUP_14, HOTKEY_SELECTION_GROUP_15, HOTKEY_SELECTION_GROUP_16, HOTKEY_SELECTION_GROUP_17, HOTKEY_SELECTION_GROUP_18, HOTKEY_SELECTION_GROUP_19, HOTKEY_SELECTION_GROUP_ADD, HOTKEY_SELECTION_GROUP_SAVE, HOTKEY_SELECTION_GROUP_SNAP, HOTKEY_SELECTION_SNAP, HOTKEY_ORDER_QUEUE, HOTKEY_CONTEXTORDER_NEXT, HOTKEY_CONTEXTORDER_PREVIOUS, HOTKEY_HIGHLIGHTALL, HOTKEY_PROFILE_TOGGLE, HOTKEY_PROFILE_SAVE, HOTKEY_PLAYMUSIC, HOTKEY_WATER_TOGGLE, HOTKEY_WATER_RAISE, HOTKEY_WATER_LOWER, HOTKEY_TERRITORY_RENDERING_TOGGLE, HOTKEY_PAUSE, HOTKEY_SPEED_INCREASE, HOTKEY_SPEED_DECREASE, HOTKEY_LAST, HOTKEY_NEGATION_FLAG = 65536 }; extern void LoadHotkeys(); -extern InReaction HotkeyInputHandler( const SDL_Event_* ev ); -extern void HotkeyRegisterGuiObject( const CStr& objName, const CStr& hotkeyName ); +extern InReaction HotkeyInputHandler(const SDL_Event_* ev); +extern void HotkeyRegisterGuiObject(const CStr& objName, const CStr& hotkeyName); + +/** + * @return the name of the specified HOTKEY_*, or empty string if not defined + **/ +extern CStr HotkeyGetName(int hotkey); /** * @return whether the specified HOTKEY_* responds to the specified SDLK_* * (mainly for the screenshot system to know whether it needs to override * the printscreen screen). Ignores modifier keys. **/ -extern bool HotkeyRespondsTo( int hotkey, int sdlkey ); +extern bool HotkeyRespondsTo(int hotkey, int sdlkey); /** * @return whether one of the key combinations for the given hotkey is pressed **/ -extern bool HotkeyIsPressed( const CStr& keyname ); +extern bool HotkeyIsPressed(const CStr& keyname); extern bool hotkeys[HOTKEY_LAST]; Index: ps/trunk/source/ps/Filesystem.cpp =================================================================== --- ps/trunk/source/ps/Filesystem.cpp (revision 7258) +++ ps/trunk/source/ps/Filesystem.cpp (revision 7259) @@ -1,127 +1,130 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include "Filesystem.h" #include "gui/GUIManager.h" #include "ps/CLogger.h" +#include "ps/Game.h" +#include "simulation2/Simulation2.h" -#include "lib/posix/posix_time.h" // usleep #include "lib/res/h_mgr.h" // h_reload #include "lib/sysdep/dir_watch.h" #define LOG_CATEGORY L"file" PIVFS g_VFS; bool FileExists(const VfsPath& pathname) { return g_VFS->GetFileInfo(pathname, 0) == INFO::OK; } // try to skip unnecessary work by ignoring uninteresting notifications. static bool CanIgnore(const DirWatchNotification& notification) { // ignore directories const fs::wpath& pathname = notification.Pathname(); if(pathname.leaf() == L".") return true; // ignore uninteresting file types (e.g. temp files, or the // hundreds of XMB files that are generated from XML) const std::wstring extension = fs::extension(pathname); const wchar_t* extensionsToIgnore[] = { L".xmb", L".tmp" }; for(size_t i = 0; i < ARRAY_SIZE(extensionsToIgnore); i++) { if(!wcscasecmp(extension.c_str(), extensionsToIgnore[i])) return true; } return false; } LibError ReloadChangedFiles() { std::vector notifications; RETURN_ERR(dir_watch_Poll(notifications)); for(size_t i = 0; i < notifications.size(); i++) { if(!CanIgnore(notifications[i])) { VfsPath pathname; RETURN_ERR(g_VFS->GetVirtualPath(notifications[i].Pathname(), pathname)); RETURN_ERR(g_VFS->Invalidate(pathname)); RETURN_ERR(g_GUI->ReloadChangedFiles(pathname)); + if (g_Game && g_Game->GetSimulation2()) + RETURN_ERR(g_Game->GetSimulation2()->ReloadChangedFile(pathname)); RETURN_ERR(h_reload(pathname)); } } return INFO::OK; } CVFSFile::CVFSFile() { } CVFSFile::~CVFSFile() { } PSRETURN CVFSFile::Load(const VfsPath& filename) { // Load should never be called more than once, so complain if (m_Buffer) { debug_assert(0); return PSRETURN_CVFSFile_AlreadyLoaded; } LibError ret = g_VFS->LoadFile(filename, m_Buffer, m_BufferSize); if (ret != INFO::OK) { LOG(CLogger::Error, LOG_CATEGORY, L"CVFSFile: file %ls couldn't be opened (vfs_load: %ld)", filename.string().c_str(), ret); return PSRETURN_CVFSFile_LoadFailed; } return PSRETURN_OK; } const u8* CVFSFile::GetBuffer() const { // Die in a very obvious way, to avoid subtle problems caused by // accidentally forgetting to check that the open succeeded if (!m_Buffer) { debug_warn(L"GetBuffer() called with no file loaded"); throw PSERROR_CVFSFile_InvalidBufferAccess(); } return m_Buffer.get(); } size_t CVFSFile::GetBufferSize() const { return m_BufferSize; } CStr CVFSFile::GetAsString() const { return std::string((char*)GetBuffer(), GetBufferSize()); } Index: ps/trunk/source/ps/Hotkey.cpp =================================================================== --- ps/trunk/source/ps/Hotkey.cpp (revision 7258) +++ ps/trunk/source/ps/Hotkey.cpp (revision 7259) @@ -1,647 +1,653 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include "Hotkey.h" #include "lib/input.h" #include "ConfigDB.h" #include "CLogger.h" #include "CConsole.h" #include "CStr.h" #include "ps/Globals.h" #include "KeyName.h" extern CConsole* g_Console; static bool unified[5]; /* SDL-type */ struct SHotkeyMapping { int mapsTo; bool negation; std::vector requires; SHotkeyMapping() : mapsTo(-1) {} }; typedef std::vector KeyMapping; enum { // 'Keycodes' for the mouse buttons MOUSE_LEFT = SDLK_LAST + SDL_BUTTON_LEFT, MOUSE_RIGHT = SDLK_LAST + SDL_BUTTON_RIGHT, MOUSE_MIDDLE = SDLK_LAST + SDL_BUTTON_MIDDLE, MOUSE_WHEELUP = SDLK_LAST + SDL_BUTTON_WHEELUP, MOUSE_WHEELDOWN = SDLK_LAST + SDL_BUTTON_WHEELDOWN, // 'Keycodes' for the unified modifier keys UNIFIED_SHIFT, UNIFIED_CTRL, UNIFIED_ALT, UNIFIED_META, UNIFIED_SUPER }; /** * HK_MAX_KEYCODES: Global maximum number of keycodes, including our "fake" keycodes for * mouse buttons and unified modifiers. */ const int HK_MAX_KEYCODES = UNIFIED_SUPER + 1; // A mapping of keycodes onto sets of SDL event codes static KeyMapping hotkeyMap[HK_MAX_KEYCODES]; // An array of the status of virtual keys bool hotkeys[HOTKEY_LAST]; struct SHotkeyInfo { int code; const char* name; int defaultmapping1, defaultmapping2; }; // Will phase out the default shortcuts at sometime in the near future // (or, failing that, will update them so they can do the tricky stuff // the config file can.) static SHotkeyInfo hotkeyInfo[] = { { HOTKEY_EXIT, "exit", SDLK_ESCAPE, 0 }, { HOTKEY_SCREENSHOT, "screenshot", SDLK_PRINT, 0 }, { HOTKEY_BIGSCREENSHOT, "bigscreenshot", 0, 0 }, { HOTKEY_WIREFRAME, "wireframe", SDLK_w, 0 }, { HOTKEY_CAMERA_RESET, "camera.reset", 0, 0 }, { HOTKEY_CAMERA_RESET_ORIGIN, "camera.reset.origin", SDLK_h, 0 }, { HOTKEY_CAMERA_ZOOM_IN, "camera.zoom.in", SDLK_PLUS, SDLK_KP_PLUS }, { HOTKEY_CAMERA_ZOOM_OUT, "camera.zoom.out", SDLK_MINUS, SDLK_KP_MINUS }, { HOTKEY_CAMERA_ZOOM_WHEEL_IN, "camera.zoom.wheel.in", MOUSE_WHEELUP, 0 }, { HOTKEY_CAMERA_ZOOM_WHEEL_OUT, "camera.zoom.wheel.out", MOUSE_WHEELDOWN, 0 }, { HOTKEY_CAMERA_ROTATE, "camera.rotate", 0, 0 }, { HOTKEY_CAMERA_ROTATE_KEYBOARD, "camera.rotate.keyboard", 0, 0 }, { HOTKEY_CAMERA_ROTATE_ABOUT_TARGET, "camera.rotate.abouttarget", 0, 0 }, { HOTKEY_CAMERA_ROTATE_ABOUT_TARGET_KEYBOARD, "camera.rotate.abouttarget.keyboard", 0, 0 }, { HOTKEY_CAMERA_PAN, "camera.pan", MOUSE_MIDDLE, 0 }, { HOTKEY_CAMERA_PAN_KEYBOARD, "camera.pan.keyboard", 0, 0 }, { HOTKEY_CAMERA_LEFT, "camera.left", SDLK_LEFT, 0 }, { HOTKEY_CAMERA_RIGHT, "camera.right", SDLK_RIGHT, 0 }, { HOTKEY_CAMERA_UP, "camera.up", SDLK_UP, 0 }, { HOTKEY_CAMERA_DOWN, "camera.down", SDLK_DOWN, 0 }, { HOTKEY_CAMERA_UNIT_VIEW, "camera.unit.view", SDLK_u, 0 }, { HOTKEY_CAMERA_UNIT_ATTACH, "camera.unit.attach", SDLK_l, 0 }, { HOTKEY_CAMERA_BOOKMARK_0, "camera.bookmark.0", SDLK_F5, 0, }, { HOTKEY_CAMERA_BOOKMARK_1, "camera.bookmark.1", SDLK_F6, 0, }, { HOTKEY_CAMERA_BOOKMARK_2, "camera.bookmark.2", SDLK_F7, 0, }, { HOTKEY_CAMERA_BOOKMARK_3, "camera.bookmark.3", SDLK_F8, 0, }, { HOTKEY_CAMERA_BOOKMARK_4, "camera.bookmark.4", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_5, "camera.bookmark.5", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_6, "camera.bookmark.6", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_7, "camera.bookmark.7", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_8, "camera.bookmark.8", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_9, "camera.bookmark.9", 0, 0, }, { HOTKEY_CAMERA_BOOKMARK_SAVE, "camera.bookmark.save", 0, 0 }, { HOTKEY_CAMERA_BOOKMARK_SNAP, "camera.bookmark.snap", 0, 0 }, { HOTKEY_CAMERA_CINEMA_ADD, "camera.cinema.add", SDLK_l, 0 }, { HOTKEY_CAMERA_CINEMA_DELETE, "camera.cinema.delete", SDLK_u, 0 }, { HOTKEY_CAMERA_CINEMA_DELETE_ALL, "camera.cinema.delete.all", SDLK_r, 0 }, { HOTKEY_CAMERA_CINEMA_QUEUE, "camera.cinema.write", SDLK_i, 0}, { HOTKEY_CONSOLE_TOGGLE, "console.toggle", SDLK_F1, 0 }, { HOTKEY_CONSOLE_COPY, "console.copy", 0, 0 }, { HOTKEY_CONSOLE_PASTE, "console.paste", 0, 0 }, { HOTKEY_SELECTION_ADD, "selection.add", SDLK_LSHIFT, SDLK_RSHIFT }, { HOTKEY_SELECTION_REMOVE, "selection.remove", SDLK_LCTRL, SDLK_RCTRL }, { HOTKEY_SELECTION_GROUP_0, "selection.group.0", SDLK_0, 0, }, { HOTKEY_SELECTION_GROUP_1, "selection.group.1", SDLK_1, 0, }, { HOTKEY_SELECTION_GROUP_2, "selection.group.2", SDLK_2, 0, }, { HOTKEY_SELECTION_GROUP_3, "selection.group.3", SDLK_3, 0, }, { HOTKEY_SELECTION_GROUP_4, "selection.group.4", SDLK_4, 0, }, { HOTKEY_SELECTION_GROUP_5, "selection.group.5", SDLK_5, 0, }, { HOTKEY_SELECTION_GROUP_6, "selection.group.6", SDLK_6, 0, }, { HOTKEY_SELECTION_GROUP_7, "selection.group.7", SDLK_7, 0, }, { HOTKEY_SELECTION_GROUP_8, "selection.group.8", SDLK_8, 0, }, { HOTKEY_SELECTION_GROUP_9, "selection.group.9", SDLK_9, 0, }, { HOTKEY_SELECTION_GROUP_10, "selection.group.10", 0, 0, }, { HOTKEY_SELECTION_GROUP_11, "selection.group.11", 0, 0, }, { HOTKEY_SELECTION_GROUP_12, "selection.group.12", 0, 0, }, { HOTKEY_SELECTION_GROUP_13, "selection.group.13", 0, 0, }, { HOTKEY_SELECTION_GROUP_14, "selection.group.14", 0, 0, }, { HOTKEY_SELECTION_GROUP_15, "selection.group.15", 0, 0, }, { HOTKEY_SELECTION_GROUP_16, "selection.group.16", 0, 0, }, { HOTKEY_SELECTION_GROUP_17, "selection.group.17", 0, 0, }, { HOTKEY_SELECTION_GROUP_18, "selection.group.18", 0, 0, }, { HOTKEY_SELECTION_GROUP_19, "selection.group.19", 0, 0, }, { HOTKEY_SELECTION_GROUP_ADD, "selection.group.add", SDLK_LSHIFT, SDLK_RSHIFT }, { HOTKEY_SELECTION_GROUP_SAVE, "selection.group.save", SDLK_LCTRL, SDLK_RCTRL }, { HOTKEY_SELECTION_GROUP_SNAP, "selection.group.snap", SDLK_LALT, SDLK_RALT }, { HOTKEY_SELECTION_SNAP, "selection.snap", SDLK_HOME, 0 }, { HOTKEY_ORDER_QUEUE, "order.queue", SDLK_LSHIFT, SDLK_RSHIFT }, { HOTKEY_CONTEXTORDER_NEXT, "contextorder.next", SDLK_RIGHTBRACKET, 0 }, { HOTKEY_CONTEXTORDER_PREVIOUS, "contextorder.previous", SDLK_LEFTBRACKET, 0 }, { HOTKEY_HIGHLIGHTALL, "highlightall", SDLK_o, 0 }, { HOTKEY_PROFILE_TOGGLE, "profile.toggle", SDLK_F11, 0 }, { HOTKEY_PROFILE_SAVE, "profile.save", 0, 0 }, { HOTKEY_PLAYMUSIC, "playmusic", SDLK_p, 0 }, { HOTKEY_WATER_TOGGLE, "water.toggle", SDLK_q, 0 }, { HOTKEY_WATER_RAISE, "water.raise", SDLK_a, 0 }, { HOTKEY_WATER_LOWER, "water.lower", SDLK_z, 0 }, { HOTKEY_TERRITORY_RENDERING_TOGGLE, "territory.rendering.toggle", SDLK_t, 0 }, { HOTKEY_PAUSE, "pause", SDLK_PAUSE, 0 }, { HOTKEY_SPEED_INCREASE, "speed.increase", 0, 0 }, { HOTKEY_SPEED_DECREASE, "speed.decrease", 0, 0 } }; /* SDL-type ends */ /* GUI-type */ struct SHotkeyMappingGui { CStr mapsTo; bool negation; std::vector requires; SHotkeyMappingGui() : mapsTo(-1) {} }; typedef std::vector GuiMapping; // A mapping of keycodes onto sets of hotkey name strings (e.g. '[hotkey.]camera.reset') static GuiMapping hotkeyMapGui[HK_MAX_KEYCODES]; typedef std::vector GuiObjectList; // A list of GUI objects typedef std::map GuiHotkeyMap; // A mapping of name strings to lists of GUI objects that they trigger static GuiHotkeyMap guiHotkeyMap; // Look up a key binding in the config file and set the mappings for // all key combinations that trigger it. static void setBindings( const CStr& hotkeyName, int integerMapping = -1 ) { CConfigValueSet* binding = g_ConfigDB.GetValues( CFG_USER, CStr( "hotkey." ) + hotkeyName ); if( binding ) { int mapping; CConfigValueSet::iterator it; CParser multikeyParser; multikeyParser.InputTaskType( "multikey", "<[!$arg(_negate)][~$arg(_negate)]$value_+_>_[!$arg(_negate)][~$arg(_negate)]$value" ); // Iterate through the bindings for this event... for( it = binding->begin(); it != binding->end(); it++ ) { std::string hotkey; if( it->GetString( hotkey ) ) { std::vector keyCombination; CParserLine multikeyIdentifier; multikeyIdentifier.ParseString( multikeyParser, hotkey ); // Iterate through multiple-key bindings (e.g. Ctrl+I) bool negateNext = false; for( size_t t = 0; t < multikeyIdentifier.GetArgCount(); t++ ) { if( multikeyIdentifier.GetArgString( (int)t, hotkey ) ) { if( hotkey == "_negate" ) { negateNext = true; continue; } // Attempt decode as key name mapping = FindKeyCode( hotkey ); // Attempt to decode as a negation of a keyname // Yes, it's going a bit far, perhaps. // Too powerful for most uses, probably. // However, it got some hardcoding out of the engine. // Thus it makes me happy. if( !mapping ) if( !it->GetInt( mapping ) ) // Attempt decode as key code { LOG(CLogger::Warning, L"hotkey", L"Couldn't map '%hs'", hotkey.c_str() ); continue; } if( negateNext ) mapping |= HOTKEY_NEGATION_FLAG; negateNext = false; keyCombination.push_back( mapping ); } } std::vector::iterator itKey, itKey2; SHotkeyMapping bindCode; SHotkeyMappingGui bindName; for( itKey = keyCombination.begin(); itKey != keyCombination.end(); itKey++ ) { bindName.mapsTo = hotkeyName; bindName.negation = ( ( *itKey & HOTKEY_NEGATION_FLAG ) ? true : false ); bindName.requires.clear(); if( integerMapping != -1 ) { bindCode.mapsTo = integerMapping; bindCode.negation = ( ( *itKey & HOTKEY_NEGATION_FLAG ) ? true : false ); bindCode.requires.clear(); } for( itKey2 = keyCombination.begin(); itKey2 != keyCombination.end(); itKey2++ ) { // Push any auxiliary keys. if( itKey != itKey2 ) { bindName.requires.push_back( *itKey2 ); if( integerMapping != -1 ) bindCode.requires.push_back( *itKey2 ); } } hotkeyMapGui[*itKey & ~HOTKEY_NEGATION_FLAG].push_back( bindName ); if( integerMapping != -1 ) hotkeyMap[*itKey & ~HOTKEY_NEGATION_FLAG].push_back( bindCode ); } } } } else if( integerMapping != -1 ) { SHotkeyMapping bind[2]; bind[0].mapsTo = integerMapping; bind[1].mapsTo = integerMapping; bind[0].requires.clear(); bind[1].requires.clear(); bind[0].negation = false; bind[1].negation = false; hotkeyMap[ hotkeyInfo[integerMapping].defaultmapping1 ].push_back( bind[0] ); if( hotkeyInfo[integerMapping].defaultmapping2 ) hotkeyMap[ hotkeyInfo[integerMapping].defaultmapping2 ].push_back( bind[1] ); } } void LoadHotkeys() { InitKeyNameMap(); for(int i = 0; i < HOTKEY_LAST; i++ ) setBindings( hotkeyInfo[i].name, i ); // Set up the state of the hotkeys given no key is down. // i.e. find those hotkeys triggered by all negations. std::vector::iterator it; std::vector::iterator j; bool allNegated; for(int i = 1; i < HK_MAX_KEYCODES; i++ ) { for( it = hotkeyMap[i].begin(); it != hotkeyMap[i].end(); it++ ) { if( !it->negation ) continue; allNegated = true; for( j = it->requires.begin(); j != it->requires.end(); j++ ) if( !( *j & HOTKEY_NEGATION_FLAG ) ) allNegated = false; debug_assert((size_t)it->mapsTo < ARRAY_SIZE(hotkeys)); if( allNegated ) hotkeys[it->mapsTo] = true; } } } void HotkeyRegisterGuiObject( const CStr& objName, const CStr& hotkeyName ) { GuiObjectList& boundTo = guiHotkeyMap[hotkeyName]; if( boundTo.empty() ) { // Load keybindings from the config file setBindings( hotkeyName ); } boundTo.push_back( objName ); } InReaction HotkeyInputHandler( const SDL_Event_* ev ) { int keycode = 0; switch( ev->ev.type ) { case SDL_KEYDOWN: case SDL_KEYUP: keycode = (int)ev->ev.key.keysym.sym; break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: if ((int)ev->ev.button.button <= SDL_BUTTON_WHEELDOWN) { keycode = SDLK_LAST + (int)ev->ev.button.button; break; } // fall through default: return IN_PASS; } // Somewhat hackish: // Create phantom 'unified-modifier' events when left- or right- modifier keys are pressed // Just send them to this handler; don't let the imaginary event codes leak back to real SDL. SDL_Event_ phantom; phantom.ev.type = ( ( ev->ev.type == SDL_KEYDOWN ) || ( ev->ev.type == SDL_MOUSEBUTTONDOWN ) ) ? SDL_KEYDOWN : SDL_KEYUP; if( ( keycode == SDLK_LSHIFT ) || ( keycode == SDLK_RSHIFT ) ) { phantom.ev.key.keysym.sym = (SDLKey)UNIFIED_SHIFT; unified[0] = ( phantom.ev.type == SDL_KEYDOWN ); HotkeyInputHandler( &phantom ); } else if( ( keycode == SDLK_LCTRL ) || ( keycode == SDLK_RCTRL ) ) { phantom.ev.key.keysym.sym = (SDLKey)UNIFIED_CTRL; unified[1] = ( phantom.ev.type == SDL_KEYDOWN ); HotkeyInputHandler( &phantom ); } else if( ( keycode == SDLK_LALT ) || ( keycode == SDLK_RALT ) ) { phantom.ev.key.keysym.sym = (SDLKey)UNIFIED_ALT; unified[2] = ( phantom.ev.type == SDL_KEYDOWN ); HotkeyInputHandler( &phantom ); } else if( ( keycode == SDLK_LMETA ) || ( keycode == SDLK_RMETA ) ) { phantom.ev.key.keysym.sym = (SDLKey)UNIFIED_META; unified[3] = ( phantom.ev.type == SDL_KEYDOWN ); HotkeyInputHandler( &phantom ); } else if( ( keycode == SDLK_LSUPER ) || ( keycode == SDLK_RSUPER ) ) { phantom.ev.key.keysym.sym = (SDLKey)UNIFIED_SUPER; unified[4] = ( phantom.ev.type == SDL_KEYDOWN ); HotkeyInputHandler( &phantom ); } // Inhibit the dispatch of hotkey events caused by printable or control keys // while the console is up. (But allow multiple-key - 'Ctrl+F' events, and whatever // key toggles the console.) bool consoleCapture = false, isCapturable; if( g_Console->IsActive() && ( ( keycode == 8 ) || ( keycode == 9 ) || ( keycode == 13 ) || /* Editing */ ( ( keycode >= 32 ) && ( keycode < 273 ) ) || /* Printable (<128), 'World' (<256) */ ( ( keycode >= 273 ) && ( keycode < 282 ) && /* Numeric keypad (<273), navigation */ ( keycode != SDLK_INSERT ) ) ) ) /* keys (<282) except insert */ consoleCapture = true; std::vector::iterator it; std::vector::iterator itGUI; SDL_Event hotkeyNotification; // Here's an interesting bit: // If you have an event bound to, say, 'F', and another to, say, 'Ctrl+F', pressing // 'F' while control is down would normally fire off both. // To avoid this, set the modifier keys for /all/ events this key would trigger // (Ctrl, for example, is both group-save and bookmark-save) // but only send a HotkeyDown event for the event with bindings most precisely // matching the conditions (i.e. the event with the highest number of auxiliary // keys, providing they're all down) bool typeKeyDown = ( ev->ev.type == SDL_KEYDOWN ) || ( ev->ev.type == SDL_MOUSEBUTTONDOWN ); // -- KEYDOWN SECTION -- // SDL-events bit size_t closestMap = 0; // avoid "uninitialized" warning size_t closestMapMatch = 0; for( it = hotkeyMap[keycode].begin(); it < hotkeyMap[keycode].end(); it++ ) { // If a key has been pressed, and this event triggers on it's release, skip it. // Similarly, if the key's been released and the event triggers on a keypress, skip it. if( it->negation == typeKeyDown ) continue; // Check to see if all auxiliary keys are down std::vector::iterator itKey; bool accept = true; isCapturable = true; for( itKey = it->requires.begin(); itKey != it->requires.end(); itKey++ ) { int keyCode = *itKey & ~HOTKEY_NEGATION_FLAG; // Clear the negation-modifier bit bool rqdState = ( *itKey & HOTKEY_NEGATION_FLAG ) == 0; // debug_assert( !rqdState ); if( keyCode < SDLK_LAST ) { if( g_keys[keyCode] != rqdState ) accept = false; } else if( keyCode < UNIFIED_SHIFT ) { if( g_mouse_buttons[keyCode-SDLK_LAST] != rqdState ) accept = false; } else if( (size_t)(keyCode-UNIFIED_SHIFT) < ARRAY_SIZE(unified) ) { if( unified[keyCode-UNIFIED_SHIFT] != rqdState ) accept = false; } else { debug_printf(L"keyCode = %i\n", keyCode); debug_warn(L"keyCode out of range in GUI hotkey requirements"); } // If this event requires a multiple keypress (with the exception // of shift+key combinations) the console won't inhibit it. if( rqdState && ( *itKey != SDLK_RSHIFT ) && ( *itKey != SDLK_LSHIFT ) ) isCapturable = false; } if( it->mapsTo == HOTKEY_CONSOLE_TOGGLE ) isCapturable = false; // Because that would be silly. debug_assert((size_t)it->mapsTo < ARRAY_SIZE(hotkeys)); if( accept && !( isCapturable && consoleCapture ) ) { hotkeys[it->mapsTo] = true; if( it->requires.size() >= closestMapMatch ) { // Only if it's a more precise match, and it either isn't capturable or the console won't capture it. closestMap = it->mapsTo; closestMapMatch = it->requires.size() + 1; } } } if( closestMapMatch ) { hotkeyNotification.type = SDL_HOTKEYDOWN; hotkeyNotification.user.code = (int)closestMap; SDL_PushEvent( &hotkeyNotification ); } // GUI bit... could do with some optimization later. CStr closestMapName = -1; closestMapMatch = 0; for( itGUI = hotkeyMapGui[keycode].begin(); itGUI != hotkeyMapGui[keycode].end(); itGUI++ ) { // If a key has been pressed, and this event triggers on it's release, skip it. // Similarly, if the key's been released and the event triggers on a keypress, skip it. if( itGUI->negation == typeKeyDown ) continue; // Check to see if all auxiliary keys are down std::vector::iterator itKey; bool accept = true; isCapturable = true; for( itKey = itGUI->requires.begin(); itKey != itGUI->requires.end(); itKey++ ) { int keyCode = *itKey & ~HOTKEY_NEGATION_FLAG; // Clear the negation-modifier bit bool rqdState = ( *itKey & HOTKEY_NEGATION_FLAG ) == 0; if( keyCode < SDLK_LAST ) { if( g_keys[keyCode] != rqdState ) accept = false; } else if( keyCode < UNIFIED_SHIFT ) { if( g_mouse_buttons[keyCode-SDLK_LAST] != rqdState ) accept = false; } else if( (size_t)(keyCode-UNIFIED_SHIFT) < ARRAY_SIZE(unified) ) { if( unified[keyCode-UNIFIED_SHIFT] != rqdState ) accept = false; } else { debug_printf(L"keyCode = %i\n", keyCode); debug_warn(L"keyCode out of range in GUI hotkey requirements"); } // If this event requires a multiple keypress (with the exception // of shift+key combinations) the console won't inhibit it. if( rqdState && ( *itKey != SDLK_RSHIFT ) && ( *itKey != SDLK_LSHIFT ) ) isCapturable = false; } if( accept && !( isCapturable && consoleCapture ) ) { if( itGUI->requires.size() >= closestMapMatch ) { closestMapName = itGUI->mapsTo; closestMapMatch = itGUI->requires.size() + 1; } } } // GUI-objects bit // This fragment is an obvious candidate for rewriting when speed becomes an issue. if( closestMapMatch ) { GuiHotkeyMap::iterator map_it; GuiObjectList::iterator obj_it; map_it = guiHotkeyMap.find( closestMapName ); if( map_it != guiHotkeyMap.end() ) { GuiObjectList& targets = map_it->second; for( obj_it = targets.begin(); obj_it != targets.end(); obj_it++ ) { hotkeyNotification.type = SDL_GUIHOTKEYPRESS; hotkeyNotification.user.data1 = &(*obj_it); SDL_PushEvent( &hotkeyNotification ); } } } // -- KEYUP SECTION -- for( it = hotkeyMap[keycode].begin(); it < hotkeyMap[keycode].end(); it++ ) { // If it's a keydown event, won't cause HotKeyUps in anything that doesn't // use this key negated => skip them // If it's a keyup event, won't cause HotKeyUps in anything that does use // this key negated => skip them too. if( it->negation != typeKeyDown ) continue; // Check to see if all auxiliary keys are down std::vector::iterator itKey; bool accept = true; for( itKey = it->requires.begin(); itKey != it->requires.end(); itKey++ ) { if( *itKey < SDLK_LAST ) { if( !g_keys[*itKey] ) accept = false; } else if( *itKey < UNIFIED_SHIFT ) { if( !g_mouse_buttons[(*itKey)-SDLK_LAST] ) accept = false; } else if( *itKey < HOTKEY_NEGATION_FLAG ) { if( !unified[(*itKey)-UNIFIED_SHIFT] ) accept = false; } } debug_assert((size_t)it->mapsTo < ARRAY_SIZE(hotkeys)); if( accept ) { hotkeys[it->mapsTo] = false; hotkeyNotification.type = SDL_HOTKEYUP; hotkeyNotification.user.code = it->mapsTo; SDL_PushEvent( &hotkeyNotification ); } } return( IN_PASS ); } +CStr HotkeyGetName(int hotkey) +{ + if (hotkey < 0 || hotkey >= HOTKEY_LAST) + return ""; + return hotkeyInfo[hotkey].name; +} -bool HotkeyRespondsTo( int hotkey, int sdlkey ) +bool HotkeyRespondsTo(int hotkey, int sdlkey) { for (KeyMapping::iterator it = hotkeyMap[sdlkey].begin(); it != hotkeyMap[sdlkey].end(); ++it) if (it->mapsTo == hotkey) return true; return false; } -bool HotkeyIsPressed( const CStr& keyname ) +bool HotkeyIsPressed(const CStr& keyname) { return hotkeys[FindKeyCode(keyname)]; } Index: ps/trunk/source/ps/GameSetup/GameSetup.cpp =================================================================== --- ps/trunk/source/ps/GameSetup/GameSetup.cpp (revision 7258) +++ ps/trunk/source/ps/GameSetup/GameSetup.cpp (revision 7259) @@ -1,1007 +1,1018 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include "lib/app_hooks.h" #include "lib/input.h" #include "lib/lockfree.h" #include "lib/ogl.h" #include "lib/timer.h" #include "lib/wchar.h" #include "lib/external_libraries/sdl.h" #include "lib/res/h_mgr.h" #include "lib/res/graphics/cursor.h" #include "lib/res/sound/snd_mgr.h" #include "lib/sysdep/cpu.h" #include "lib/sysdep/gfx.h" #include "lib/tex/tex.h" #include "ps/CConsole.h" #include "ps/CLogger.h" #include "ps/ConfigDB.h" #include "ps/Filesystem.h" #include "ps/Font.h" #include "ps/Game.h" #include "ps/Globals.h" #include "ps/Hotkey.h" #include "ps/Interact.h" #include "ps/Loader.h" #include "ps/Overlay.h" #include "ps/Profile.h" #include "ps/ProfileViewer.h" #include "ps/StringConvert.h" #include "ps/Util.h" #include "ps/i18n.h" #include "graphics/CinemaTrack.h" #include "graphics/GameView.h" #include "graphics/LightEnv.h" #include "graphics/MapReader.h" #include "graphics/MaterialManager.h" #include "graphics/ParticleEngine.h" #include "graphics/TextureManager.h" #include "renderer/Renderer.h" #include "renderer/VertexBufferManager.h" #include "maths/MathUtil.h" #include "simulation/Entity.h" #include "simulation/EntityManager.h" #include "simulation/Scheduler.h" +#include "simulation2/Simulation2.h" + #include "scripting/ScriptableComplex.inl" #include "scripting/ScriptingHost.h" #include "scripting/DOMEvent.h" #include "scripting/GameEvents.h" #include "scripting/ScriptableComplex.h" + #include "maths/scripting/JSInterface_Vector3D.h" + #include "graphics/scripting/JSInterface_Camera.h" #include "graphics/scripting/JSInterface_LightEnv.h" + #include "ps/scripting/JSInterface_Selection.h" #include "ps/scripting/JSInterface_Console.h" #include "ps/scripting/JSCollection.h" + #include "simulation/scripting/SimulationScriptInit.h" -#include "gui/scripting/JSInterface_IGUIObject.h" -#include "gui/scripting/JSInterface_GUITypes.h" + #include "gui/GUI.h" #include "gui/GUIManager.h" +#include "gui/scripting/JSInterface_IGUIObject.h" +#include "gui/scripting/JSInterface_GUITypes.h" +#include "gui/scripting/ScriptFunctions.h" + #include "sound/JSI_Sound.h" #include "network/NetLog.h" #include "network/NetServer.h" #include "network/NetClient.h" #include "ps/Pyrogenesis.h" // psSetLogDir #include "ps/GameSetup/Atlas.h" #include "ps/GameSetup/GameSetup.h" #include "ps/GameSetup/Paths.h" #include "ps/GameSetup/Config.h" #include "ps/GameSetup/CmdLineArgs.h" ERROR_GROUP(System); ERROR_TYPE(System, SDLInitFailed); ERROR_TYPE(System, VmodeFailed); ERROR_TYPE(System, RequiredExtensionsMissing); #define LOG_CATEGORY L"gamesetup" bool g_DoRenderGui = true; static int SetVideoMode(int w, int h, int bpp, bool fullscreen) { SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); Uint32 flags = SDL_OPENGL; if(fullscreen) flags |= SDL_FULLSCREEN; if(!SDL_SetVideoMode(w, h, bpp, flags)) return -1; // Work around a bug in the proprietary Linux ATI driver (at least versions 8.16.20 and 8.14.13). // The driver appears to register its own atexit hook on context creation. // If this atexit hook is called before SDL_Quit destroys the OpenGL context, // some kind of double-free problem causes a crash and lockup in the driver. // Calling SDL_Quit twice appears to be harmless, though, and avoids the problem // by destroying the context *before* the driver's atexit hook is called. // (Note that atexit hooks are guaranteed to be called in reverse order of their registration.) atexit(SDL_Quit); // End work around. glViewport(0, 0, w, h); ogl_Init(); // required after each mode change if(SDL_SetGamma(g_Gamma, g_Gamma, g_Gamma) < 0) debug_warn(L"SDL_SetGamma failed"); return 0; } static const int SANE_TEX_QUALITY_DEFAULT = 5; // keep in sync with code static void SetTextureQuality(int quality) { int q_flags; GLint filter; retry: // keep this in sync with SANE_TEX_QUALITY_DEFAULT switch(quality) { // worst quality case 0: q_flags = OGL_TEX_HALF_RES|OGL_TEX_HALF_BPP; filter = GL_NEAREST; break; // [perf] add bilinear filtering case 1: q_flags = OGL_TEX_HALF_RES|OGL_TEX_HALF_BPP; filter = GL_LINEAR; break; // [vmem] no longer reduce resolution case 2: q_flags = OGL_TEX_HALF_BPP; filter = GL_LINEAR; break; // [vmem] add mipmaps case 3: q_flags = OGL_TEX_HALF_BPP; filter = GL_NEAREST_MIPMAP_LINEAR; break; // [perf] better filtering case 4: q_flags = OGL_TEX_HALF_BPP; filter = GL_LINEAR_MIPMAP_LINEAR; break; // [vmem] no longer reduce bpp case SANE_TEX_QUALITY_DEFAULT: q_flags = OGL_TEX_FULL_QUALITY; filter = GL_LINEAR_MIPMAP_LINEAR; break; // [perf] add anisotropy case 6: // TODO: add anisotropic filtering q_flags = OGL_TEX_FULL_QUALITY; filter = GL_LINEAR_MIPMAP_LINEAR; break; // invalid default: debug_warn(L"SetTextureQuality: invalid quality"); quality = SANE_TEX_QUALITY_DEFAULT; // careful: recursion doesn't work and we don't want to duplicate // the "sane" default values. goto retry; } ogl_tex_set_defaults(q_flags, filter); } //---------------------------------------------------------------------------- // GUI integration //---------------------------------------------------------------------------- // display progress / description in loading screen void GUI_DisplayLoadProgress(int percent, const wchar_t* pending_task) { CStrW i18n_description = I18n::translate(pending_task); JSString* js_desc = StringConvert::wstring_to_jsstring(g_ScriptingHost.getContext(), i18n_description); g_ScriptingHost.SetGlobal("g_Progress", INT_TO_JSVAL(percent)); g_ScriptingHost.SetGlobal("g_LoadDescription", STRING_TO_JSVAL(js_desc)); g_GUI->SendEventToAll("progress"); } void Render() { MICROLOG(L"begin frame"); ogl_WarnIfError(); CStr skystring = "61 193 255"; CFG_GET_USER_VAL("skycolor", String, skystring); CColor skycol; GUI::ParseString(skystring, skycol); g_Renderer.SetClearColor(skycol.AsSColor4ub()); // start new frame g_Renderer.BeginFrame(); ogl_WarnIfError(); if (g_Game && g_Game->IsGameStarted()) { g_Game->GetView()->Render(); glPushAttrib( GL_ENABLE_BIT ); glDisable( GL_LIGHTING ); glDisable( GL_TEXTURE_2D ); glDisable( GL_DEPTH_TEST ); if( g_EntGraph ) { PROFILE( "render entity overlays" ); glColor3f( 1.0f, 0.0f, 1.0f ); g_EntityManager.RenderAll(); // <-- collision outlines, pathing routes } glEnable( GL_DEPTH_TEST ); PROFILE_START( "render entity outlines" ); g_Mouseover.RenderSelectionOutlines(); g_Selection.RenderSelectionOutlines(); PROFILE_END( "render entity outlines" ); PROFILE_START( "render entity auras" ); g_Mouseover.RenderAuras(); g_Selection.RenderAuras(); PROFILE_END( "render entity auras" ); glDisable(GL_DEPTH_TEST); PROFILE_START( "render entity bars" ); pglActiveTextureARB(GL_TEXTURE1_ARB); glDisable(GL_TEXTURE_2D); pglActiveTextureARB(GL_TEXTURE0_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias); glMatrixMode(GL_TEXTURE); glLoadIdentity(); /*glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.0f, 1000.f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();*/ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); g_Mouseover.RenderBars(); g_Selection.RenderBars(); glDisable(GL_BLEND); /*glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW);*/ PROFILE_END( "render entity bars" ); glPopAttrib(); glMatrixMode(GL_MODELVIEW); // Depth test is now enabled PROFILE_START( "render rally points" ); g_Selection.RenderRallyPoints(); g_Mouseover.RenderRallyPoints(); PROFILE_END( "render rally points" ); PROFILE_START( "render cinematic splines" ); //Sets/resets renderering properties itself g_Game->GetView()->GetCinema()->DrawSpline(); PROFILE_END( "render cinematic splines" ); } ogl_WarnIfError(); PROFILE_START( "render fonts" ); MICROLOG(L"render fonts"); // overlay mode glPushAttrib(GL_ENABLE_BIT); glEnable(GL_TEXTURE_2D); glDisable(GL_CULL_FACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); PROFILE_END( "render fonts" ); ogl_WarnIfError(); // Temp GUI message GeeTODO MICROLOG(L"render GUI"); PROFILE_START( "render gui" ); if(g_DoRenderGui) g_GUI->Draw(); PROFILE_END( "render gui" ); ogl_WarnIfError(); // Particle Engine Updating CParticleEngine::GetInstance()->UpdateEmitters(); // Text: // Use the GL_ALPHA texture as the alpha channel with a flat colouring glDisable(GL_ALPHA_TEST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // Added -- glEnable(GL_TEXTURE_2D); // -- GL ogl_WarnIfError(); { PROFILE( "render console" ); glLoadIdentity(); MICROLOG(L"render console"); CFont font(L"console"); font.Bind(); g_Console->Render(); } ogl_WarnIfError(); // Profile information PROFILE_START( "render profiling" ); g_ProfileViewer.RenderProfile(); PROFILE_END( "render profiling" ); ogl_WarnIfError(); if (g_Game && g_Game->IsGameStarted()) { PROFILE( "render selection overlays" ); g_Mouseover.RenderOverlays(); g_Selection.RenderOverlays(); } ogl_WarnIfError(); // Draw the cursor (or set the Windows cursor, on Windows) CStrW cursorName = g_BuildingPlacer.m_active ? L"action-build" : g_CursorName; if (cursorName.empty()) cursor_draw(NULL, g_mouse_x, g_mouse_y); else cursor_draw(cursorName.c_str(), g_mouse_x, g_mouse_y); // restore glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); MICROLOG(L"end frame"); g_Renderer.EndFrame(); ogl_WarnIfError(); } static void RegisterJavascriptInterfaces() { // maths JSI_Vector3D::init(); // graphics JSI_Camera::init(); JSI_LightEnv::init(); CGameView::ScriptingInit(); // renderer CRenderer::ScriptingInit(); // sound JSI_Sound::ScriptingInit(); // scripting SColour::ScriptingInit(); CScriptEvent::ScriptingInit(); // call CJSComplexPropertyAccessor's ScriptingInit. doesn't really // matter which we use, but we know CJSPropertyAccessor is // already being compiled for T = CEntity. ScriptableComplex_InitComplexPropertyAccessor(); // ps JSI_Console::init(); CProfileNode::ScriptingInit(); CGameAttributes::ScriptingInit(); CPlayerSlot::ScriptingInit(); CPlayer::ScriptingInit(); PlayerCollection::Init( "PlayerCollection" ); // network CNetMessage::ScriptingInit(); CNetClient::ScriptingInit(); CNetServer::ScriptingInit(); CNetSession::ScriptingInit(); CServerPlayer::ScriptingInit(); // simulation SimulationScriptInit(); // GUI CGUI::ScriptingInit(); + + GuiScriptingInit(g_ScriptingHost.GetScriptInterface()); } static void InitScripting() { TIMER(L"InitScripting"); // Create the scripting host. This needs to be done before the GUI is created. // [7ms] new ScriptingHost; // It would be nice for onLoad code to be able to access the setTimeout() calls. new CScheduler; RegisterJavascriptInterfaces(); #define REG_JS_CONSTANT(_name) g_ScriptingHost.DefineConstant(#_name, _name) REG_JS_CONSTANT(SDL_BUTTON_LEFT); REG_JS_CONSTANT(SDL_BUTTON_MIDDLE); REG_JS_CONSTANT(SDL_BUTTON_RIGHT); REG_JS_CONSTANT(SDL_BUTTON_WHEELUP); REG_JS_CONSTANT(SDL_BUTTON_WHEELDOWN); #undef REG_JS_CONSTANT new CGameEvents; } static size_t ChooseCacheSize() { #if OS_WIN //const size_t overheadKiB = (wutil_WindowsVersion() >= WUTIL_VERSION_VISTA)? 1024 : 512; #endif return 96*MiB; } static void InitVfs(const CmdLineArgs& args) { TIMER(L"InitVfs"); const Paths paths(args); fs::wpath logs(paths.Logs()); CreateDirectories(logs, 0700); psSetLogDir(logs); const size_t cacheSize = ChooseCacheSize(); g_VFS = CreateVfs(cacheSize); g_VFS->Mount(L"screenshots/", paths.Data()/L"screenshots/"); g_VFS->Mount(L"config/", paths.RData()/L"config/"); g_VFS->Mount(L"profiles/", paths.Config()/L"profiles/"); g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); // (adding XMBs to archive speeds up subsequent reads) std::vector mods = args.GetMultiple("mod"); mods.push_back("public"); if(!args.Has("onlyPublicFiles")) mods.push_back("internal"); fs::wpath modArchivePath(paths.Cache()/L"mods"); fs::wpath modLoosePath(paths.RData()/L"mods"); for (size_t i = 0; i < mods.size(); ++i) { size_t priority = i; int flags = VFS_MOUNT_WATCH|VFS_MOUNT_ARCHIVABLE; std::wstring modName (wstring_from_utf8(mods[i])); g_VFS->Mount(L"", AddSlash(modLoosePath/modName), flags, priority); g_VFS->Mount(L"", AddSlash(modArchivePath/modName), flags, priority); } // note: don't bother with g_VFS->TextRepresentation - directories // haven't yet been populated and are empty. } static void InitPs(bool setup_gui) { if (setup_gui) { // The things here aren't strictly GUI, but they're unnecessary when in Atlas // because the game doesn't draw any text or handle keys or anything { // console TIMER(L"ps_console"); g_Console->UpdateScreenSize(g_xres, g_yres); // Calculate and store the line spacing CFont font(L"console"); g_Console->m_iFontHeight = font.GetLineSpacing(); g_Console->m_iFontWidth = font.GetCharacterWidth(L'C'); g_Console->m_charsPerPage = (size_t)(g_xres / g_Console->m_iFontWidth); // Offset by an arbitrary amount, to make it fit more nicely g_Console->m_iFontOffset = 9; } // language and hotkeys { TIMER(L"ps_lang_hotkeys"); std::string lang = "english"; CFG_GET_SYS_VAL("language", String, lang); I18n::LoadLanguage(lang.c_str()); LoadHotkeys(); } // GUI uses VFS, so this must come after VFS init. g_GUI->SwitchPage(L"page_pregame.xml", JSVAL_VOID); } else { // We do actually need *some* kind of GUI loaded, so use the // (currently empty) Atlas one g_GUI->SwitchPage(L"page_atlas.xml", JSVAL_VOID); } } static void InitInput() { SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); // register input handlers // This stack is constructed so the first added, will be the last // one called. This is important, because each of the handlers // has the potential to block events to go further down // in the chain. I.e. the last one in the list added, is the // only handler that can block all messages before they are // processed. in_add_handler(game_view_handler); in_add_handler(InteractInputHandler); in_add_handler(conInputHandler); in_add_handler(CProfileViewer::InputThunk); in_add_handler(HotkeyInputHandler); in_add_handler(GlobalsInputHandler); } static void ShutdownPs() { SAFE_DELETE(g_GUI); delete g_Console; g_Console = 0; // disable the special Windows cursor, or free textures for OGL cursors cursor_draw(0, g_mouse_x, g_mouse_y); // Unload the real language (since it depends on the scripting engine, // which is going to be killed later) and use the English fallback messages I18n::LoadLanguage(NULL); } static void InitRenderer() { TIMER(L"InitRenderer"); if(g_NoGLS3TC) ogl_tex_override(OGL_TEX_S3TC, OGL_TEX_DISABLE); if(g_NoGLAutoMipmap) ogl_tex_override(OGL_TEX_AUTO_MIPMAP_GEN, OGL_TEX_DISABLE); // create renderer new CRenderer; // set renderer options from command line options - NOVBO must be set before opening the renderer g_Renderer.SetOptionBool(CRenderer::OPT_NOVBO,g_NoGLVBO); g_Renderer.SetOptionBool(CRenderer::OPT_NOFRAMEBUFFEROBJECT,g_NoGLFramebufferObject); g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS,g_Shadows); g_Renderer.SetOptionBool(CRenderer::OPT_FANCYWATER,g_FancyWater); g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath)); g_Renderer.SetOptionFloat(CRenderer::OPT_LODBIAS, g_LodBias); // create terrain related stuff new CTextureManager; // create the material manager new CMaterialManager; MICROLOG(L"init renderer"); g_Renderer.Open(g_xres,g_yres,g_bpp); // Setup lighting environment. Since the Renderer accesses the // lighting environment through a pointer, this has to be done before // the first Frame. g_Renderer.SetLightEnv(&g_LightEnv); // I haven't seen the camera affecting GUI rendering and such, but the // viewport has to be updated according to the video mode SViewPort vp; vp.m_X=0; vp.m_Y=0; vp.m_Width=g_xres; vp.m_Height=g_yres; g_Renderer.SetViewport(vp); ColorActivateFastImpl(); } static void InitSDL() { MICROLOG(L"init sdl"); if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) < 0) { LOG(CLogger::Error, LOG_CATEGORY, L"SDL library initialization failed: %hs", SDL_GetError()); throw PSERROR_System_SDLInitFailed(); } atexit(SDL_Quit); SDL_EnableUNICODE(1); } void EndGame() { if (g_NetServer) { delete g_NetServer; g_NetServer=NULL; } else if (g_NetClient) { delete g_NetClient; g_NetClient=NULL; } delete g_Game; g_Game=NULL; } void Shutdown(int flags) { MICROLOG(L"Shutdown"); if (g_Game) EndGame(); ShutdownPs(); // Must delete g_GUI before g_ScriptingHost TIMER_BEGIN(L"shutdown Scheduler"); delete &g_Scheduler; TIMER_END(L"shutdown Scheduler"); delete &g_JSGameEvents; if (! (flags & INIT_NO_SIM)) { TIMER_BEGIN(L"shutdown mouse stuff"); delete &g_Mouseover; delete &g_Selection; delete &g_BuildingPlacer; TIMER_END(L"shutdown mouse stuff"); TIMER_BEGIN(L"shutdown game scripting stuff"); delete &g_GameAttributes; SimulationShutdown(); TIMER_END(L"shutdown game scripting stuff"); } // destroy actor related stuff TIMER_BEGIN(L"shutdown actor stuff"); delete &g_MaterialManager; TIMER_END(L"shutdown actor stuff"); // destroy terrain related stuff TIMER_BEGIN(L"shutdown TexMan"); delete &g_TexMan; TIMER_END(L"shutdown TexMan"); // destroy renderer TIMER_BEGIN(L"shutdown Renderer"); delete &g_Renderer; g_VBMan.Shutdown(); TIMER_END(L"shutdown Renderer"); TIMER_BEGIN(L"shutdown ScriptingHost"); delete &g_ScriptingHost; TIMER_END(L"shutdown ScriptingHost"); TIMER_BEGIN(L"shutdown ConfigDB"); delete &g_ConfigDB; TIMER_END(L"shutdown ConfigDB"); // Shut down the network loop TIMER_BEGIN(L"shutdown CSocketBase"); CSocketBase::Shutdown(); TIMER_END(L"shutdown CSocketBase"); TIMER_BEGIN(L"shutdown CNetLogManager"); CNetLogManager::Shutdown(); TIMER_END(L"shutdown CNetLogManager"); // Really shut down the i18n system. Any future calls // to translate() will crash. TIMER_BEGIN(L"shutdown I18N"); I18n::Shutdown(); TIMER_END(L"shutdown I18N"); // resource // first shut down all resource owners, and then the handle manager. TIMER_BEGIN(L"resource modules"); snd_shutdown(); g_VFS.reset(); // this forcibly frees all open handles (thus preventing real leaks), // and makes further access to h_mgr impossible. h_mgr_shutdown(); TIMER_END(L"resource modules"); TIMER_BEGIN(L"shutdown misc"); timer_DisplayClientTotals(); // should be last, since the above use them SAFE_DELETE(g_Logger); delete &g_Profiler; delete &g_ProfileViewer; TIMER_END(L"shutdown misc"); } void EarlyInit() { MICROLOG(L"EarlyInit"); // If you ever want to catch a particular allocation: //_CrtSetBreakAlloc(232647); debug_SetThreadName("main"); // add all debug_printf "tags" that we are interested in: debug_filter_add(L"TIMER"); debug_filter_add(L"HRT"); cpu_ConfigureFloatingPoint(); timer_LatchStartTime(); // Initialise the low-quality rand function srand(time(NULL)); // NOTE: this rand should *not* be used for simulation! } void Init(const CmdLineArgs& args, int flags) { const bool setup_vmode = (flags & INIT_HAVE_VMODE) == 0; MICROLOG(L"Init"); h_mgr_init(); // Do this as soon as possible, because it chdirs // and will mess up the error reporting if anything // crashes before the working directory is set. MICROLOG(L"init vfs"); InitVfs(args); // This must come after VFS init, which sets the current directory // (required for finding our output log files). g_Logger = new CLogger; // Call LoadLanguage(NULL) to initialize the I18n system, but // without loading an actual language file - translate() will // just show the English key text, which is better than crashing // from a null pointer when attempting to translate e.g. error messages. // Real languages can only be loaded when the scripting system has // been initialised. // // this uses LOG and must therefore come after CLogger init. MICROLOG(L"init i18n"); I18n::LoadLanguage(NULL); // override ah_translate with our i18n code. AppHooks hooks = {0}; hooks.translate = psTranslate; hooks.translate_free = psTranslateFree; hooks.bundle_logs = psBundleLogs; hooks.get_log_dir = psLogDir; app_hooks_update(&hooks); // Set up the console early, so that debugging // messages can be logged to it. (The console's size // and fonts are set later in InitPs()) g_Console = new CConsole(); if(setup_vmode) InitSDL(); // preferred video mode = current desktop settings // (command line params may override these) gfx_get_video_mode(&g_xres, &g_yres, &g_bpp, &g_freq); new CProfileViewer; new CProfileManager; // before any script code MICROLOG(L"init scripting"); InitScripting(); // before GUI // g_ConfigDB, command line args, globals CONFIG_Init(args); // setup_gui must be set after CONFIG_Init, so command-line parameters can disable it const bool setup_gui = ((flags & INIT_NO_GUI) == 0 && g_AutostartMap.empty()); // GUI is notified in SetVideoMode, so this must come before that. - g_GUI = new CGUIManager(); + g_GUI = new CGUIManager(g_ScriptingHost.GetScriptInterface()); // default to windowed, so users don't get stuck if e.g. half the // filesystem is missing and the config files aren't loaded bool windowed = true; CFG_GET_SYS_VAL("windowed", Bool, windowed); if(setup_vmode) { MICROLOG(L"SetVideoMode"); if(SetVideoMode(g_xres, g_yres, 32, !windowed) < 0) { LOG(CLogger::Error, LOG_CATEGORY, L"Could not set %dx%d graphics mode: %hs", g_xres, g_yres, SDL_GetError()); throw PSERROR_System_VmodeFailed(); } SDL_WM_SetCaption("0 A.D.", "0 A.D."); } tex_codec_register_all(); const int quality = SANE_TEX_QUALITY_DEFAULT; // TODO: set value from config file SetTextureQuality(quality); // needed by ogl_tex to detect broken gfx card/driver combos, // but takes a while due to WMI startup, so make it optional. if(!g_Quickstart) gfx_detect(); ogl_WarnIfError(); if(!g_Quickstart) { WriteSystemInfo(); // note: no longer vfs_display here. it's dog-slow due to unbuffered // file output and very rarely needed. } else { // speed up startup by disabling all sound // (OpenAL init will be skipped). // must be called before first snd_open. snd_disable(true); } // (must come after SetVideoMode, since it calls ogl_Init) const char* missing = ogl_HaveExtensions(0, "GL_ARB_multitexture", "GL_EXT_draw_range_elements", "GL_ARB_texture_env_combine", "GL_ARB_texture_env_dot3", NULL); if(missing) { wchar_t buf[500]; swprintf_s(buf, ARRAY_SIZE(buf), L"The %hs extension doesn't appear to be available on your computer." L" The game may still work, though - you are welcome to try at your own risk." L" If not or it doesn't look right, upgrade your graphics card.", missing ); DEBUG_DISPLAY_ERROR(buf); // TODO: i18n } if (!ogl_HaveExtension("GL_ARB_texture_env_crossbar")) { DEBUG_DISPLAY_ERROR( L"The GL_ARB_texture_env_crossbar extension doesn't appear to be available on your computer." L" Shadows are not available and overall graphics quality might suffer." L" You are advised to try installing newer drivers and/or upgrade your graphics card."); g_Shadows = false; } // enable/disable VSync // note: "GL_EXT_SWAP_CONTROL" is "historical" according to dox. #if OS_WIN if(ogl_HaveExtension("WGL_EXT_swap_control")) pwglSwapIntervalEXT(g_VSync? 1 : 0); #endif MICROLOG(L"init ps"); InitPs(setup_gui); ogl_WarnIfError(); InitRenderer(); if (! (flags & INIT_NO_SIM)) { // This needs to be done after the renderer has loaded all its actors... SimulationInit(); { TIMER(L"Init_miscgamesection"); new CSelectedEntities; new CMouseoverEntities; new CBuildingPlacer; new CGameAttributes; } // Register a few Game/Network JS globals g_ScriptingHost.SetGlobal("g_GameAttributes", OBJECT_TO_JSVAL(g_GameAttributes.GetScript())); } InitInput(); ogl_WarnIfError(); if (g_FixedFrameTiming) { CCamera &camera = *g_Game->GetView()->GetCamera(); #if 0 // TOPDOWN camera.SetProjection(1.0f,10000.0f,DEGTORAD(90)); camera.m_Orientation.SetIdentity(); camera.m_Orientation.RotateX(DEGTORAD(90)); camera.m_Orientation.Translate(CELL_SIZE*250*0.5, 250, CELL_SIZE*250*0.5); #else // std view camera.SetProjection(1.0f,10000.0f,DEGTORAD(20)); camera.m_Orientation.SetXRotation(DEGTORAD(30)); camera.m_Orientation.RotateY(DEGTORAD(-45)); camera.m_Orientation.Translate(350, 350, -275); #endif camera.UpdateFrustum(); } if (! g_AutostartMap.empty()) { // Code copied mostly from atlas/GameInterface/Handlers/Map.cpp - // maybe should be refactored to avoid duplication g_GameAttributes.m_MapFile = g_AutostartMap+".pmp"; for (int i=1; i<8; ++i) g_GameAttributes.GetSlot(i)->AssignLocal(); g_Game = new CGame(); PSRETURN ret = g_Game->StartGame(&g_GameAttributes); debug_assert(ret == PSRETURN_OK); LDR_NonprogressiveLoad(); ret = g_Game->ReallyStartGame(); debug_assert(ret == PSRETURN_OK); } } void RenderGui(bool RenderingState) { g_DoRenderGui = RenderingState; } Index: ps/trunk/source/ps/Errors.cpp =================================================================== --- ps/trunk/source/ps/Errors.cpp (revision 7258) +++ ps/trunk/source/ps/Errors.cpp (revision 7259) @@ -1,351 +1,566 @@ // Auto-generated by errorlist.pl - do not edit. #include "precompiled.h" #include "Errors.h" -class PSERROR_CVFSFile : public PSERROR {}; -class PSERROR_DllLoader : public PSERROR {}; -class PSERROR_Error : public PSERROR {}; -class PSERROR_File : public PSERROR {}; -class PSERROR_GUI : public PSERROR {}; -class PSERROR_Game : public PSERROR {}; -class PSERROR_I18n : public PSERROR {}; -class PSERROR_Renderer : public PSERROR {}; -class PSERROR_Scripting : public PSERROR {}; -class PSERROR_System : public PSERROR {}; -class PSERROR_Xeromyces : public PSERROR {}; - -class PSERROR_Game_World : public PSERROR_Game {}; -class PSERROR_I18n_Script : public PSERROR_I18n {}; -class PSERROR_Scripting_DefineType : public PSERROR_Scripting {}; -class PSERROR_Scripting_LoadFile : public PSERROR_Scripting {}; - -class PSERROR_CVFSFile_AlreadyLoaded : public PSERROR_CVFSFile { public: PSRETURN getCode() const; }; -class PSERROR_CVFSFile_InvalidBufferAccess : public PSERROR_CVFSFile { public: PSRETURN getCode() const; }; -class PSERROR_CVFSFile_LoadFailed : public PSERROR_CVFSFile { public: PSRETURN getCode() const; }; -class PSERROR_DllLoader_DllNotLoaded : public PSERROR_DllLoader { public: PSRETURN getCode() const; }; -class PSERROR_DllLoader_SymbolNotFound : public PSERROR_DllLoader { public: PSRETURN getCode() const; }; -class PSERROR_Error_InvalidError : public PSERROR_Error { public: PSRETURN getCode() const; }; -class PSERROR_File_InvalidType : public PSERROR_File { public: PSRETURN getCode() const; }; -class PSERROR_File_InvalidVersion : public PSERROR_File { public: PSRETURN getCode() const; }; -class PSERROR_File_OpenFailed : public PSERROR_File { public: PSRETURN getCode() const; }; -class PSERROR_File_ReadFailed : public PSERROR_File { public: PSRETURN getCode() const; }; -class PSERROR_File_UnexpectedEOF : public PSERROR_File { public: PSRETURN getCode() const; }; -class PSERROR_File_WriteFailed : public PSERROR_File { public: PSRETURN getCode() const; }; -class PSERROR_GUI_InvalidSetting : public PSERROR_GUI { public: PSRETURN getCode() const; }; -class PSERROR_GUI_JSOpenFailed : public PSERROR_GUI { public: PSRETURN getCode() const; }; -class PSERROR_GUI_NameAmbiguity : public PSERROR_GUI { public: PSRETURN getCode() const; }; -class PSERROR_GUI_NullObjectProvided : public PSERROR_GUI { public: PSRETURN getCode() const; }; -class PSERROR_GUI_ObjectNeedsName : public PSERROR_GUI { public: PSRETURN getCode() const; }; -class PSERROR_GUI_OperationNeedsGUIObject : public PSERROR_GUI { public: PSRETURN getCode() const; }; -class PSERROR_GUI_UnableToParse : public PSERROR_GUI { public: PSRETURN getCode() const; }; -class PSERROR_Game_World_MapLoadFailed : public PSERROR_Game_World { public: PSRETURN getCode() const; }; -class PSERROR_I18n_Script_SetupFailed : public PSERROR_I18n_Script { public: PSRETURN getCode() const; }; -class PSERROR_Renderer_VBOFailed : public PSERROR_Renderer { public: PSRETURN getCode() const; }; -class PSERROR_Scripting_CallFunctionFailed : public PSERROR_Scripting { public: PSRETURN getCode() const; }; -class PSERROR_Scripting_ConversionFailed : public PSERROR_Scripting { public: PSRETURN getCode() const; }; -class PSERROR_Scripting_CreateObjectFailed : public PSERROR_Scripting { public: PSRETURN getCode() const; }; -class PSERROR_Scripting_DefineConstantFailed : public PSERROR_Scripting { public: PSRETURN getCode() const; }; -class PSERROR_Scripting_DefineType_AlreadyExists : public PSERROR_Scripting_DefineType { public: PSRETURN getCode() const; }; -class PSERROR_Scripting_DefineType_CreationFailed : public PSERROR_Scripting_DefineType { public: PSRETURN getCode() const; }; -class PSERROR_Scripting_LoadFile_EvalErrors : public PSERROR_Scripting_LoadFile { public: PSRETURN getCode() const; }; -class PSERROR_Scripting_LoadFile_OpenFailed : public PSERROR_Scripting_LoadFile { public: PSRETURN getCode() const; }; -class PSERROR_Scripting_RegisterFunctionFailed : public PSERROR_Scripting { public: PSRETURN getCode() const; }; -class PSERROR_Scripting_SetupFailed : public PSERROR_Scripting { public: PSRETURN getCode() const; }; -class PSERROR_Scripting_TypeDoesNotExist : public PSERROR_Scripting { public: PSRETURN getCode() const; }; -class PSERROR_System_RequiredExtensionsMissing : public PSERROR_System { public: PSRETURN getCode() const; }; -class PSERROR_System_SDLInitFailed : public PSERROR_System { public: PSRETURN getCode() const; }; -class PSERROR_System_VmodeFailed : public PSERROR_System { public: PSRETURN getCode() const; }; -class PSERROR_Xeromyces_XMLOpenFailed : public PSERROR_Xeromyces { public: PSRETURN getCode() const; }; -class PSERROR_Xeromyces_XMLParseError : public PSERROR_Xeromyces { public: PSRETURN getCode() const; }; +class PSERROR_CVFSFile : public PSERROR { protected: PSERROR_CVFSFile(const char* msg); }; +class PSERROR_Deserialize : public PSERROR { protected: PSERROR_Deserialize(const char* msg); }; +class PSERROR_DllLoader : public PSERROR { protected: PSERROR_DllLoader(const char* msg); }; +class PSERROR_Error : public PSERROR { protected: PSERROR_Error(const char* msg); }; +class PSERROR_File : public PSERROR { protected: PSERROR_File(const char* msg); }; +class PSERROR_GUI : public PSERROR { protected: PSERROR_GUI(const char* msg); }; +class PSERROR_Game : public PSERROR { protected: PSERROR_Game(const char* msg); }; +class PSERROR_I18n : public PSERROR { protected: PSERROR_I18n(const char* msg); }; +class PSERROR_Renderer : public PSERROR { protected: PSERROR_Renderer(const char* msg); }; +class PSERROR_Scripting : public PSERROR { protected: PSERROR_Scripting(const char* msg); }; +class PSERROR_Serialize : public PSERROR { protected: PSERROR_Serialize(const char* msg); }; +class PSERROR_System : public PSERROR { protected: PSERROR_System(const char* msg); }; +class PSERROR_Xeromyces : public PSERROR { protected: PSERROR_Xeromyces(const char* msg); }; + +class PSERROR_Game_World : public PSERROR_Game { protected: PSERROR_Game_World(const char* msg); }; +class PSERROR_I18n_Script : public PSERROR_I18n { protected: PSERROR_I18n_Script(const char* msg); }; +class PSERROR_Scripting_DefineType : public PSERROR_Scripting { protected: PSERROR_Scripting_DefineType(const char* msg); }; +class PSERROR_Scripting_LoadFile : public PSERROR_Scripting { protected: PSERROR_Scripting_LoadFile(const char* msg); }; + +class PSERROR_CVFSFile_AlreadyLoaded : public PSERROR_CVFSFile { public: PSERROR_CVFSFile_AlreadyLoaded(); PSERROR_CVFSFile_AlreadyLoaded(const char* msg); PSRETURN getCode() const; }; +class PSERROR_CVFSFile_InvalidBufferAccess : public PSERROR_CVFSFile { public: PSERROR_CVFSFile_InvalidBufferAccess(); PSERROR_CVFSFile_InvalidBufferAccess(const char* msg); PSRETURN getCode() const; }; +class PSERROR_CVFSFile_LoadFailed : public PSERROR_CVFSFile { public: PSERROR_CVFSFile_LoadFailed(); PSERROR_CVFSFile_LoadFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Deserialize_InvalidCharInString : public PSERROR_Deserialize { public: PSERROR_Deserialize_InvalidCharInString(); PSERROR_Deserialize_InvalidCharInString(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Deserialize_OutOfBounds : public PSERROR_Deserialize { public: PSERROR_Deserialize_OutOfBounds(); PSERROR_Deserialize_OutOfBounds(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Deserialize_ReadFailed : public PSERROR_Deserialize { public: PSERROR_Deserialize_ReadFailed(); PSERROR_Deserialize_ReadFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Deserialize_ScriptError : public PSERROR_Deserialize { public: PSERROR_Deserialize_ScriptError(); PSERROR_Deserialize_ScriptError(const char* msg); PSRETURN getCode() const; }; +class PSERROR_DllLoader_DllNotLoaded : public PSERROR_DllLoader { public: PSERROR_DllLoader_DllNotLoaded(); PSERROR_DllLoader_DllNotLoaded(const char* msg); PSRETURN getCode() const; }; +class PSERROR_DllLoader_SymbolNotFound : public PSERROR_DllLoader { public: PSERROR_DllLoader_SymbolNotFound(); PSERROR_DllLoader_SymbolNotFound(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Error_InvalidError : public PSERROR_Error { public: PSERROR_Error_InvalidError(); PSERROR_Error_InvalidError(const char* msg); PSRETURN getCode() const; }; +class PSERROR_File_InvalidType : public PSERROR_File { public: PSERROR_File_InvalidType(); PSERROR_File_InvalidType(const char* msg); PSRETURN getCode() const; }; +class PSERROR_File_InvalidVersion : public PSERROR_File { public: PSERROR_File_InvalidVersion(); PSERROR_File_InvalidVersion(const char* msg); PSRETURN getCode() const; }; +class PSERROR_File_OpenFailed : public PSERROR_File { public: PSERROR_File_OpenFailed(); PSERROR_File_OpenFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_File_ReadFailed : public PSERROR_File { public: PSERROR_File_ReadFailed(); PSERROR_File_ReadFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_File_UnexpectedEOF : public PSERROR_File { public: PSERROR_File_UnexpectedEOF(); PSERROR_File_UnexpectedEOF(const char* msg); PSRETURN getCode() const; }; +class PSERROR_File_WriteFailed : public PSERROR_File { public: PSERROR_File_WriteFailed(); PSERROR_File_WriteFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_GUI_InvalidSetting : public PSERROR_GUI { public: PSERROR_GUI_InvalidSetting(); PSERROR_GUI_InvalidSetting(const char* msg); PSRETURN getCode() const; }; +class PSERROR_GUI_JSOpenFailed : public PSERROR_GUI { public: PSERROR_GUI_JSOpenFailed(); PSERROR_GUI_JSOpenFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_GUI_NameAmbiguity : public PSERROR_GUI { public: PSERROR_GUI_NameAmbiguity(); PSERROR_GUI_NameAmbiguity(const char* msg); PSRETURN getCode() const; }; +class PSERROR_GUI_NullObjectProvided : public PSERROR_GUI { public: PSERROR_GUI_NullObjectProvided(); PSERROR_GUI_NullObjectProvided(const char* msg); PSRETURN getCode() const; }; +class PSERROR_GUI_ObjectNeedsName : public PSERROR_GUI { public: PSERROR_GUI_ObjectNeedsName(); PSERROR_GUI_ObjectNeedsName(const char* msg); PSRETURN getCode() const; }; +class PSERROR_GUI_OperationNeedsGUIObject : public PSERROR_GUI { public: PSERROR_GUI_OperationNeedsGUIObject(); PSERROR_GUI_OperationNeedsGUIObject(const char* msg); PSRETURN getCode() const; }; +class PSERROR_GUI_UnableToParse : public PSERROR_GUI { public: PSERROR_GUI_UnableToParse(); PSERROR_GUI_UnableToParse(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Game_World_MapLoadFailed : public PSERROR_Game_World { public: PSERROR_Game_World_MapLoadFailed(); PSERROR_Game_World_MapLoadFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_I18n_Script_SetupFailed : public PSERROR_I18n_Script { public: PSERROR_I18n_Script_SetupFailed(); PSERROR_I18n_Script_SetupFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Renderer_VBOFailed : public PSERROR_Renderer { public: PSERROR_Renderer_VBOFailed(); PSERROR_Renderer_VBOFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Scripting_CallFunctionFailed : public PSERROR_Scripting { public: PSERROR_Scripting_CallFunctionFailed(); PSERROR_Scripting_CallFunctionFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Scripting_ConversionFailed : public PSERROR_Scripting { public: PSERROR_Scripting_ConversionFailed(); PSERROR_Scripting_ConversionFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Scripting_CreateObjectFailed : public PSERROR_Scripting { public: PSERROR_Scripting_CreateObjectFailed(); PSERROR_Scripting_CreateObjectFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Scripting_DefineConstantFailed : public PSERROR_Scripting { public: PSERROR_Scripting_DefineConstantFailed(); PSERROR_Scripting_DefineConstantFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Scripting_DefineType_AlreadyExists : public PSERROR_Scripting_DefineType { public: PSERROR_Scripting_DefineType_AlreadyExists(); PSERROR_Scripting_DefineType_AlreadyExists(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Scripting_DefineType_CreationFailed : public PSERROR_Scripting_DefineType { public: PSERROR_Scripting_DefineType_CreationFailed(); PSERROR_Scripting_DefineType_CreationFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Scripting_LoadFile_EvalErrors : public PSERROR_Scripting_LoadFile { public: PSERROR_Scripting_LoadFile_EvalErrors(); PSERROR_Scripting_LoadFile_EvalErrors(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Scripting_LoadFile_OpenFailed : public PSERROR_Scripting_LoadFile { public: PSERROR_Scripting_LoadFile_OpenFailed(); PSERROR_Scripting_LoadFile_OpenFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Scripting_RegisterFunctionFailed : public PSERROR_Scripting { public: PSERROR_Scripting_RegisterFunctionFailed(); PSERROR_Scripting_RegisterFunctionFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Scripting_SetupFailed : public PSERROR_Scripting { public: PSERROR_Scripting_SetupFailed(); PSERROR_Scripting_SetupFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Scripting_TypeDoesNotExist : public PSERROR_Scripting { public: PSERROR_Scripting_TypeDoesNotExist(); PSERROR_Scripting_TypeDoesNotExist(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Serialize_InvalidCharInString : public PSERROR_Serialize { public: PSERROR_Serialize_InvalidCharInString(); PSERROR_Serialize_InvalidCharInString(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Serialize_InvalidScriptValue : public PSERROR_Serialize { public: PSERROR_Serialize_InvalidScriptValue(); PSERROR_Serialize_InvalidScriptValue(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Serialize_OutOfBounds : public PSERROR_Serialize { public: PSERROR_Serialize_OutOfBounds(); PSERROR_Serialize_OutOfBounds(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Serialize_ScriptError : public PSERROR_Serialize { public: PSERROR_Serialize_ScriptError(); PSERROR_Serialize_ScriptError(const char* msg); PSRETURN getCode() const; }; +class PSERROR_System_RequiredExtensionsMissing : public PSERROR_System { public: PSERROR_System_RequiredExtensionsMissing(); PSERROR_System_RequiredExtensionsMissing(const char* msg); PSRETURN getCode() const; }; +class PSERROR_System_SDLInitFailed : public PSERROR_System { public: PSERROR_System_SDLInitFailed(); PSERROR_System_SDLInitFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_System_VmodeFailed : public PSERROR_System { public: PSERROR_System_VmodeFailed(); PSERROR_System_VmodeFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Xeromyces_XMLOpenFailed : public PSERROR_Xeromyces { public: PSERROR_Xeromyces_XMLOpenFailed(); PSERROR_Xeromyces_XMLOpenFailed(const char* msg); PSRETURN getCode() const; }; +class PSERROR_Xeromyces_XMLParseError : public PSERROR_Xeromyces { public: PSERROR_Xeromyces_XMLParseError(); PSERROR_Xeromyces_XMLParseError(const char* msg); PSRETURN getCode() const; }; extern const PSRETURN PSRETURN_CVFSFile_AlreadyLoaded = 0x01000001; extern const PSRETURN PSRETURN_CVFSFile_InvalidBufferAccess = 0x01000002; extern const PSRETURN PSRETURN_CVFSFile_LoadFailed = 0x01000003; -extern const PSRETURN PSRETURN_DllLoader_DllNotLoaded = 0x02000001; -extern const PSRETURN PSRETURN_DllLoader_SymbolNotFound = 0x02000002; -extern const PSRETURN PSRETURN_Error_InvalidError = 0x03000001; -extern const PSRETURN PSRETURN_File_InvalidType = 0x04000001; -extern const PSRETURN PSRETURN_File_InvalidVersion = 0x04000002; -extern const PSRETURN PSRETURN_File_OpenFailed = 0x04000003; -extern const PSRETURN PSRETURN_File_ReadFailed = 0x04000004; -extern const PSRETURN PSRETURN_File_UnexpectedEOF = 0x04000005; -extern const PSRETURN PSRETURN_File_WriteFailed = 0x04000006; -extern const PSRETURN PSRETURN_GUI_InvalidSetting = 0x05000001; -extern const PSRETURN PSRETURN_GUI_JSOpenFailed = 0x05000002; -extern const PSRETURN PSRETURN_GUI_NameAmbiguity = 0x05000003; -extern const PSRETURN PSRETURN_GUI_NullObjectProvided = 0x05000004; -extern const PSRETURN PSRETURN_GUI_ObjectNeedsName = 0x05000005; -extern const PSRETURN PSRETURN_GUI_OperationNeedsGUIObject = 0x05000006; -extern const PSRETURN PSRETURN_GUI_UnableToParse = 0x05000007; -extern const PSRETURN PSRETURN_Game_World_MapLoadFailed = 0x06040001; -extern const PSRETURN PSRETURN_I18n_Script_SetupFailed = 0x07030001; -extern const PSRETURN PSRETURN_Renderer_VBOFailed = 0x08000001; -extern const PSRETURN PSRETURN_Scripting_DefineType_AlreadyExists = 0x09010001; -extern const PSRETURN PSRETURN_Scripting_DefineType_CreationFailed = 0x09010002; -extern const PSRETURN PSRETURN_Scripting_LoadFile_EvalErrors = 0x09020001; -extern const PSRETURN PSRETURN_Scripting_LoadFile_OpenFailed = 0x09020002; -extern const PSRETURN PSRETURN_Scripting_CallFunctionFailed = 0x09000001; -extern const PSRETURN PSRETURN_Scripting_ConversionFailed = 0x09000002; -extern const PSRETURN PSRETURN_Scripting_CreateObjectFailed = 0x09000003; -extern const PSRETURN PSRETURN_Scripting_DefineConstantFailed = 0x09000004; -extern const PSRETURN PSRETURN_Scripting_RegisterFunctionFailed = 0x09000005; -extern const PSRETURN PSRETURN_Scripting_SetupFailed = 0x09000006; -extern const PSRETURN PSRETURN_Scripting_TypeDoesNotExist = 0x09000007; -extern const PSRETURN PSRETURN_System_RequiredExtensionsMissing = 0x0a000001; -extern const PSRETURN PSRETURN_System_SDLInitFailed = 0x0a000002; -extern const PSRETURN PSRETURN_System_VmodeFailed = 0x0a000003; -extern const PSRETURN PSRETURN_Xeromyces_XMLOpenFailed = 0x0b000001; -extern const PSRETURN PSRETURN_Xeromyces_XMLParseError = 0x0b000002; +extern const PSRETURN PSRETURN_Deserialize_InvalidCharInString = 0x02000001; +extern const PSRETURN PSRETURN_Deserialize_OutOfBounds = 0x02000002; +extern const PSRETURN PSRETURN_Deserialize_ReadFailed = 0x02000003; +extern const PSRETURN PSRETURN_Deserialize_ScriptError = 0x02000004; +extern const PSRETURN PSRETURN_DllLoader_DllNotLoaded = 0x03000001; +extern const PSRETURN PSRETURN_DllLoader_SymbolNotFound = 0x03000002; +extern const PSRETURN PSRETURN_Error_InvalidError = 0x04000001; +extern const PSRETURN PSRETURN_File_InvalidType = 0x05000001; +extern const PSRETURN PSRETURN_File_InvalidVersion = 0x05000002; +extern const PSRETURN PSRETURN_File_OpenFailed = 0x05000003; +extern const PSRETURN PSRETURN_File_ReadFailed = 0x05000004; +extern const PSRETURN PSRETURN_File_UnexpectedEOF = 0x05000005; +extern const PSRETURN PSRETURN_File_WriteFailed = 0x05000006; +extern const PSRETURN PSRETURN_GUI_InvalidSetting = 0x06000001; +extern const PSRETURN PSRETURN_GUI_JSOpenFailed = 0x06000002; +extern const PSRETURN PSRETURN_GUI_NameAmbiguity = 0x06000003; +extern const PSRETURN PSRETURN_GUI_NullObjectProvided = 0x06000004; +extern const PSRETURN PSRETURN_GUI_ObjectNeedsName = 0x06000005; +extern const PSRETURN PSRETURN_GUI_OperationNeedsGUIObject = 0x06000006; +extern const PSRETURN PSRETURN_GUI_UnableToParse = 0x06000007; +extern const PSRETURN PSRETURN_Game_World_MapLoadFailed = 0x07040001; +extern const PSRETURN PSRETURN_I18n_Script_SetupFailed = 0x08030001; +extern const PSRETURN PSRETURN_Renderer_VBOFailed = 0x09000001; +extern const PSRETURN PSRETURN_Scripting_DefineType_AlreadyExists = 0x0a010001; +extern const PSRETURN PSRETURN_Scripting_DefineType_CreationFailed = 0x0a010002; +extern const PSRETURN PSRETURN_Scripting_LoadFile_EvalErrors = 0x0a020001; +extern const PSRETURN PSRETURN_Scripting_LoadFile_OpenFailed = 0x0a020002; +extern const PSRETURN PSRETURN_Scripting_CallFunctionFailed = 0x0a000001; +extern const PSRETURN PSRETURN_Scripting_ConversionFailed = 0x0a000002; +extern const PSRETURN PSRETURN_Scripting_CreateObjectFailed = 0x0a000003; +extern const PSRETURN PSRETURN_Scripting_DefineConstantFailed = 0x0a000004; +extern const PSRETURN PSRETURN_Scripting_RegisterFunctionFailed = 0x0a000005; +extern const PSRETURN PSRETURN_Scripting_SetupFailed = 0x0a000006; +extern const PSRETURN PSRETURN_Scripting_TypeDoesNotExist = 0x0a000007; +extern const PSRETURN PSRETURN_Serialize_InvalidCharInString = 0x0b000001; +extern const PSRETURN PSRETURN_Serialize_InvalidScriptValue = 0x0b000002; +extern const PSRETURN PSRETURN_Serialize_OutOfBounds = 0x0b000003; +extern const PSRETURN PSRETURN_Serialize_ScriptError = 0x0b000004; +extern const PSRETURN PSRETURN_System_RequiredExtensionsMissing = 0x0c000001; +extern const PSRETURN PSRETURN_System_SDLInitFailed = 0x0c000002; +extern const PSRETURN PSRETURN_System_VmodeFailed = 0x0c000003; +extern const PSRETURN PSRETURN_Xeromyces_XMLOpenFailed = 0x0d000001; +extern const PSRETURN PSRETURN_Xeromyces_XMLParseError = 0x0d000002; extern const PSRETURN MASK__PSRETURN_CVFSFile = 0xff000000; extern const PSRETURN CODE__PSRETURN_CVFSFile = 0x01000000; +extern const PSRETURN MASK__PSRETURN_Deserialize = 0xff000000; +extern const PSRETURN CODE__PSRETURN_Deserialize = 0x02000000; extern const PSRETURN MASK__PSRETURN_DllLoader = 0xff000000; -extern const PSRETURN CODE__PSRETURN_DllLoader = 0x02000000; +extern const PSRETURN CODE__PSRETURN_DllLoader = 0x03000000; extern const PSRETURN MASK__PSRETURN_Error = 0xff000000; -extern const PSRETURN CODE__PSRETURN_Error = 0x03000000; +extern const PSRETURN CODE__PSRETURN_Error = 0x04000000; extern const PSRETURN MASK__PSRETURN_File = 0xff000000; -extern const PSRETURN CODE__PSRETURN_File = 0x04000000; +extern const PSRETURN CODE__PSRETURN_File = 0x05000000; extern const PSRETURN MASK__PSRETURN_GUI = 0xff000000; -extern const PSRETURN CODE__PSRETURN_GUI = 0x05000000; +extern const PSRETURN CODE__PSRETURN_GUI = 0x06000000; extern const PSRETURN MASK__PSRETURN_Game = 0xff000000; -extern const PSRETURN CODE__PSRETURN_Game = 0x06000000; +extern const PSRETURN CODE__PSRETURN_Game = 0x07000000; extern const PSRETURN MASK__PSRETURN_I18n = 0xff000000; -extern const PSRETURN CODE__PSRETURN_I18n = 0x07000000; +extern const PSRETURN CODE__PSRETURN_I18n = 0x08000000; extern const PSRETURN MASK__PSRETURN_Renderer = 0xff000000; -extern const PSRETURN CODE__PSRETURN_Renderer = 0x08000000; -extern const PSRETURN MASK__PSRETURN_Scripting = 0xff000000; -extern const PSRETURN CODE__PSRETURN_Scripting = 0x09000000; -extern const PSRETURN MASK__PSRETURN_System = 0x0a000000; -extern const PSRETURN CODE__PSRETURN_System = 0x0a000000; -extern const PSRETURN MASK__PSRETURN_Xeromyces = 0x0b000000; -extern const PSRETURN CODE__PSRETURN_Xeromyces = 0x0b000000; +extern const PSRETURN CODE__PSRETURN_Renderer = 0x09000000; +extern const PSRETURN MASK__PSRETURN_Scripting = 0x0a000000; +extern const PSRETURN CODE__PSRETURN_Scripting = 0x0a000000; +extern const PSRETURN MASK__PSRETURN_Serialize = 0x0b000000; +extern const PSRETURN CODE__PSRETURN_Serialize = 0x0b000000; +extern const PSRETURN MASK__PSRETURN_System = 0x0c000000; +extern const PSRETURN CODE__PSRETURN_System = 0x0c000000; +extern const PSRETURN MASK__PSRETURN_Xeromyces = 0x0d000000; +extern const PSRETURN CODE__PSRETURN_Xeromyces = 0x0d000000; extern const PSRETURN MASK__PSRETURN_Game_World = 0xffff0000; -extern const PSRETURN CODE__PSRETURN_Game_World = 0x06040000; +extern const PSRETURN CODE__PSRETURN_Game_World = 0x07040000; extern const PSRETURN MASK__PSRETURN_I18n_Script = 0xffff0000; -extern const PSRETURN CODE__PSRETURN_I18n_Script = 0x07030000; -extern const PSRETURN MASK__PSRETURN_Scripting_DefineType = 0xffff0000; -extern const PSRETURN CODE__PSRETURN_Scripting_DefineType = 0x09010000; -extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile = 0xffff0000; -extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile = 0x09020000; +extern const PSRETURN CODE__PSRETURN_I18n_Script = 0x08030000; +extern const PSRETURN MASK__PSRETURN_Scripting_DefineType = 0x0aff0000; +extern const PSRETURN CODE__PSRETURN_Scripting_DefineType = 0x0a010000; +extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile = 0x0aff0000; +extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile = 0x0a020000; extern const PSRETURN MASK__PSRETURN_CVFSFile_AlreadyLoaded = 0xffffffff; extern const PSRETURN CODE__PSRETURN_CVFSFile_AlreadyLoaded = 0x01000001; extern const PSRETURN MASK__PSRETURN_CVFSFile_InvalidBufferAccess = 0xffffffff; extern const PSRETURN CODE__PSRETURN_CVFSFile_InvalidBufferAccess = 0x01000002; extern const PSRETURN MASK__PSRETURN_CVFSFile_LoadFailed = 0xffffffff; extern const PSRETURN CODE__PSRETURN_CVFSFile_LoadFailed = 0x01000003; +extern const PSRETURN MASK__PSRETURN_Deserialize_InvalidCharInString = 0xffffffff; +extern const PSRETURN CODE__PSRETURN_Deserialize_InvalidCharInString = 0x02000001; +extern const PSRETURN MASK__PSRETURN_Deserialize_OutOfBounds = 0xffffffff; +extern const PSRETURN CODE__PSRETURN_Deserialize_OutOfBounds = 0x02000002; +extern const PSRETURN MASK__PSRETURN_Deserialize_ReadFailed = 0xffffffff; +extern const PSRETURN CODE__PSRETURN_Deserialize_ReadFailed = 0x02000003; +extern const PSRETURN MASK__PSRETURN_Deserialize_ScriptError = 0xffffffff; +extern const PSRETURN CODE__PSRETURN_Deserialize_ScriptError = 0x02000004; extern const PSRETURN MASK__PSRETURN_DllLoader_DllNotLoaded = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_DllLoader_DllNotLoaded = 0x02000001; +extern const PSRETURN CODE__PSRETURN_DllLoader_DllNotLoaded = 0x03000001; extern const PSRETURN MASK__PSRETURN_DllLoader_SymbolNotFound = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_DllLoader_SymbolNotFound = 0x02000002; +extern const PSRETURN CODE__PSRETURN_DllLoader_SymbolNotFound = 0x03000002; extern const PSRETURN MASK__PSRETURN_Error_InvalidError = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Error_InvalidError = 0x03000001; +extern const PSRETURN CODE__PSRETURN_Error_InvalidError = 0x04000001; extern const PSRETURN MASK__PSRETURN_File_InvalidType = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_File_InvalidType = 0x04000001; +extern const PSRETURN CODE__PSRETURN_File_InvalidType = 0x05000001; extern const PSRETURN MASK__PSRETURN_File_InvalidVersion = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_File_InvalidVersion = 0x04000002; +extern const PSRETURN CODE__PSRETURN_File_InvalidVersion = 0x05000002; extern const PSRETURN MASK__PSRETURN_File_OpenFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_File_OpenFailed = 0x04000003; +extern const PSRETURN CODE__PSRETURN_File_OpenFailed = 0x05000003; extern const PSRETURN MASK__PSRETURN_File_ReadFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_File_ReadFailed = 0x04000004; +extern const PSRETURN CODE__PSRETURN_File_ReadFailed = 0x05000004; extern const PSRETURN MASK__PSRETURN_File_UnexpectedEOF = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_File_UnexpectedEOF = 0x04000005; +extern const PSRETURN CODE__PSRETURN_File_UnexpectedEOF = 0x05000005; extern const PSRETURN MASK__PSRETURN_File_WriteFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_File_WriteFailed = 0x04000006; +extern const PSRETURN CODE__PSRETURN_File_WriteFailed = 0x05000006; extern const PSRETURN MASK__PSRETURN_GUI_InvalidSetting = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_GUI_InvalidSetting = 0x05000001; +extern const PSRETURN CODE__PSRETURN_GUI_InvalidSetting = 0x06000001; extern const PSRETURN MASK__PSRETURN_GUI_JSOpenFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_GUI_JSOpenFailed = 0x05000002; +extern const PSRETURN CODE__PSRETURN_GUI_JSOpenFailed = 0x06000002; extern const PSRETURN MASK__PSRETURN_GUI_NameAmbiguity = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_GUI_NameAmbiguity = 0x05000003; +extern const PSRETURN CODE__PSRETURN_GUI_NameAmbiguity = 0x06000003; extern const PSRETURN MASK__PSRETURN_GUI_NullObjectProvided = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_GUI_NullObjectProvided = 0x05000004; +extern const PSRETURN CODE__PSRETURN_GUI_NullObjectProvided = 0x06000004; extern const PSRETURN MASK__PSRETURN_GUI_ObjectNeedsName = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_GUI_ObjectNeedsName = 0x05000005; +extern const PSRETURN CODE__PSRETURN_GUI_ObjectNeedsName = 0x06000005; extern const PSRETURN MASK__PSRETURN_GUI_OperationNeedsGUIObject = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_GUI_OperationNeedsGUIObject = 0x05000006; +extern const PSRETURN CODE__PSRETURN_GUI_OperationNeedsGUIObject = 0x06000006; extern const PSRETURN MASK__PSRETURN_GUI_UnableToParse = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_GUI_UnableToParse = 0x05000007; +extern const PSRETURN CODE__PSRETURN_GUI_UnableToParse = 0x06000007; extern const PSRETURN MASK__PSRETURN_Game_World_MapLoadFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Game_World_MapLoadFailed = 0x06040001; +extern const PSRETURN CODE__PSRETURN_Game_World_MapLoadFailed = 0x07040001; extern const PSRETURN MASK__PSRETURN_I18n_Script_SetupFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_I18n_Script_SetupFailed = 0x07030001; +extern const PSRETURN CODE__PSRETURN_I18n_Script_SetupFailed = 0x08030001; extern const PSRETURN MASK__PSRETURN_Renderer_VBOFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Renderer_VBOFailed = 0x08000001; +extern const PSRETURN CODE__PSRETURN_Renderer_VBOFailed = 0x09000001; extern const PSRETURN MASK__PSRETURN_Scripting_DefineType_AlreadyExists = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_AlreadyExists = 0x09010001; +extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_AlreadyExists = 0x0a010001; extern const PSRETURN MASK__PSRETURN_Scripting_DefineType_CreationFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_CreationFailed = 0x09010002; +extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_CreationFailed = 0x0a010002; extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile_EvalErrors = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_EvalErrors = 0x09020001; +extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_EvalErrors = 0x0a020001; extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile_OpenFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_OpenFailed = 0x09020002; +extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_OpenFailed = 0x0a020002; extern const PSRETURN MASK__PSRETURN_Scripting_CallFunctionFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Scripting_CallFunctionFailed = 0x09000001; +extern const PSRETURN CODE__PSRETURN_Scripting_CallFunctionFailed = 0x0a000001; extern const PSRETURN MASK__PSRETURN_Scripting_ConversionFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Scripting_ConversionFailed = 0x09000002; +extern const PSRETURN CODE__PSRETURN_Scripting_ConversionFailed = 0x0a000002; extern const PSRETURN MASK__PSRETURN_Scripting_CreateObjectFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Scripting_CreateObjectFailed = 0x09000003; +extern const PSRETURN CODE__PSRETURN_Scripting_CreateObjectFailed = 0x0a000003; extern const PSRETURN MASK__PSRETURN_Scripting_DefineConstantFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Scripting_DefineConstantFailed = 0x09000004; +extern const PSRETURN CODE__PSRETURN_Scripting_DefineConstantFailed = 0x0a000004; extern const PSRETURN MASK__PSRETURN_Scripting_RegisterFunctionFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Scripting_RegisterFunctionFailed = 0x09000005; +extern const PSRETURN CODE__PSRETURN_Scripting_RegisterFunctionFailed = 0x0a000005; extern const PSRETURN MASK__PSRETURN_Scripting_SetupFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Scripting_SetupFailed = 0x09000006; +extern const PSRETURN CODE__PSRETURN_Scripting_SetupFailed = 0x0a000006; extern const PSRETURN MASK__PSRETURN_Scripting_TypeDoesNotExist = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Scripting_TypeDoesNotExist = 0x09000007; +extern const PSRETURN CODE__PSRETURN_Scripting_TypeDoesNotExist = 0x0a000007; +extern const PSRETURN MASK__PSRETURN_Serialize_InvalidCharInString = 0xffffffff; +extern const PSRETURN CODE__PSRETURN_Serialize_InvalidCharInString = 0x0b000001; +extern const PSRETURN MASK__PSRETURN_Serialize_InvalidScriptValue = 0xffffffff; +extern const PSRETURN CODE__PSRETURN_Serialize_InvalidScriptValue = 0x0b000002; +extern const PSRETURN MASK__PSRETURN_Serialize_OutOfBounds = 0xffffffff; +extern const PSRETURN CODE__PSRETURN_Serialize_OutOfBounds = 0x0b000003; +extern const PSRETURN MASK__PSRETURN_Serialize_ScriptError = 0xffffffff; +extern const PSRETURN CODE__PSRETURN_Serialize_ScriptError = 0x0b000004; extern const PSRETURN MASK__PSRETURN_System_RequiredExtensionsMissing = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_System_RequiredExtensionsMissing = 0x0a000001; +extern const PSRETURN CODE__PSRETURN_System_RequiredExtensionsMissing = 0x0c000001; extern const PSRETURN MASK__PSRETURN_System_SDLInitFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_System_SDLInitFailed = 0x0a000002; +extern const PSRETURN CODE__PSRETURN_System_SDLInitFailed = 0x0c000002; extern const PSRETURN MASK__PSRETURN_System_VmodeFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_System_VmodeFailed = 0x0a000003; +extern const PSRETURN CODE__PSRETURN_System_VmodeFailed = 0x0c000003; extern const PSRETURN MASK__PSRETURN_Xeromyces_XMLOpenFailed = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLOpenFailed = 0x0b000001; +extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLOpenFailed = 0x0d000001; extern const PSRETURN MASK__PSRETURN_Xeromyces_XMLParseError = 0xffffffff; -extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLParseError = 0x0b000002; +extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLParseError = 0x0d000002; +PSERROR_CVFSFile::PSERROR_CVFSFile(const char* msg) : PSERROR(msg) { } +PSERROR_Deserialize::PSERROR_Deserialize(const char* msg) : PSERROR(msg) { } +PSERROR_DllLoader::PSERROR_DllLoader(const char* msg) : PSERROR(msg) { } +PSERROR_Error::PSERROR_Error(const char* msg) : PSERROR(msg) { } +PSERROR_File::PSERROR_File(const char* msg) : PSERROR(msg) { } +PSERROR_GUI::PSERROR_GUI(const char* msg) : PSERROR(msg) { } +PSERROR_Game::PSERROR_Game(const char* msg) : PSERROR(msg) { } +PSERROR_I18n::PSERROR_I18n(const char* msg) : PSERROR(msg) { } +PSERROR_Renderer::PSERROR_Renderer(const char* msg) : PSERROR(msg) { } +PSERROR_Scripting::PSERROR_Scripting(const char* msg) : PSERROR(msg) { } +PSERROR_Serialize::PSERROR_Serialize(const char* msg) : PSERROR(msg) { } +PSERROR_System::PSERROR_System(const char* msg) : PSERROR(msg) { } +PSERROR_Xeromyces::PSERROR_Xeromyces(const char* msg) : PSERROR(msg) { } +PSERROR_Game_World::PSERROR_Game_World(const char* msg) : PSERROR_Game(msg) { } +PSERROR_I18n_Script::PSERROR_I18n_Script(const char* msg) : PSERROR_I18n(msg) { } +PSERROR_Scripting_DefineType::PSERROR_Scripting_DefineType(const char* msg) : PSERROR_Scripting(msg) { } +PSERROR_Scripting_LoadFile::PSERROR_Scripting_LoadFile(const char* msg) : PSERROR_Scripting(msg) { } + +PSERROR_CVFSFile_AlreadyLoaded::PSERROR_CVFSFile_AlreadyLoaded() : PSERROR_CVFSFile(NULL) { } +PSERROR_CVFSFile_AlreadyLoaded::PSERROR_CVFSFile_AlreadyLoaded(const char* msg) : PSERROR_CVFSFile(msg) { } PSRETURN PSERROR_CVFSFile_AlreadyLoaded::getCode() const { return 0x01000001; } + +PSERROR_CVFSFile_InvalidBufferAccess::PSERROR_CVFSFile_InvalidBufferAccess() : PSERROR_CVFSFile(NULL) { } +PSERROR_CVFSFile_InvalidBufferAccess::PSERROR_CVFSFile_InvalidBufferAccess(const char* msg) : PSERROR_CVFSFile(msg) { } PSRETURN PSERROR_CVFSFile_InvalidBufferAccess::getCode() const { return 0x01000002; } + +PSERROR_CVFSFile_LoadFailed::PSERROR_CVFSFile_LoadFailed() : PSERROR_CVFSFile(NULL) { } +PSERROR_CVFSFile_LoadFailed::PSERROR_CVFSFile_LoadFailed(const char* msg) : PSERROR_CVFSFile(msg) { } PSRETURN PSERROR_CVFSFile_LoadFailed::getCode() const { return 0x01000003; } -PSRETURN PSERROR_DllLoader_DllNotLoaded::getCode() const { return 0x02000001; } -PSRETURN PSERROR_DllLoader_SymbolNotFound::getCode() const { return 0x02000002; } -PSRETURN PSERROR_Error_InvalidError::getCode() const { return 0x03000001; } -PSRETURN PSERROR_File_InvalidType::getCode() const { return 0x04000001; } -PSRETURN PSERROR_File_InvalidVersion::getCode() const { return 0x04000002; } -PSRETURN PSERROR_File_OpenFailed::getCode() const { return 0x04000003; } -PSRETURN PSERROR_File_ReadFailed::getCode() const { return 0x04000004; } -PSRETURN PSERROR_File_UnexpectedEOF::getCode() const { return 0x04000005; } -PSRETURN PSERROR_File_WriteFailed::getCode() const { return 0x04000006; } -PSRETURN PSERROR_GUI_InvalidSetting::getCode() const { return 0x05000001; } -PSRETURN PSERROR_GUI_JSOpenFailed::getCode() const { return 0x05000002; } -PSRETURN PSERROR_GUI_NameAmbiguity::getCode() const { return 0x05000003; } -PSRETURN PSERROR_GUI_NullObjectProvided::getCode() const { return 0x05000004; } -PSRETURN PSERROR_GUI_ObjectNeedsName::getCode() const { return 0x05000005; } -PSRETURN PSERROR_GUI_OperationNeedsGUIObject::getCode() const { return 0x05000006; } -PSRETURN PSERROR_GUI_UnableToParse::getCode() const { return 0x05000007; } -PSRETURN PSERROR_Game_World_MapLoadFailed::getCode() const { return 0x06040001; } -PSRETURN PSERROR_I18n_Script_SetupFailed::getCode() const { return 0x07030001; } -PSRETURN PSERROR_Renderer_VBOFailed::getCode() const { return 0x08000001; } -PSRETURN PSERROR_Scripting_DefineType_AlreadyExists::getCode() const { return 0x09010001; } -PSRETURN PSERROR_Scripting_DefineType_CreationFailed::getCode() const { return 0x09010002; } -PSRETURN PSERROR_Scripting_LoadFile_EvalErrors::getCode() const { return 0x09020001; } -PSRETURN PSERROR_Scripting_LoadFile_OpenFailed::getCode() const { return 0x09020002; } -PSRETURN PSERROR_Scripting_CallFunctionFailed::getCode() const { return 0x09000001; } -PSRETURN PSERROR_Scripting_ConversionFailed::getCode() const { return 0x09000002; } -PSRETURN PSERROR_Scripting_CreateObjectFailed::getCode() const { return 0x09000003; } -PSRETURN PSERROR_Scripting_DefineConstantFailed::getCode() const { return 0x09000004; } -PSRETURN PSERROR_Scripting_RegisterFunctionFailed::getCode() const { return 0x09000005; } -PSRETURN PSERROR_Scripting_SetupFailed::getCode() const { return 0x09000006; } -PSRETURN PSERROR_Scripting_TypeDoesNotExist::getCode() const { return 0x09000007; } -PSRETURN PSERROR_System_RequiredExtensionsMissing::getCode() const { return 0x0a000001; } -PSRETURN PSERROR_System_SDLInitFailed::getCode() const { return 0x0a000002; } -PSRETURN PSERROR_System_VmodeFailed::getCode() const { return 0x0a000003; } -PSRETURN PSERROR_Xeromyces_XMLOpenFailed::getCode() const { return 0x0b000001; } -PSRETURN PSERROR_Xeromyces_XMLParseError::getCode() const { return 0x0b000002; } -const char* PSERROR::what() const throw () -{ - return GetErrorString(getCode()); -} +PSERROR_Deserialize_InvalidCharInString::PSERROR_Deserialize_InvalidCharInString() : PSERROR_Deserialize(NULL) { } +PSERROR_Deserialize_InvalidCharInString::PSERROR_Deserialize_InvalidCharInString(const char* msg) : PSERROR_Deserialize(msg) { } +PSRETURN PSERROR_Deserialize_InvalidCharInString::getCode() const { return 0x02000001; } + +PSERROR_Deserialize_OutOfBounds::PSERROR_Deserialize_OutOfBounds() : PSERROR_Deserialize(NULL) { } +PSERROR_Deserialize_OutOfBounds::PSERROR_Deserialize_OutOfBounds(const char* msg) : PSERROR_Deserialize(msg) { } +PSRETURN PSERROR_Deserialize_OutOfBounds::getCode() const { return 0x02000002; } + +PSERROR_Deserialize_ReadFailed::PSERROR_Deserialize_ReadFailed() : PSERROR_Deserialize(NULL) { } +PSERROR_Deserialize_ReadFailed::PSERROR_Deserialize_ReadFailed(const char* msg) : PSERROR_Deserialize(msg) { } +PSRETURN PSERROR_Deserialize_ReadFailed::getCode() const { return 0x02000003; } + +PSERROR_Deserialize_ScriptError::PSERROR_Deserialize_ScriptError() : PSERROR_Deserialize(NULL) { } +PSERROR_Deserialize_ScriptError::PSERROR_Deserialize_ScriptError(const char* msg) : PSERROR_Deserialize(msg) { } +PSRETURN PSERROR_Deserialize_ScriptError::getCode() const { return 0x02000004; } + +PSERROR_DllLoader_DllNotLoaded::PSERROR_DllLoader_DllNotLoaded() : PSERROR_DllLoader(NULL) { } +PSERROR_DllLoader_DllNotLoaded::PSERROR_DllLoader_DllNotLoaded(const char* msg) : PSERROR_DllLoader(msg) { } +PSRETURN PSERROR_DllLoader_DllNotLoaded::getCode() const { return 0x03000001; } + +PSERROR_DllLoader_SymbolNotFound::PSERROR_DllLoader_SymbolNotFound() : PSERROR_DllLoader(NULL) { } +PSERROR_DllLoader_SymbolNotFound::PSERROR_DllLoader_SymbolNotFound(const char* msg) : PSERROR_DllLoader(msg) { } +PSRETURN PSERROR_DllLoader_SymbolNotFound::getCode() const { return 0x03000002; } + +PSERROR_Error_InvalidError::PSERROR_Error_InvalidError() : PSERROR_Error(NULL) { } +PSERROR_Error_InvalidError::PSERROR_Error_InvalidError(const char* msg) : PSERROR_Error(msg) { } +PSRETURN PSERROR_Error_InvalidError::getCode() const { return 0x04000001; } + +PSERROR_File_InvalidType::PSERROR_File_InvalidType() : PSERROR_File(NULL) { } +PSERROR_File_InvalidType::PSERROR_File_InvalidType(const char* msg) : PSERROR_File(msg) { } +PSRETURN PSERROR_File_InvalidType::getCode() const { return 0x05000001; } + +PSERROR_File_InvalidVersion::PSERROR_File_InvalidVersion() : PSERROR_File(NULL) { } +PSERROR_File_InvalidVersion::PSERROR_File_InvalidVersion(const char* msg) : PSERROR_File(msg) { } +PSRETURN PSERROR_File_InvalidVersion::getCode() const { return 0x05000002; } + +PSERROR_File_OpenFailed::PSERROR_File_OpenFailed() : PSERROR_File(NULL) { } +PSERROR_File_OpenFailed::PSERROR_File_OpenFailed(const char* msg) : PSERROR_File(msg) { } +PSRETURN PSERROR_File_OpenFailed::getCode() const { return 0x05000003; } + +PSERROR_File_ReadFailed::PSERROR_File_ReadFailed() : PSERROR_File(NULL) { } +PSERROR_File_ReadFailed::PSERROR_File_ReadFailed(const char* msg) : PSERROR_File(msg) { } +PSRETURN PSERROR_File_ReadFailed::getCode() const { return 0x05000004; } + +PSERROR_File_UnexpectedEOF::PSERROR_File_UnexpectedEOF() : PSERROR_File(NULL) { } +PSERROR_File_UnexpectedEOF::PSERROR_File_UnexpectedEOF(const char* msg) : PSERROR_File(msg) { } +PSRETURN PSERROR_File_UnexpectedEOF::getCode() const { return 0x05000005; } + +PSERROR_File_WriteFailed::PSERROR_File_WriteFailed() : PSERROR_File(NULL) { } +PSERROR_File_WriteFailed::PSERROR_File_WriteFailed(const char* msg) : PSERROR_File(msg) { } +PSRETURN PSERROR_File_WriteFailed::getCode() const { return 0x05000006; } + +PSERROR_GUI_InvalidSetting::PSERROR_GUI_InvalidSetting() : PSERROR_GUI(NULL) { } +PSERROR_GUI_InvalidSetting::PSERROR_GUI_InvalidSetting(const char* msg) : PSERROR_GUI(msg) { } +PSRETURN PSERROR_GUI_InvalidSetting::getCode() const { return 0x06000001; } + +PSERROR_GUI_JSOpenFailed::PSERROR_GUI_JSOpenFailed() : PSERROR_GUI(NULL) { } +PSERROR_GUI_JSOpenFailed::PSERROR_GUI_JSOpenFailed(const char* msg) : PSERROR_GUI(msg) { } +PSRETURN PSERROR_GUI_JSOpenFailed::getCode() const { return 0x06000002; } + +PSERROR_GUI_NameAmbiguity::PSERROR_GUI_NameAmbiguity() : PSERROR_GUI(NULL) { } +PSERROR_GUI_NameAmbiguity::PSERROR_GUI_NameAmbiguity(const char* msg) : PSERROR_GUI(msg) { } +PSRETURN PSERROR_GUI_NameAmbiguity::getCode() const { return 0x06000003; } + +PSERROR_GUI_NullObjectProvided::PSERROR_GUI_NullObjectProvided() : PSERROR_GUI(NULL) { } +PSERROR_GUI_NullObjectProvided::PSERROR_GUI_NullObjectProvided(const char* msg) : PSERROR_GUI(msg) { } +PSRETURN PSERROR_GUI_NullObjectProvided::getCode() const { return 0x06000004; } + +PSERROR_GUI_ObjectNeedsName::PSERROR_GUI_ObjectNeedsName() : PSERROR_GUI(NULL) { } +PSERROR_GUI_ObjectNeedsName::PSERROR_GUI_ObjectNeedsName(const char* msg) : PSERROR_GUI(msg) { } +PSRETURN PSERROR_GUI_ObjectNeedsName::getCode() const { return 0x06000005; } + +PSERROR_GUI_OperationNeedsGUIObject::PSERROR_GUI_OperationNeedsGUIObject() : PSERROR_GUI(NULL) { } +PSERROR_GUI_OperationNeedsGUIObject::PSERROR_GUI_OperationNeedsGUIObject(const char* msg) : PSERROR_GUI(msg) { } +PSRETURN PSERROR_GUI_OperationNeedsGUIObject::getCode() const { return 0x06000006; } + +PSERROR_GUI_UnableToParse::PSERROR_GUI_UnableToParse() : PSERROR_GUI(NULL) { } +PSERROR_GUI_UnableToParse::PSERROR_GUI_UnableToParse(const char* msg) : PSERROR_GUI(msg) { } +PSRETURN PSERROR_GUI_UnableToParse::getCode() const { return 0x06000007; } + +PSERROR_Game_World_MapLoadFailed::PSERROR_Game_World_MapLoadFailed() : PSERROR_Game_World(NULL) { } +PSERROR_Game_World_MapLoadFailed::PSERROR_Game_World_MapLoadFailed(const char* msg) : PSERROR_Game_World(msg) { } +PSRETURN PSERROR_Game_World_MapLoadFailed::getCode() const { return 0x07040001; } -const char* GetErrorString(const PSERROR& err) +PSERROR_I18n_Script_SetupFailed::PSERROR_I18n_Script_SetupFailed() : PSERROR_I18n_Script(NULL) { } +PSERROR_I18n_Script_SetupFailed::PSERROR_I18n_Script_SetupFailed(const char* msg) : PSERROR_I18n_Script(msg) { } +PSRETURN PSERROR_I18n_Script_SetupFailed::getCode() const { return 0x08030001; } + +PSERROR_Renderer_VBOFailed::PSERROR_Renderer_VBOFailed() : PSERROR_Renderer(NULL) { } +PSERROR_Renderer_VBOFailed::PSERROR_Renderer_VBOFailed(const char* msg) : PSERROR_Renderer(msg) { } +PSRETURN PSERROR_Renderer_VBOFailed::getCode() const { return 0x09000001; } + +PSERROR_Scripting_DefineType_AlreadyExists::PSERROR_Scripting_DefineType_AlreadyExists() : PSERROR_Scripting_DefineType(NULL) { } +PSERROR_Scripting_DefineType_AlreadyExists::PSERROR_Scripting_DefineType_AlreadyExists(const char* msg) : PSERROR_Scripting_DefineType(msg) { } +PSRETURN PSERROR_Scripting_DefineType_AlreadyExists::getCode() const { return 0x0a010001; } + +PSERROR_Scripting_DefineType_CreationFailed::PSERROR_Scripting_DefineType_CreationFailed() : PSERROR_Scripting_DefineType(NULL) { } +PSERROR_Scripting_DefineType_CreationFailed::PSERROR_Scripting_DefineType_CreationFailed(const char* msg) : PSERROR_Scripting_DefineType(msg) { } +PSRETURN PSERROR_Scripting_DefineType_CreationFailed::getCode() const { return 0x0a010002; } + +PSERROR_Scripting_LoadFile_EvalErrors::PSERROR_Scripting_LoadFile_EvalErrors() : PSERROR_Scripting_LoadFile(NULL) { } +PSERROR_Scripting_LoadFile_EvalErrors::PSERROR_Scripting_LoadFile_EvalErrors(const char* msg) : PSERROR_Scripting_LoadFile(msg) { } +PSRETURN PSERROR_Scripting_LoadFile_EvalErrors::getCode() const { return 0x0a020001; } + +PSERROR_Scripting_LoadFile_OpenFailed::PSERROR_Scripting_LoadFile_OpenFailed() : PSERROR_Scripting_LoadFile(NULL) { } +PSERROR_Scripting_LoadFile_OpenFailed::PSERROR_Scripting_LoadFile_OpenFailed(const char* msg) : PSERROR_Scripting_LoadFile(msg) { } +PSRETURN PSERROR_Scripting_LoadFile_OpenFailed::getCode() const { return 0x0a020002; } + +PSERROR_Scripting_CallFunctionFailed::PSERROR_Scripting_CallFunctionFailed() : PSERROR_Scripting(NULL) { } +PSERROR_Scripting_CallFunctionFailed::PSERROR_Scripting_CallFunctionFailed(const char* msg) : PSERROR_Scripting(msg) { } +PSRETURN PSERROR_Scripting_CallFunctionFailed::getCode() const { return 0x0a000001; } + +PSERROR_Scripting_ConversionFailed::PSERROR_Scripting_ConversionFailed() : PSERROR_Scripting(NULL) { } +PSERROR_Scripting_ConversionFailed::PSERROR_Scripting_ConversionFailed(const char* msg) : PSERROR_Scripting(msg) { } +PSRETURN PSERROR_Scripting_ConversionFailed::getCode() const { return 0x0a000002; } + +PSERROR_Scripting_CreateObjectFailed::PSERROR_Scripting_CreateObjectFailed() : PSERROR_Scripting(NULL) { } +PSERROR_Scripting_CreateObjectFailed::PSERROR_Scripting_CreateObjectFailed(const char* msg) : PSERROR_Scripting(msg) { } +PSRETURN PSERROR_Scripting_CreateObjectFailed::getCode() const { return 0x0a000003; } + +PSERROR_Scripting_DefineConstantFailed::PSERROR_Scripting_DefineConstantFailed() : PSERROR_Scripting(NULL) { } +PSERROR_Scripting_DefineConstantFailed::PSERROR_Scripting_DefineConstantFailed(const char* msg) : PSERROR_Scripting(msg) { } +PSRETURN PSERROR_Scripting_DefineConstantFailed::getCode() const { return 0x0a000004; } + +PSERROR_Scripting_RegisterFunctionFailed::PSERROR_Scripting_RegisterFunctionFailed() : PSERROR_Scripting(NULL) { } +PSERROR_Scripting_RegisterFunctionFailed::PSERROR_Scripting_RegisterFunctionFailed(const char* msg) : PSERROR_Scripting(msg) { } +PSRETURN PSERROR_Scripting_RegisterFunctionFailed::getCode() const { return 0x0a000005; } + +PSERROR_Scripting_SetupFailed::PSERROR_Scripting_SetupFailed() : PSERROR_Scripting(NULL) { } +PSERROR_Scripting_SetupFailed::PSERROR_Scripting_SetupFailed(const char* msg) : PSERROR_Scripting(msg) { } +PSRETURN PSERROR_Scripting_SetupFailed::getCode() const { return 0x0a000006; } + +PSERROR_Scripting_TypeDoesNotExist::PSERROR_Scripting_TypeDoesNotExist() : PSERROR_Scripting(NULL) { } +PSERROR_Scripting_TypeDoesNotExist::PSERROR_Scripting_TypeDoesNotExist(const char* msg) : PSERROR_Scripting(msg) { } +PSRETURN PSERROR_Scripting_TypeDoesNotExist::getCode() const { return 0x0a000007; } + +PSERROR_Serialize_InvalidCharInString::PSERROR_Serialize_InvalidCharInString() : PSERROR_Serialize(NULL) { } +PSERROR_Serialize_InvalidCharInString::PSERROR_Serialize_InvalidCharInString(const char* msg) : PSERROR_Serialize(msg) { } +PSRETURN PSERROR_Serialize_InvalidCharInString::getCode() const { return 0x0b000001; } + +PSERROR_Serialize_InvalidScriptValue::PSERROR_Serialize_InvalidScriptValue() : PSERROR_Serialize(NULL) { } +PSERROR_Serialize_InvalidScriptValue::PSERROR_Serialize_InvalidScriptValue(const char* msg) : PSERROR_Serialize(msg) { } +PSRETURN PSERROR_Serialize_InvalidScriptValue::getCode() const { return 0x0b000002; } + +PSERROR_Serialize_OutOfBounds::PSERROR_Serialize_OutOfBounds() : PSERROR_Serialize(NULL) { } +PSERROR_Serialize_OutOfBounds::PSERROR_Serialize_OutOfBounds(const char* msg) : PSERROR_Serialize(msg) { } +PSRETURN PSERROR_Serialize_OutOfBounds::getCode() const { return 0x0b000003; } + +PSERROR_Serialize_ScriptError::PSERROR_Serialize_ScriptError() : PSERROR_Serialize(NULL) { } +PSERROR_Serialize_ScriptError::PSERROR_Serialize_ScriptError(const char* msg) : PSERROR_Serialize(msg) { } +PSRETURN PSERROR_Serialize_ScriptError::getCode() const { return 0x0b000004; } + +PSERROR_System_RequiredExtensionsMissing::PSERROR_System_RequiredExtensionsMissing() : PSERROR_System(NULL) { } +PSERROR_System_RequiredExtensionsMissing::PSERROR_System_RequiredExtensionsMissing(const char* msg) : PSERROR_System(msg) { } +PSRETURN PSERROR_System_RequiredExtensionsMissing::getCode() const { return 0x0c000001; } + +PSERROR_System_SDLInitFailed::PSERROR_System_SDLInitFailed() : PSERROR_System(NULL) { } +PSERROR_System_SDLInitFailed::PSERROR_System_SDLInitFailed(const char* msg) : PSERROR_System(msg) { } +PSRETURN PSERROR_System_SDLInitFailed::getCode() const { return 0x0c000002; } + +PSERROR_System_VmodeFailed::PSERROR_System_VmodeFailed() : PSERROR_System(NULL) { } +PSERROR_System_VmodeFailed::PSERROR_System_VmodeFailed(const char* msg) : PSERROR_System(msg) { } +PSRETURN PSERROR_System_VmodeFailed::getCode() const { return 0x0c000003; } + +PSERROR_Xeromyces_XMLOpenFailed::PSERROR_Xeromyces_XMLOpenFailed() : PSERROR_Xeromyces(NULL) { } +PSERROR_Xeromyces_XMLOpenFailed::PSERROR_Xeromyces_XMLOpenFailed(const char* msg) : PSERROR_Xeromyces(msg) { } +PSRETURN PSERROR_Xeromyces_XMLOpenFailed::getCode() const { return 0x0d000001; } + +PSERROR_Xeromyces_XMLParseError::PSERROR_Xeromyces_XMLParseError() : PSERROR_Xeromyces(NULL) { } +PSERROR_Xeromyces_XMLParseError::PSERROR_Xeromyces_XMLParseError(const char* msg) : PSERROR_Xeromyces(msg) { } +PSRETURN PSERROR_Xeromyces_XMLParseError::getCode() const { return 0x0d000002; } + + +PSERROR::PSERROR(const char* msg) : m_msg(msg) { } + +const char* PSERROR::what() const throw () { - return GetErrorString(err.getCode()); + return m_msg ? m_msg : GetErrorString(getCode()); } const char* GetErrorString(PSRETURN code) { switch (code) { case 0x01000001: return "CVFSFile_AlreadyLoaded"; case 0x01000002: return "CVFSFile_InvalidBufferAccess"; case 0x01000003: return "CVFSFile_LoadFailed"; - case 0x02000001: return "DllLoader_DllNotLoaded"; - case 0x02000002: return "DllLoader_SymbolNotFound"; - case 0x03000001: return "Error_InvalidError"; - case 0x04000001: return "File_InvalidType"; - case 0x04000002: return "File_InvalidVersion"; - case 0x04000003: return "File_OpenFailed"; - case 0x04000004: return "File_ReadFailed"; - case 0x04000005: return "File_UnexpectedEOF"; - case 0x04000006: return "File_WriteFailed"; - case 0x05000001: return "GUI_InvalidSetting"; - case 0x05000002: return "GUI_JSOpenFailed"; - case 0x05000003: return "GUI_NameAmbiguity"; - case 0x05000004: return "GUI_NullObjectProvided"; - case 0x05000005: return "GUI_ObjectNeedsName"; - case 0x05000006: return "GUI_OperationNeedsGUIObject"; - case 0x05000007: return "GUI_UnableToParse"; - case 0x06040001: return "Game_World_MapLoadFailed"; - case 0x07030001: return "I18n_Script_SetupFailed"; - case 0x08000001: return "Renderer_VBOFailed"; - case 0x09010001: return "Scripting_DefineType_AlreadyExists"; - case 0x09010002: return "Scripting_DefineType_CreationFailed"; - case 0x09020001: return "Scripting_LoadFile_EvalErrors"; - case 0x09020002: return "Scripting_LoadFile_OpenFailed"; - case 0x09000001: return "Scripting_CallFunctionFailed"; - case 0x09000002: return "Scripting_ConversionFailed"; - case 0x09000003: return "Scripting_CreateObjectFailed"; - case 0x09000004: return "Scripting_DefineConstantFailed"; - case 0x09000005: return "Scripting_RegisterFunctionFailed"; - case 0x09000006: return "Scripting_SetupFailed"; - case 0x09000007: return "Scripting_TypeDoesNotExist"; - case 0x0a000001: return "System_RequiredExtensionsMissing"; - case 0x0a000002: return "System_SDLInitFailed"; - case 0x0a000003: return "System_VmodeFailed"; - case 0x0b000001: return "Xeromyces_XMLOpenFailed"; - case 0x0b000002: return "Xeromyces_XMLParseError"; + case 0x02000001: return "Deserialize_InvalidCharInString"; + case 0x02000002: return "Deserialize_OutOfBounds"; + case 0x02000003: return "Deserialize_ReadFailed"; + case 0x02000004: return "Deserialize_ScriptError"; + case 0x03000001: return "DllLoader_DllNotLoaded"; + case 0x03000002: return "DllLoader_SymbolNotFound"; + case 0x04000001: return "Error_InvalidError"; + case 0x05000001: return "File_InvalidType"; + case 0x05000002: return "File_InvalidVersion"; + case 0x05000003: return "File_OpenFailed"; + case 0x05000004: return "File_ReadFailed"; + case 0x05000005: return "File_UnexpectedEOF"; + case 0x05000006: return "File_WriteFailed"; + case 0x06000001: return "GUI_InvalidSetting"; + case 0x06000002: return "GUI_JSOpenFailed"; + case 0x06000003: return "GUI_NameAmbiguity"; + case 0x06000004: return "GUI_NullObjectProvided"; + case 0x06000005: return "GUI_ObjectNeedsName"; + case 0x06000006: return "GUI_OperationNeedsGUIObject"; + case 0x06000007: return "GUI_UnableToParse"; + case 0x07040001: return "Game_World_MapLoadFailed"; + case 0x08030001: return "I18n_Script_SetupFailed"; + case 0x09000001: return "Renderer_VBOFailed"; + case 0x0a010001: return "Scripting_DefineType_AlreadyExists"; + case 0x0a010002: return "Scripting_DefineType_CreationFailed"; + case 0x0a020001: return "Scripting_LoadFile_EvalErrors"; + case 0x0a020002: return "Scripting_LoadFile_OpenFailed"; + case 0x0a000001: return "Scripting_CallFunctionFailed"; + case 0x0a000002: return "Scripting_ConversionFailed"; + case 0x0a000003: return "Scripting_CreateObjectFailed"; + case 0x0a000004: return "Scripting_DefineConstantFailed"; + case 0x0a000005: return "Scripting_RegisterFunctionFailed"; + case 0x0a000006: return "Scripting_SetupFailed"; + case 0x0a000007: return "Scripting_TypeDoesNotExist"; + case 0x0b000001: return "Serialize_InvalidCharInString"; + case 0x0b000002: return "Serialize_InvalidScriptValue"; + case 0x0b000003: return "Serialize_OutOfBounds"; + case 0x0b000004: return "Serialize_ScriptError"; + case 0x0c000001: return "System_RequiredExtensionsMissing"; + case 0x0c000002: return "System_SDLInitFailed"; + case 0x0c000003: return "System_VmodeFailed"; + case 0x0d000001: return "Xeromyces_XMLOpenFailed"; + case 0x0d000002: return "Xeromyces_XMLParseError"; default: return "Unrecognised error"; } } void ThrowError(PSRETURN code) { switch (code) // Use 'break' in case someone tries to continue from the exception { case 0x01000001: throw PSERROR_CVFSFile_AlreadyLoaded(); break; case 0x01000002: throw PSERROR_CVFSFile_InvalidBufferAccess(); break; case 0x01000003: throw PSERROR_CVFSFile_LoadFailed(); break; - case 0x02000001: throw PSERROR_DllLoader_DllNotLoaded(); break; - case 0x02000002: throw PSERROR_DllLoader_SymbolNotFound(); break; - case 0x03000001: throw PSERROR_Error_InvalidError(); break; - case 0x04000001: throw PSERROR_File_InvalidType(); break; - case 0x04000002: throw PSERROR_File_InvalidVersion(); break; - case 0x04000003: throw PSERROR_File_OpenFailed(); break; - case 0x04000004: throw PSERROR_File_ReadFailed(); break; - case 0x04000005: throw PSERROR_File_UnexpectedEOF(); break; - case 0x04000006: throw PSERROR_File_WriteFailed(); break; - case 0x05000001: throw PSERROR_GUI_InvalidSetting(); break; - case 0x05000002: throw PSERROR_GUI_JSOpenFailed(); break; - case 0x05000003: throw PSERROR_GUI_NameAmbiguity(); break; - case 0x05000004: throw PSERROR_GUI_NullObjectProvided(); break; - case 0x05000005: throw PSERROR_GUI_ObjectNeedsName(); break; - case 0x05000006: throw PSERROR_GUI_OperationNeedsGUIObject(); break; - case 0x05000007: throw PSERROR_GUI_UnableToParse(); break; - case 0x06040001: throw PSERROR_Game_World_MapLoadFailed(); break; - case 0x07030001: throw PSERROR_I18n_Script_SetupFailed(); break; - case 0x08000001: throw PSERROR_Renderer_VBOFailed(); break; - case 0x09010001: throw PSERROR_Scripting_DefineType_AlreadyExists(); break; - case 0x09010002: throw PSERROR_Scripting_DefineType_CreationFailed(); break; - case 0x09020001: throw PSERROR_Scripting_LoadFile_EvalErrors(); break; - case 0x09020002: throw PSERROR_Scripting_LoadFile_OpenFailed(); break; - case 0x09000001: throw PSERROR_Scripting_CallFunctionFailed(); break; - case 0x09000002: throw PSERROR_Scripting_ConversionFailed(); break; - case 0x09000003: throw PSERROR_Scripting_CreateObjectFailed(); break; - case 0x09000004: throw PSERROR_Scripting_DefineConstantFailed(); break; - case 0x09000005: throw PSERROR_Scripting_RegisterFunctionFailed(); break; - case 0x09000006: throw PSERROR_Scripting_SetupFailed(); break; - case 0x09000007: throw PSERROR_Scripting_TypeDoesNotExist(); break; - case 0x0a000001: throw PSERROR_System_RequiredExtensionsMissing(); break; - case 0x0a000002: throw PSERROR_System_SDLInitFailed(); break; - case 0x0a000003: throw PSERROR_System_VmodeFailed(); break; - case 0x0b000001: throw PSERROR_Xeromyces_XMLOpenFailed(); break; - case 0x0b000002: throw PSERROR_Xeromyces_XMLParseError(); break; + case 0x02000001: throw PSERROR_Deserialize_InvalidCharInString(); break; + case 0x02000002: throw PSERROR_Deserialize_OutOfBounds(); break; + case 0x02000003: throw PSERROR_Deserialize_ReadFailed(); break; + case 0x02000004: throw PSERROR_Deserialize_ScriptError(); break; + case 0x03000001: throw PSERROR_DllLoader_DllNotLoaded(); break; + case 0x03000002: throw PSERROR_DllLoader_SymbolNotFound(); break; + case 0x04000001: throw PSERROR_Error_InvalidError(); break; + case 0x05000001: throw PSERROR_File_InvalidType(); break; + case 0x05000002: throw PSERROR_File_InvalidVersion(); break; + case 0x05000003: throw PSERROR_File_OpenFailed(); break; + case 0x05000004: throw PSERROR_File_ReadFailed(); break; + case 0x05000005: throw PSERROR_File_UnexpectedEOF(); break; + case 0x05000006: throw PSERROR_File_WriteFailed(); break; + case 0x06000001: throw PSERROR_GUI_InvalidSetting(); break; + case 0x06000002: throw PSERROR_GUI_JSOpenFailed(); break; + case 0x06000003: throw PSERROR_GUI_NameAmbiguity(); break; + case 0x06000004: throw PSERROR_GUI_NullObjectProvided(); break; + case 0x06000005: throw PSERROR_GUI_ObjectNeedsName(); break; + case 0x06000006: throw PSERROR_GUI_OperationNeedsGUIObject(); break; + case 0x06000007: throw PSERROR_GUI_UnableToParse(); break; + case 0x07040001: throw PSERROR_Game_World_MapLoadFailed(); break; + case 0x08030001: throw PSERROR_I18n_Script_SetupFailed(); break; + case 0x09000001: throw PSERROR_Renderer_VBOFailed(); break; + case 0x0a010001: throw PSERROR_Scripting_DefineType_AlreadyExists(); break; + case 0x0a010002: throw PSERROR_Scripting_DefineType_CreationFailed(); break; + case 0x0a020001: throw PSERROR_Scripting_LoadFile_EvalErrors(); break; + case 0x0a020002: throw PSERROR_Scripting_LoadFile_OpenFailed(); break; + case 0x0a000001: throw PSERROR_Scripting_CallFunctionFailed(); break; + case 0x0a000002: throw PSERROR_Scripting_ConversionFailed(); break; + case 0x0a000003: throw PSERROR_Scripting_CreateObjectFailed(); break; + case 0x0a000004: throw PSERROR_Scripting_DefineConstantFailed(); break; + case 0x0a000005: throw PSERROR_Scripting_RegisterFunctionFailed(); break; + case 0x0a000006: throw PSERROR_Scripting_SetupFailed(); break; + case 0x0a000007: throw PSERROR_Scripting_TypeDoesNotExist(); break; + case 0x0b000001: throw PSERROR_Serialize_InvalidCharInString(); break; + case 0x0b000002: throw PSERROR_Serialize_InvalidScriptValue(); break; + case 0x0b000003: throw PSERROR_Serialize_OutOfBounds(); break; + case 0x0b000004: throw PSERROR_Serialize_ScriptError(); break; + case 0x0c000001: throw PSERROR_System_RequiredExtensionsMissing(); break; + case 0x0c000002: throw PSERROR_System_SDLInitFailed(); break; + case 0x0c000003: throw PSERROR_System_VmodeFailed(); break; + case 0x0d000001: throw PSERROR_Xeromyces_XMLOpenFailed(); break; + case 0x0d000002: throw PSERROR_Xeromyces_XMLParseError(); break; default: throw PSERROR_Error_InvalidError(); // Hmm... } } Index: ps/trunk/source/ps/Game.h =================================================================== --- ps/trunk/source/ps/Game.h (revision 7258) +++ ps/trunk/source/ps/Game.h (revision 7259) @@ -1,214 +1,226 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /** * File : Game.h * Project : engine * Description : Contains the CGame Class which is a representation of the game itself. * **/ #ifndef INCLUDED_GAME #define INCLUDED_GAME #include "ps/Errors.h" #include class CWorld; class CSimulation; +class CSimulation2; class CGameView; class CSimulation; class CPlayer; class CGameAttributes; /** * Default player limit (not counting the Gaia player) * This may be overridden by system.cfg ("max_players") **/ #define PS_MAX_PLAYERS 8 /** * The container that holds the rules, resources and attributes of the game. * The CGame object is responsible for creating a game that is defined by * a set of attributes provided. The CGame object is also responsible for * maintaining the relations between CPlayer and CWorld, CSimulation and CWorld. **/ class CGame { NONCOPYABLE(CGame); /** * pointer to the CWorld object representing the game world. **/ CWorld *m_World; /** * pointer to the CSimulation object operating on the game world. **/ CSimulation *m_Simulation; /** + * pointer to the CSimulation2 object operating on the game world. + **/ + CSimulation2 *m_Simulation2; + /** * pointer to the CGameView object representing the view into the game world. **/ CGameView *m_GameView; /** * pointer to the local CPlayer object operating on the game world. **/ CPlayer *m_pLocalPlayer; /** * STL vectors of pointers to all CPlayer objects(including gaia) operating on the game world. **/ std::vector m_Players; /** * number of players operating on the game world(not including gaia). **/ size_t m_NumPlayers; /** * the game has been initialized and ready for use if true. **/ bool m_GameStarted; /** * scale multiplier for simulation rate. **/ float m_SimRate; /** * enumerated values for game status. **/ enum EOG { EOG_NEUTRAL, /// Game is in progress EOG_DRAW, /// Game is over as a Draw by means of agreement of civilizations EOG_SPECIAL_DRAW, /// Game is over by players dying at the same time...? EOG_LOSE, /// Game is over, local player loses EOG_WIN /// Game is over, local player wins } GameStatus; public: CGame(); ~CGame(); /** * the game is paused and no updates will be performed if true. **/ bool m_Paused; /* Initialize all local state and members for playing a game described by the attribute class, and start the game. Return: 0 on OK - a PSRETURN code otherwise */ PSRETURN StartGame(CGameAttributes *pGameAttributes); PSRETURN ReallyStartGame(); /* Perform all per-frame updates */ bool Update(double deltaTime, bool doInterpolate = true); void UpdateGameStatus(); void EndGame(); /** * Get pointer to the local player object. * * @return CPlayer * the value of m_pLocalPlayer. **/ inline CPlayer *GetLocalPlayer() { return m_pLocalPlayer; } /** * Change the pointer to the local player object. * * @param CPlayer * pLocalPlayer pointer to a valid player object. **/ inline void SetLocalPlayer(CPlayer *pLocalPlayer) { m_pLocalPlayer=pLocalPlayer; } // PT: No longer inline, because it does too much error checking. When // everything stops trying to access players before they're loaded, feel // free to put the inline version back. CPlayer *GetPlayer(size_t idx); /** * Get a reference to the m_Players vector. * * @return std::vector * reference to m_Players. **/ inline std::vector* GetPlayers() { return( &m_Players ); } /** * Get m_NumPlayers. * * @return the number of players (not including gaia) **/ inline size_t GetNumPlayers() const { return m_NumPlayers; } /** * Get m_GameStarted. * * @return bool the value of m_GameStarted. **/ inline bool IsGameStarted() const { return m_GameStarted; } /** * Get the pointer to the game world object. * * @return CWorld * the value of m_World. **/ inline CWorld *GetWorld() { return m_World; } /** * Get the pointer to the game view object. * * @return CGameView * the value of m_GameView. **/ inline CGameView *GetView() { return m_GameView; } /** * Get the pointer to the simulation object. * * @return CSimulation * the value of m_Simulation. **/ inline CSimulation *GetSimulation() { return m_Simulation; } + /** + * Get the pointer to the simulation2 object. + * + * @return CSimulation2 * the value of m_Simulation2. + **/ + inline CSimulation2 *GetSimulation2() + { return m_Simulation2; } /** * Set the simulation scale multiplier. * * @param float simRate value to set m_SimRate to. * Because m_SimRate is also used to * scale TimeSinceLastFrame it must be * clamped to 0.0f. **/ inline void SetSimRate(float simRate) { m_SimRate = std::max(simRate, 0.0f); } /** * Get the simulation scale multiplier. * * @return float value of m_SimRate. **/ inline float GetSimRate() const { return m_SimRate; } private: PSRETURN RegisterInit(CGameAttributes* pAttribs); }; extern CGame *g_Game; #endif Index: ps/trunk/source/ps/Game.cpp =================================================================== --- ps/trunk/source/ps/Game.cpp (revision 7258) +++ ps/trunk/source/ps/Game.cpp (revision 7259) @@ -1,343 +1,361 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /** * File : Game.cpp * Project : engine * Description : Contains the CGame Class implementation. * **/ #include "precompiled.h" #include "Game.h" #include "graphics/GameView.h" #include "graphics/UnitManager.h" #include "lib/timer.h" #include "network/NetClient.h" #include "ps/CConsole.h" #include "ps/CLogger.h" #include "ps/CStr.h" #include "ps/GameAttributes.h" #include "ps/Loader.h" #include "ps/Profile.h" #include "ps/World.h" #include "simulation/Entity.h" #include "simulation/EntityManager.h" #include "simulation/Simulation.h" +#include "simulation2/Simulation2.h" #include "gui/GUIManager.h" class CNetServer; extern CNetServer *g_NetServer; extern bool g_GameRestarted; /** * Globally accessible pointer to the CGame object. **/ CGame *g_Game=NULL; // Disable "warning C4355: 'this' : used in base member initializer list". // "The base-class constructors and class member constructors are called before // this constructor. In effect, you've passed a pointer to an unconstructed // object to another constructor. If those other constructors access any // members or call member functions on this, the result will be undefined." // In this case, the pointers are simply stored for later use, so there // should be no problem. #if MSC_VERSION # pragma warning (disable: 4355) #endif /** * Constructor * **/ CGame::CGame(): m_World(new CWorld(this)), m_Simulation(new CSimulation(this)), + m_Simulation2(g_UseSimulation2 ? new CSimulation2(&m_World->GetUnitManager(), m_World->GetTerrain()) : NULL), m_GameView(new CGameView(this)), m_pLocalPlayer(NULL), m_GameStarted(false), m_Paused(false), m_SimRate(1.0f) { // Need to set the CObjectManager references after various objects have // been initialised, so do it here rather than via the initialisers above. m_World->GetUnitManager().SetObjectManager(m_GameView->GetObjectManager()); + + if (g_UseSimulation2) + { + m_Simulation2->LoadScripts(L"simulation/helpers/"); + m_Simulation2->LoadScripts(L"simulation/components/"); + m_Simulation2->ResetState(); + } } #if MSC_VERSION # pragma warning (default: 4355) #endif /** * Destructor * **/ CGame::~CGame() { // Again, the in-game call tree is going to be different to the main menu one. g_Profiler.StructuralReset(); delete m_GameView; + delete m_Simulation2; delete m_Simulation; delete m_World; } /** * Initializes the game with the set of attributes provided. * Makes calls to initialize the game view, world, and simulation objects. * Calls are made to facilitate progress reporting of the initialization. * * @param CGameAttributes * pAttribs pointer to the game attribute values * @return PSRETURN 0 **/ PSRETURN CGame::RegisterInit(CGameAttributes* pAttribs) { LDR_BeginRegistering(); // RC, 040804 - GameView needs to be initialized before World, otherwise GameView initialization // overwrites anything stored in the map file that gets loaded by CWorld::Initialize with default // values. At the minute, it's just lighting settings, but could be extended to store camera position. // Storing lighting settings in the game view seems a little odd, but it's no big deal; maybe move it at // some point to be stored in the world object? m_GameView->RegisterInit(pAttribs); m_World->RegisterInit(pAttribs); m_Simulation->RegisterInit(pAttribs); LDR_EndRegistering(); return 0; } /** * Game initialization has been completed. Set game started flag and start the session. * * @return PSRETURN 0 **/ PSRETURN CGame::ReallyStartGame() { // Call the reallyStartGame GUI function, but only if it exists jsval fval, rval; JSBool ok = JS_GetProperty(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), "reallyStartGame", &fval); debug_assert(ok); if (ok && !JSVAL_IS_VOID(fval)) { ok = JS_CallFunctionValue(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), fval, 0, NULL, &rval); } debug_printf(L"GAME STARTED, ALL INIT COMPLETE\n"); m_GameStarted=true; // The call tree we've built for pregame probably isn't useful in-game. g_Profiler.StructuralReset(); // Mark terrain as modified so the minimap can repaint (is there a cleaner way of handling this?) g_GameRestarted = true; g_GUI->SendEventToAll("sessionstart"); return 0; } /** * Prepare to start the game. * Set up the players list then call RegisterInit that initializes the game and is used to report progress. * * @param CGameAttributes * pGameAttributes game attributes for initialization. * @return PSRETURN 0 if successful, * error information if not. **/ PSRETURN CGame::StartGame(CGameAttributes *pAttribs) { try { // JW: this loop is taken from ScEd and fixes lack of player color. // TODO: determine proper number of players. // SB: Only do this for non-network games if (!g_NetClient && !g_NetServer) { for (int i=1; i<8; ++i) pAttribs->GetSlot(i)->AssignLocal(); } pAttribs->FinalizeSlots(); m_NumPlayers=pAttribs->GetSlotCount(); // Player 0 = Gaia - allocate one extra m_Players.resize(m_NumPlayers + 1); for (size_t i=0;i <= m_NumPlayers;i++) m_Players[i]=pAttribs->GetPlayer(i); if (g_NetClient) { // TODO m_pLocalPlayer = g_NetClient->GetLocalPlayer(); debug_assert(m_pLocalPlayer && "Darn it! We weren't assigned to a slot!"); } else { m_pLocalPlayer=m_Players[1]; } RegisterInit(pAttribs); } catch (PSERROR_Game& e) { return e.getCode(); } return 0; } // TODO: doInterpolate is optional because Atlas interpolates explicitly, // so that it has more control over the update rate. The game might want to // do the same, and then doInterpolate should be redundant and removed. /** * Periodic heartbeat that controls the process. * Simulation update is called and game status update is called. * * @param double deltaTime elapsed time since last beat in seconds. * @param bool doInterpolate perform interpolation if true. * @return bool false if it can't keep up with the desired simulation rate * indicating that you might want to render less frequently. **/ bool CGame::Update(double deltaTime, bool doInterpolate) { if (m_Paused) return true; deltaTime *= m_SimRate; bool ok = m_Simulation->Update(deltaTime); + if (g_UseSimulation2) + m_Simulation2->Update(deltaTime); if (doInterpolate) + { m_Simulation->Interpolate(deltaTime); + if (g_UseSimulation2) + m_Simulation2->Interpolate(deltaTime); + } + g_GUI->SendEventToAll("SimulationUpdate"); + // TODO Detect game over and bring up the summary screen or something // ^ Quick game over hack is implemented, no summary screen however /*if (m_World->GetEntityManager().GetDeath()) { UpdateGameStatus(); if (GameStatus != 0) EndGame(); } //reset death event flag m_World->GetEntityManager().SetDeath(false);*/ return ok; } /** * Test player statistics and update game status as required. * **/ /* void CGame::UpdateGameStatus() { bool EOG_lose = true; bool EOG_win = true; CPlayer *local = GetLocalPlayer(); for (int i=0; iGetEntityManager().GetHandle(i); if ( !handle ) continue; CPlayer *tmpPlayer = handle->m_entity->GetPlayer(); //Are we still alive? if ( local == tmpPlayer && handle->m_entity->m_extant ) { EOG_lose = false; if (EOG_win == false) break; } //Are they still alive? else if ( handle->m_entity->m_extant ) { EOG_win = false; if (EOG_lose == false) break; } } if (EOG_lose && EOG_win) GameStatus = EOG_SPECIAL_DRAW; else if (EOG_win) GameStatus = EOG_WIN; else if (EOG_lose) GameStatus = EOG_LOSE; else GameStatus = EOG_NEUTRAL; }*/ /** * End of game console message creation. * **/ void CGame::EndGame() { g_Console->InsertMessage( L"It's the end of the game as we know it!"); switch (GameStatus) { case EOG_DRAW: g_Console->InsertMessage( L"A diplomatic draw ain't so bad, eh?"); break; case EOG_SPECIAL_DRAW: g_Console->InsertMessage( L"Amazingly, you managed to draw from dieing at the same time as your opponent...you have my respect."); break; case EOG_LOSE: g_Console->InsertMessage( L"My condolences on your loss."); break; case EOG_WIN: g_Console->InsertMessage( L"Thou art victorious!"); break; default: break; } } /** * Get the player object from the players list at the provided index. * * @param idx sequential position in the list. * @return CPlayer * pointer to player requested. **/ CPlayer *CGame::GetPlayer(size_t idx) { if (idx > m_NumPlayers) { // debug_warn(L"Invalid player ID"); // LOG(CLogger::Error, "", "Invalid player ID %d (outside 0..%d)", idx, m_NumPlayers); return m_Players[0]; } // Be a bit more paranoid - maybe m_Players hasn't been set large enough else if (idx >= m_Players.size()) { debug_warn(L"Invalid player ID"); LOG(CLogger::Error, L"", L"Invalid player ID %lu (not <=%lu - internal error?)", (unsigned long)idx, (unsigned long)m_Players.size()); if (m_Players.size() != 0) return m_Players[0]; else return NULL; // the caller will probably crash because of this, // but at least we've reported the error } else return m_Players[idx]; } Index: ps/trunk/source/ps/Errors.h =================================================================== --- ps/trunk/source/ps/Errors.h (revision 7258) +++ ps/trunk/source/ps/Errors.h (revision 7259) @@ -1,56 +1,110 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #ifndef INCLUDED_ERRORS #define INCLUDED_ERRORS +/* + +The overly-complex error system works as follows: + +A source file (typically a .h) can declare errors as follows: + + ERROR_GROUP(ModuleName); + ERROR_TYPE(ModuleName, FrobnificationFailed); + ERROR_SUBGROUP(ModuleName, ComponentName); + ERROR_TYPE(ModuleName_ComponentName, FileNotFound); + +etc, to build up a hierarchy of error types. + +Then you have to run the /build/errorlist/errorlist.pl script, to regenerate +the Errors.cpp file. + +Then you can use the declared errors as an error code: + + PSRETURN foo() { return PSRETURN_ModuleName_FrobnificationFailed; } + + if (ret != PSRETURN_OK) + ... // something failed + + if (ret) + ... // something failed + + if (ret == PSRETURN_ModuleName_FrobnificationFailed) + ... // particular error + + if (ERROR_IS(ret, PSRETURN_ModuleName)) + ... // matches any type PSRETURN_ModuleName_* (and PSRETURN_ModuleName_*_* etc) + +And you can use it as an exception: + + void foo() { throw PSERROR_ModuleName_FrobnificationFailed(); } + + void bar() { throw PSERROR_ModuleName_FrobnificationFailed("More informative message"); } + + try { + foo(); + } catch (PSERROR_ModuleName_FrobnificationFailed e) { + // catches that particular error type + } catch (PSERROR_ModuleName e) { + // catches anything in the hierarchy + } catch (PSERROR e) { + std::cout << e.what(); + } + +plus a few extra things for converting between error codes and exceptions. + +*/ + #include typedef u32 PSRETURN; class PSERROR : public std::exception { public: + PSERROR(const char* msg); virtual const char* what() const throw (); virtual PSRETURN getCode() const = 0; // for functions that catch exceptions then return error codes +private: + const char* m_msg; }; -#define ERROR_GROUP(a) class PSERROR_##a : public PSERROR {}; \ +#define ERROR_GROUP(a) class PSERROR_##a : public PSERROR { protected: PSERROR_##a(const char* msg); }; \ extern const PSRETURN MASK__PSRETURN_##a; \ extern const PSRETURN CODE__PSRETURN_##a -#define ERROR_SUBGROUP(a,b) class PSERROR_##a##_##b : public PSERROR_##a {}; \ +#define ERROR_SUBGROUP(a,b) class PSERROR_##a##_##b : public PSERROR_##a { protected: PSERROR_##a##_##b(const char* msg); }; \ extern const PSRETURN MASK__PSRETURN_##a##_##b; \ extern const PSRETURN CODE__PSRETURN_##a##_##b -#define ERROR_TYPE(a,b) class PSERROR_##a##_##b : public PSERROR_##a { public: PSRETURN getCode() const; }; \ +#define ERROR_TYPE(a,b) class PSERROR_##a##_##b : public PSERROR_##a { public: PSERROR_##a##_##b(); PSERROR_##a##_##b(const char* msg); PSRETURN getCode() const; }; \ extern const PSRETURN MASK__PSRETURN_##a##_##b; \ extern const PSRETURN CODE__PSRETURN_##a##_##b; \ extern const PSRETURN PSRETURN_##a##_##b #define ERROR_IS(a, b) ( ((a) & MASK__PSRETURN_##b) == CODE__PSRETURN_##b ) const PSRETURN PSRETURN_OK = 0; const PSRETURN MASK__PSRETURN_OK = 0xFFFFFFFF; const PSRETURN CODE__PSRETURN_OK = 0; const char* GetErrorString(PSRETURN code); -const char* GetErrorString(const PSERROR& err); void ThrowError(PSRETURN code); #endif Index: ps/trunk/source/gui/scripting/ScriptFunctions.h =================================================================== --- ps/trunk/source/gui/scripting/ScriptFunctions.h (nonexistent) +++ ps/trunk/source/gui/scripting/ScriptFunctions.h (revision 7259) @@ -0,0 +1,25 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#ifndef INCLUDED_GUI_SCRIPTFUNCTIONS +#define INCLUDED_GUI_SCRIPTFUNCTIONS + +class ScriptInterface; + +void GuiScriptingInit(ScriptInterface& scriptInterface); + +#endif // INCLUDED_GUI_SCRIPTFUNCTIONS Property changes on: ps/trunk/source/gui/scripting/ScriptFunctions.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/gui/scripting/GuiScriptConversions.cpp =================================================================== --- ps/trunk/source/gui/scripting/GuiScriptConversions.cpp (nonexistent) +++ ps/trunk/source/gui/scripting/GuiScriptConversions.cpp (revision 7259) @@ -0,0 +1,132 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#include "precompiled.h" + +#include "scriptinterface/ScriptInterface.h" + +#include "lib/external_libraries/sdl.h" +#include "ps/Hotkey.h" + +#include "js/jsapi.h" + +#define SET(obj, name, value) STMT(jsval v_ = ToJSVal(cx, (value)); JS_SetProperty(cx, (obj), (name), &v_)) + // ignore JS_SetProperty return value, because errors should be impossible + // and we can't do anything useful in the case of errors anyway + +template<> jsval ScriptInterface::ToJSVal(JSContext* cx, SDL_Event_ const& val) +{ + const char* typeName; + + switch (val.ev.type) + { + case SDL_ACTIVEEVENT: typeName = "activeevent"; break; + case SDL_KEYDOWN: typeName = "keydown"; break; + case SDL_KEYUP: typeName = "keyup"; break; + case SDL_MOUSEMOTION: typeName = "mousemotion"; break; + case SDL_MOUSEBUTTONDOWN: typeName = "mousebuttondown"; break; + case SDL_MOUSEBUTTONUP: typeName = "mousebuttonup"; break; + case SDL_HOTKEYDOWN: typeName = "hotkeydown"; break; + case SDL_HOTKEYUP: typeName = "hotkeyup"; break; + case SDL_GUIHOTKEYPRESS: typeName = "guihotkeypress"; break; + default: typeName = "(unknown)"; break; + } + + ScriptInterface::LocalRootScope scope(cx); + if (! scope.OK()) + return JSVAL_VOID; + + JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL); + if (! obj) + return JSVAL_VOID; + + SET(obj, "type", typeName); + + switch (val.ev.type) + { + case SDL_ACTIVEEVENT: + { + SET(obj, "gain", (int)val.ev.active.gain); + SET(obj, "state", (int)val.ev.active.state); + break; + } + case SDL_KEYDOWN: + case SDL_KEYUP: + { + // SET(obj, "which", (int)val.ev.key.which); // (not in wsdl.h) + // SET(obj, "state", (int)val.ev.key.state); // (not in wsdl.h) + + JSObject* keysym = JS_NewObject(cx, NULL, NULL, NULL); + if (! keysym) + return JSVAL_VOID; + jsval keysymVal = OBJECT_TO_JSVAL(keysym); + JS_SetProperty(cx, obj, "keysym", &keysymVal); + + // SET(keysym, "scancode", (int)val.ev.key.keysym.scancode); // (not in wsdl.h) + SET(keysym, "sym", (int)val.ev.key.keysym.sym); + // SET(keysym, "mod", (int)val.ev.key.keysym.mod); // (not in wsdl.h) + if (val.ev.key.keysym.unicode) + { + std::wstring unicode(1, (wchar_t)val.ev.key.keysym.unicode); + SET(keysym, "unicode", unicode); + } + else + { + SET(keysym, "unicode", CScriptVal(JSVAL_VOID)); + } + // TODO: scripts have no idea what all the key/mod enum values are; + // we should probably expose them as constants if we expect scripts to use them + + break; + } + case SDL_MOUSEMOTION: + { + // SET(obj, "which", (int)val.ev.motion.which); // (not in wsdl.h) + // SET(obj, "state", (int)val.ev.motion.state); // (not in wsdl.h) + SET(obj, "x", (int)val.ev.motion.x); + SET(obj, "y", (int)val.ev.motion.y); + // SET(obj, "xrel", (int)val.ev.motion.xrel); // (not in wsdl.h) + // SET(obj, "yrel", (int)val.ev.motion.yrel); // (not in wsdl.h) + break; + } + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + { + // SET(obj, "which", (int)val.ev.button.which); // (not in wsdl.h) + SET(obj, "button", (int)val.ev.button.button); + SET(obj, "state", (int)val.ev.button.state); + SET(obj, "x", (int)val.ev.button.x); + SET(obj, "y", (int)val.ev.button.y); + break; + } + case SDL_HOTKEYDOWN: + case SDL_HOTKEYUP: + { + CStr name = HotkeyGetName(val.ev.user.code); + SET(obj, "hotkey", name.c_str()); + break; + } + case SDL_GUIHOTKEYPRESS: + { + CStr* name = static_cast(val.ev.user.data1); + SET(obj, "object", name->c_str()); + break; + } + } + + return OBJECT_TO_JSVAL(obj); +} Property changes on: ps/trunk/source/gui/scripting/GuiScriptConversions.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/gui/scripting/ScriptFunctions.cpp =================================================================== --- ps/trunk/source/gui/scripting/ScriptFunctions.cpp (nonexistent) +++ ps/trunk/source/gui/scripting/ScriptFunctions.cpp (revision 7259) @@ -0,0 +1,221 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#include "precompiled.h" + +#include "scriptinterface/ScriptInterface.h" + +#include "graphics/Camera.h" +#include "graphics/GameView.h" +#include "gui/GUIManager.h" +#include "ps/CLogger.h" +#include "ps/Game.h" +#include "ps/Overlay.h" +#include "ps/Player.h" +#include "simulation2/Simulation2.h" +#include "simulation2/components/ICmpGuiInterface.h" +#include "simulation2/components/ICmpCommandQueue.h" +#include "simulation2/helpers/Selection.h" + +/* + * This file defines a set of functions that are available to GUI scripts, to allow + * interaction with the rest of the engine. + * Functions are exposed to scripts within the global object 'Engine', so + * scripts should call "Engine.FunctionName(...)" etc. + */ + +namespace { + +CScriptVal GetActiveGui(void* UNUSED(cbdata)) +{ + return OBJECT_TO_JSVAL(g_GUI->GetScriptObject()); +} + +void PushGuiPage(void* UNUSED(cbdata), std::wstring name, CScriptVal initData) +{ + g_GUI->PushPage(name, initData); +} + +void SwitchGuiPage(void* UNUSED(cbdata), std::wstring name, CScriptVal initData) +{ + g_GUI->SwitchPage(name, initData); +} + +void PopGuiPage(void* UNUSED(cbdata)) +{ + g_GUI->PopPage(); +} + +bool IsNewSimulation(void* UNUSED(cbdata)) +{ + return g_UseSimulation2; +} + +// TODO: this should probably be moved into ScriptInterface in case anyone else wants to use it +static jsval CloneValueBetweenContexts(JSContext* cxFrom, JSContext* cxTo, jsval val) +{ + if (JSVAL_IS_INT(val) || JSVAL_IS_BOOLEAN(val) || JSVAL_IS_NULL(val) || JSVAL_IS_VOID(val)) + return val; + + if (JSVAL_IS_DOUBLE(val)) + { + jsval rval; + if (JS_NewNumberValue(cxTo, *JSVAL_TO_DOUBLE(val), &rval)) + return rval; + else + return JSVAL_VOID; + } + + if (JSVAL_IS_STRING(val)) + { + JSString* str = JS_NewUCStringCopyN(cxTo, JS_GetStringChars(JSVAL_TO_STRING(val)), JS_GetStringLength(JSVAL_TO_STRING(val))); + if (str == NULL) + return JSVAL_VOID; + return STRING_TO_JSVAL(str); + } + + if (JSVAL_IS_OBJECT(val)) + { + jsval source; + if (!JS_CallFunctionName(cxFrom, JSVAL_TO_OBJECT(val), "toSource", 0, NULL, &source)) + return JSVAL_VOID; + if (!JSVAL_IS_STRING(source)) + return JSVAL_VOID; + JS_AddRoot(cxFrom, &source); + jsval rval; + JSBool ok = JS_EvaluateUCScript(cxTo, JS_GetGlobalObject(cxTo), + JS_GetStringChars(JSVAL_TO_STRING(source)), JS_GetStringLength(JSVAL_TO_STRING(source)), + "(CloneValueBetweenContexts)", 0, &rval); + JS_RemoveRoot(cxFrom, &source); + return ok ? rval : JSVAL_VOID; + } + + LOGERROR(L"CloneValueBetweenContexts: value is of unexpected type"); + return JSVAL_VOID; +} + +CScriptVal GetSimulationState(void* cbdata) +{ + CGUIManager* guiManager = static_cast (cbdata); + + if (!g_UseSimulation2 || !g_Game) + return JSVAL_VOID; + CSimulation2* sim = g_Game->GetSimulation2(); + debug_assert(sim); + CmpPtr gui(*sim, SYSTEM_ENTITY); + if (gui.null()) + return JSVAL_VOID; + + int player = -1; + if (g_Game && g_Game->GetLocalPlayer()) + player = g_Game->GetLocalPlayer()->GetPlayerID(); + + return CloneValueBetweenContexts(sim->GetScriptInterface().GetContext(), guiManager->GetScriptInterface().GetContext(), gui->GetSimulationState(player).get()); +} + +CScriptVal GetEntityState(void* cbdata, entity_id_t ent) +{ + CGUIManager* guiManager = static_cast (cbdata); + + if (!g_UseSimulation2 || !g_Game) + return JSVAL_VOID; + CSimulation2* sim = g_Game->GetSimulation2(); + debug_assert(sim); + CmpPtr gui(*sim, SYSTEM_ENTITY); + if (gui.null()) + return JSVAL_VOID; + + int player = -1; + if (g_Game && g_Game->GetLocalPlayer()) + player = g_Game->GetLocalPlayer()->GetPlayerID(); + + return CloneValueBetweenContexts(sim->GetScriptInterface().GetContext(), guiManager->GetScriptInterface().GetContext(), gui->GetEntityState(player, ent).get()); +} + +void SetEntitySelectionHighlight(void* UNUSED(cbdata), entity_id_t ent, CColor color) +{ + if (!g_UseSimulation2 || !g_Game) + return; + CSimulation2* sim = g_Game->GetSimulation2(); + debug_assert(sim); + CmpPtr gui(*sim, SYSTEM_ENTITY); + if (gui.null()) + return; + + // TODO: stop duplicating all this prolog code + + gui->SetSelectionHighlight(ent, color); +} + +void PostNetworkCommand(void* cbdata, CScriptVal cmd) +{ + CGUIManager* guiManager = static_cast (cbdata); + + if (!g_UseSimulation2 || !g_Game) + return; + CSimulation2* sim = g_Game->GetSimulation2(); + debug_assert(sim); + CmpPtr queue(*sim, SYSTEM_ENTITY); + if (queue.null()) + return; + + int player = -1; + if (g_Game && g_Game->GetLocalPlayer()) + player = g_Game->GetLocalPlayer()->GetPlayerID(); + + jsval cmd2 = CloneValueBetweenContexts(guiManager->GetScriptInterface().GetContext(), sim->GetScriptInterface().GetContext(), cmd.get()); + + queue->PushClientCommand(player, cmd2); + // TODO: This shouldn't call Push, it should send the message to the network layer + // (which should propagate it across the network and eventually call Push on the + // appropriate turn) +} + +std::vector PickEntitiesAtPoint(void* UNUSED(cbdata), int x, int y) +{ + return EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y); +} + +CFixedVector3D GetTerrainAtPoint(void* UNUSED(cbdata), int x, int y) +{ + CVector3D pos = g_Game->GetView()->GetCamera()->GetWorldCoordinates(x, y, false); + return CFixedVector3D(CFixed_23_8::FromFloat(pos.X), CFixed_23_8::FromFloat(pos.Y), CFixed_23_8::FromFloat(pos.Z)); +} + +} // namespace + +void GuiScriptingInit(ScriptInterface& scriptInterface) +{ + // GUI manager functions: + scriptInterface.RegisterFunction("GetActiveGui"); + scriptInterface.RegisterFunction("PushGuiPage"); + scriptInterface.RegisterFunction("SwitchGuiPage"); + scriptInterface.RegisterFunction("PopGuiPage"); + + // Simulation<->GUI interface functions: + scriptInterface.RegisterFunction("IsNewSimulation"); + scriptInterface.RegisterFunction("GetSimulationState"); + scriptInterface.RegisterFunction("GetEntityState"); + scriptInterface.RegisterFunction("SetEntitySelectionHighlight"); + + scriptInterface.RegisterFunction("PostNetworkCommand"); + + // Entity picking + scriptInterface.RegisterFunction, int, int, &PickEntitiesAtPoint>("PickEntitiesAtPoint"); + scriptInterface.RegisterFunction("GetTerrainAtPoint"); + +} Property changes on: ps/trunk/source/gui/scripting/ScriptFunctions.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/gui/GUIManager.cpp =================================================================== --- ps/trunk/source/gui/GUIManager.cpp (revision 7258) +++ ps/trunk/source/gui/GUIManager.cpp (revision 7259) @@ -1,206 +1,257 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + #include "precompiled.h" #include "GUIManager.h" #include "CGUI.h" #include "lib/timer.h" #include "ps/CLogger.h" #include "ps/Profile.h" #include "ps/XML/Xeromyces.h" +#include "scriptinterface/ScriptInterface.h" CGUIManager* g_GUI = NULL; // General TODOs: // // A lot of the CGUI data could (and should) be shared between // multiple pages, instead of treating them as completely independent, to save // memory and loading time. // // Hotkeys are not unregistered when a page is unloaded. // called from main loop when (input) events are received. // event is passed to other handlers if false is returned. // trampoline: we don't want to make the HandleEvent implementation static InReaction gui_handler(const SDL_Event_* ev) { PROFILE("GUI event handler"); return g_GUI->HandleEvent(ev); } CGUIManager::SGUIPage::SGUIPage() { JS_AddNamedRoot(g_ScriptingHost.GetContext(), &initData, "SGUIPage initData"); } CGUIManager::SGUIPage::SGUIPage(const SGUIPage& page) { *this = page; JS_AddNamedRoot(g_ScriptingHost.GetContext(), &initData, "SGUIPage initData copy"); } CGUIManager::SGUIPage::~SGUIPage() { JS_RemoveRoot(g_ScriptingHost.GetContext(), &initData); } -CGUIManager::CGUIManager() +CGUIManager::CGUIManager(ScriptInterface& scriptInterface) : + m_ScriptInterface(scriptInterface) { + debug_assert(ScriptInterface::GetCallbackData(scriptInterface.GetContext()) == NULL); + scriptInterface.SetCallbackData(this); } CGUIManager::~CGUIManager() { } -void CGUIManager::SwitchPage(const CStrW& pageName, jsval initData) +void CGUIManager::SwitchPage(const CStrW& pageName, CScriptVal initData) { m_PageStack.clear(); PushPage(pageName, initData); } -void CGUIManager::PushPage(const CStrW& pageName, jsval initData) +void CGUIManager::PushPage(const CStrW& pageName, CScriptVal initData) { m_PageStack.push_back(SGUIPage()); m_PageStack.back().name = pageName; m_PageStack.back().initData = initData; LoadPage(m_PageStack.back()); } void CGUIManager::PopPage() { if (m_PageStack.size() < 2) { debug_warn(L"Tried to pop GUI page when there's < 2 in the stack"); return; } m_PageStack.pop_back(); } void CGUIManager::LoadPage(SGUIPage& page) { page.inputs.clear(); page.gui.reset(new CGUI()); page.gui->Initialize(); VfsPath path = VfsPath(L"gui")/page.name.c_str(); page.inputs.insert(path); CXeromyces xero; if (xero.Load(path) != PSRETURN_OK) // Fail silently (Xeromyces reported the error) return; int elmt_page = xero.GetElementID("page"); int elmt_include = xero.GetElementID("include"); XMBElement root = xero.GetRoot(); if (root.GetNodeName() != elmt_page) { LOGERROR(L"GUI page '%ls' must have root element ", page.name.c_str()); return; } XERO_ITER_EL(root, node) { if (node.GetNodeName() != elmt_include) { LOGERROR(L"GUI page '%ls' must only have elements inside ", page.name.c_str()); continue; } CStrW name (node.GetText()); TIMER(name.c_str()); VfsPath path (VfsPath(L"gui")/name.c_str()); page.gui->LoadXmlFile(path, page.inputs); } page.gui->SendEventToAll("load"); // Call the init() function - jsval rval; - if (! JS_CallFunctionName(g_ScriptingHost.GetContext(), page.gui->GetScriptObject(), "init", 1, &page.initData, &rval)) + if (!m_ScriptInterface.CallFunctionVoid(OBJECT_TO_JSVAL(page.gui->GetScriptObject()), "init", page.initData)) { LOGERROR(L"GUI page '%ls': Failed to call init() function", page.name.c_str()); } } LibError CGUIManager::ReloadChangedFiles(const VfsPath& path) { for (PageStackType::iterator it = m_PageStack.begin(); it != m_PageStack.end(); ++it) { if (it->inputs.count(path)) { LOGMESSAGE(L"GUI file '%ls' changed - reloading page '%ls'", path.string().c_str(), it->name.c_str()); LoadPage(*it); } } return INFO::OK; } + +InReaction CGUIManager::HandleEvent(const SDL_Event_* ev) +{ + // We want scripts to have access to the raw input events, so they can do complex + // processing when necessary (e.g. for unit selection and camera movement). + // Sometimes they'll want to be layered behind the GUI widgets (e.g. to detect mousedowns on the + // visible game area), sometimes they'll want to intercepts events before the GUI (e.g. + // to capture all mouse events until a mouseup after dragging). + // So we call two separate handler functions: + + bool handled; + { + PROFILE("handleInputBeforeGui"); + if (m_ScriptInterface.CallFunction(OBJECT_TO_JSVAL(top()->GetScriptObject()), "handleInputBeforeGui", *ev, handled)) + if (handled) + return IN_HANDLED; + } + + { + PROFILE("handle event in native GUI"); + InReaction r = top()->HandleEvent(ev); + if (r != IN_PASS) + return r; + } + + { + PROFILE("handleInputAfterGui"); + if (m_ScriptInterface.CallFunction(OBJECT_TO_JSVAL(top()->GetScriptObject()), "handleInputAfterGui", *ev, handled)) + if (handled) + return IN_HANDLED; + } + + return IN_PASS; +} + + bool CGUIManager::GetPreDefinedColor(const CStr& name, CColor& output) { return top()->GetPreDefinedColor(name, output); } bool CGUIManager::IconExists(const CStr& str) const { return top()->IconExists(str); } SGUIIcon CGUIManager::GetIcon(const CStr& str) const { return top()->GetIcon(str); } IGUIObject* CGUIManager::FindObjectByName(const CStr& name) const { return top()->FindObjectByName(name); } void CGUIManager::SendEventToAll(const CStr& eventName) { top()->SendEventToAll(eventName); } void CGUIManager::TickObjects() { for (PageStackType::iterator it = m_PageStack.begin(); it != m_PageStack.end(); ++it) it->gui->TickObjects(); } void CGUIManager::Draw() { for (PageStackType::iterator it = m_PageStack.begin(); it != m_PageStack.end(); ++it) it->gui->Draw(); } void CGUIManager::UpdateResolution() { for (PageStackType::iterator it = m_PageStack.begin(); it != m_PageStack.end(); ++it) it->gui->UpdateResolution(); } JSObject* CGUIManager::GetScriptObject() { return top()->GetScriptObject(); } -InReaction CGUIManager::HandleEvent(const SDL_Event_* ev) -{ - return top()->HandleEvent(ev); -} - // This returns a shared_ptr to make sure the CGUI doesn't get deallocated // while we're in the middle of calling a function on it (e.g. if a GUI script // calls SwitchPage) shared_ptr CGUIManager::top() const { debug_assert(m_PageStack.size()); return m_PageStack.back().gui; } Index: ps/trunk/source/gui/CGUI.h =================================================================== --- ps/trunk/source/gui/CGUI.h (revision 7258) +++ ps/trunk/source/gui/CGUI.h (revision 7259) @@ -1,637 +1,636 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ /* CGUI --Overview-- This is the top class of the whole GUI, all objects and settings are stored within this class. --More info-- Check GUI.h */ #ifndef INCLUDED_CGUI #define INCLUDED_CGUI //-------------------------------------------------------- // Includes / Compiler directives //-------------------------------------------------------- // NOTE: GUI.h included at the bottom of this file (has to be after CGUI class // definition) #include "GUITooltip.h" #include "GUIbase.h" #include "ps/Overlay.h" // CPos and CRect #include "lib/input.h" #include "ps/XML/Xeromyces.h" #include //-------------------------------------------------------- // Macros //-------------------------------------------------------- //-------------------------------------------------------- // Types //-------------------------------------------------------- //-------------------------------------------------------- // Error declarations //-------------------------------------------------------- ERROR_TYPE(GUI, JSOpenFailed); //-------------------------------------------------------- // Declarations //-------------------------------------------------------- /** * Contains a list of values for new defaults to objects. */ struct SGUIStyle { // A list of defaults for std::map m_SettingsDefaults; }; struct JSObject; // The GUI stores a JSObject*, so needs to know that JSObject exists class IGUIObject; class CGUISpriteInstance; struct SGUIText; struct CColor; struct SGUIText; struct SGUIIcon; class CGUIString; class CGUISprite; struct SGUIImageEffects; struct SGUIScrollBarStyle; class GUITooltip; /** - * The main object that includes the whole GUI. Is singleton - * and accessed by g_GUI. + * The main object that represents a whole GUI page. * * No interfacial functions throws. */ class CGUI { friend class IGUIObject; friend class IGUIScrollBarOwner; friend class CInternalCGUIAccessorBase; private: // Private typedefs typedef IGUIObject *(*ConstructObjectFunction)(); public: CGUI(); ~CGUI(); /** * Initializes GUI script classes */ static void ScriptingInit(); /** * Initializes the GUI, needs to be called before the GUI is used */ void Initialize(); /** * Performs processing that should happen every frame (including the "Tick" event) */ void TickObjects(); /** * Sends a specified event to every object */ void SendEventToAll(const CStr& EventName); /** * Displays the whole GUI */ void Draw(); /** * Draw GUI Sprite * * @param Sprite Object referring to the sprite (which also caches * calculations for faster rendering) * @param CellID Number of the icon cell to use. (Ignored if this sprite doesn't * have any images with "cell-size") * @param Z Drawing order, depth value * @param Rect Position and Size * @param Clipping The sprite shouldn't be drawn outside this rectangle */ void DrawSprite(const CGUISpriteInstance& Sprite, int CellID, const float &Z, const CRect &Rect, const CRect &Clipping=CRect()); /** * Draw a SGUIText object * * @param Text Text object. * @param DefaultColor Color used if no tag applied. * @param pos position * @param z z value. */ void DrawText(SGUIText &Text, const CColor &DefaultColor, const CPos &pos, const float &z, const CRect &clipping); /** * Clean up, call this to clean up all memory allocated * within the GUI. */ void Destroy(); /** * The replacement of Process(), handles an SDL_Event_ * * @param ev SDL Event, like mouse/keyboard input */ InReaction HandleEvent(const SDL_Event_* ev); /** * Load a GUI XML file into the GUI. * * VERY IMPORTANT! All \-files must be read before * everything else! * * @param Filename Name of file * @param Paths Set of paths; all XML and JS files loaded will be added to this */ void LoadXmlFile(const VfsPath& Filename, std::set& Paths); /** * Checks if object exists and return true or false accordingly * * @param Name String name of object * @return true if object exists */ bool ObjectExists(const CStr& Name) const; /** * Returns the GUI object with the desired name, or NULL * if no match is found, * * @param Name String name of object * @return Matching object, or NULL */ IGUIObject* FindObjectByName(const CStr& Name) const; /** * The GUI needs to have all object types inputted and * their constructors. Also it needs to associate a type * by a string name of the type. * * To add a type: * @code * AddObjectType("button", &CButton::ConstructObject); * @endcode * * @param str Reference name of object type * @param pFunc Pointer of function ConstuctObject() in the object * * @see CGUI#ConstructObject() */ void AddObjectType(const CStr& str, ConstructObjectFunction pFunc) { m_ObjectTypes[str] = pFunc; } /** * Update Resolution, should be called every time the resolution * of the OpenGL screen has been changed, this is because it needs * to re-cache all its actual sizes * * Needs no input since screen resolution is global. * * @see IGUIObject#UpdateCachedSize() */ void UpdateResolution(); /** * Generate a SGUIText object from the inputted string. * The function will break down the string and its * tags to calculate exactly which rendering queries * will be sent to the Renderer. * * Done through the CGUI since it can communicate with * * @param Text Text to generate SGUIText object from * @param Font Default font, notice both Default color and default font * can be changed by tags. * @param Width Width, 0 if no word-wrapping. * @param BufferZone space between text and edge, and space between text and images. * * pObject is *only* if error parsing fails, and we need to be able to output * which object the error occured in to aid the user. The parameter is completely * optional. */ SGUIText GenerateText(const CGUIString &Text, const CStr& Font, const float &Width, const float &BufferZone, const IGUIObject *pObject=NULL); /** * Returns the JSObject* associated with the GUI * * @return The relevant JS object */ JSObject* GetScriptObject() { return m_ScriptObject; } /** * Check if an icon exists */ bool IconExists(const CStr& str) const { return (m_Icons.count(str) != 0); } /** * Get Icon (a copy, can never be changed) */ SGUIIcon GetIcon(const CStr& str) const { return m_Icons.find(str)->second; } /** * Get pre-defined color (if it exists) * Returns false if it fails. */ bool GetPreDefinedColor(const CStr& name, CColor &Output); private: /** * Updates the object pointers, needs to be called each * time an object has been added or removed. * * This function is atomic, meaning if it throws anything, it will * have seen it through that nothing was ultimately changed. * * @throws PSERROR_GUI that is thrown from IGUIObject::AddToPointersMap(). */ void UpdateObjects(); /** * Adds an object to the GUI's object database * Private, since you can only add objects through * XML files. Why? Because it enables the GUI to * be much more encapsulated and safe. * * @throws Rethrows PSERROR_GUI from IGUIObject::AddChild(). */ void AddObject(IGUIObject* pObject); /** * You input the name of the object type, and let's * say you input "button", then it will construct a * CGUIObjet* as a CButton. * * @param str Name of object type * @return Newly constructed IGUIObject (but constructed as a subclass) */ IGUIObject *ConstructObject(const CStr& str); /** * Get Focused Object. */ IGUIObject *GetFocusedObject() { return m_FocusedObject; } //-------------------------------------------------------- /** @name XML Reading Xeromyces specific subroutines * * These does not throw! * Because when reading in XML files, it won't be fatal * if an error occurs, perhaps one particular object * fails, but it'll still continue reading in the next. * All Error are reported with ReportParseError */ //-------------------------------------------------------- /* Xeromyces_* functions tree (ReadRootObjects) | +-