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