Index: source/lib/sysdep/os/osx/osxpaths.h
===================================================================
--- source/lib/sysdep/os/osx/osxpaths.h	(revision 0)
+++ source/lib/sysdep/os/osx/osxpaths.h	(working copy)
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012 Wildfire Games
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+#ifndef INCLUDE_OSXPATHS_H
+#define INCLUDE_OSXPATHS_H
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFString.h>
+
+CFStringRef osx_AppSupportPath();
+
+CFStringRef osx_CachesPath();
+
+CFStringRef osx_DocumentsPath();
+
+#endif // INCLUDE_OSXPATHS_H
Index: source/lib/sysdep/os/osx/osxpaths.h
===================================================================
--- source/lib/sysdep/os/osx/osxpaths.h	(revision 0)
+++ source/lib/sysdep/os/osx/osxpaths.h	(working copy)

Property changes on: source/lib/sysdep/os/osx/osxpaths.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
Index: source/lib/sysdep/os/osx/osxpaths.mm
===================================================================
--- source/lib/sysdep/os/osx/osxpaths.mm	(revision 0)
+++ source/lib/sysdep/os/osx/osxpaths.mm	(working copy)
@@ -0,0 +1,92 @@
+/* Copyright (c) 2012 Wildfire Games
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "osxpaths.h"
+
+
+// Find ~/Library/Application Support/ directory
+CFStringRef osx_AppSupportPath()
+{
+	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+	NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
+
+	if ([paths count] == 0)
+	{
+		return NULL;
+	}
+
+	CFStringRef path = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)paths, 0);
+
+	// Caller will be responsible for freeing the string with CFRelease
+	CFRetain(path);
+
+	[pool release];
+
+	return path;
+}
+
+// Find ~/Library/Caches/ directory
+CFStringRef osx_CachesPath()
+{
+	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+	NSArray* paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+
+	if ([paths count] == 0)
+	{
+		return NULL;
+	}
+
+	CFStringRef path = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)paths, 0);
+
+	// Caller will be responsible for freeing the string with CFRelease
+	CFRetain(path);
+
+	[pool release];
+
+	return path;
+}
+
+// Find ~/Documents/ directory
+CFStringRef osx_DocumentsPath()
+{
+	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+	NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+
+	if ([paths count] == 0)
+	{
+		return NULL;
+	}
+
+	CFStringRef path = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)paths, 0);
+
+	// Caller will be responsible for freeing the string with CFRelease
+	CFRetain(path);
+
+	[pool release];
+
+	return path;
+}
Index: source/lib/sysdep/os/win/wutil.cpp
===================================================================
--- source/lib/sysdep/os/win/wutil.cpp	(revision 11023)
+++ source/lib/sysdep/os/win/wutil.cpp	(working copy)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010 Wildfire Games
+/* Copyright (c) 2012 Wildfire Games
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files (the
@@ -251,7 +251,9 @@
 // (NB: wutil_Init is called before static ctors => use placement new)
 static OsPath* systemPath;
 static OsPath* executablePath;
-static OsPath* appdataPath;
+static OsPath* localAppdataPath;
+static OsPath* roamingAppdataPath;
+static OsPath* personalPath;
 
 const OsPath& wutil_SystemPath()
 {
@@ -263,12 +265,22 @@
 	return *executablePath;
 }
 
-const OsPath& wutil_AppdataPath()
+const OsPath& wutil_LocalAppdataPath()
 {
-	return *appdataPath;
+	return *localAppdataPath;
 }
 
+const OsPath& wutil_RoamingAppdataPath()
+{
+	return *roamingAppdataPath;
+}
 
+const OsPath& wutil_PersonalPath()
+{
+	return *personalPath;
+}
+
+
 static void GetDirectories()
 {
 	WinScopedPreserveLastError s;
@@ -286,7 +298,7 @@
 	// executable's directory
 	executablePath = new(wutil_Allocate(sizeof(OsPath))) OsPath(sys_ExecutablePathname().Parent());
 
-	// application data
+	// roaming application data
 	{
 		HWND hwnd = 0;	// ignored unless a dial-up connection is needed to access the folder
 		HANDLE token = 0;
@@ -295,8 +307,32 @@
 		ENSURE(SUCCEEDED(ret));
 		if(GetLastError() == ERROR_NO_TOKEN)	// avoid polluting last error
 			SetLastError(0);
-		appdataPath = new(wutil_Allocate(sizeof(OsPath))) OsPath(path);
+		roamingAppdataPath = new(wutil_Allocate(sizeof(OsPath))) OsPath(path);
 	}
+
+	// local application data
+	{
+		HWND hwnd = 0;	// ignored unless a dial-up connection is needed to access the folder
+		HANDLE token = 0;
+		wchar_t path[MAX_PATH];	// mandated by SHGetFolderPathW
+		const HRESULT ret = SHGetFolderPathW(hwnd, CSIDL_LOCAL_APPDATA, token, 0, path);
+		ENSURE(SUCCEEDED(ret));
+		if(GetLastError() == ERROR_NO_TOKEN)	// avoid polluting last error
+			SetLastError(0);
+		localAppdataPath = new(wutil_Allocate(sizeof(OsPath))) OsPath(path);
+	}
+
+	// my documents
+	{
+		HWND hwnd = 0;	// ignored unless a dial-up connection is needed to access the folder
+		HANDLE token = 0;
+		wchar_t path[MAX_PATH];	// mandated by SHGetFolderPathW
+		const HRESULT ret = SHGetFolderPathW(hwnd, CSIDL_PERSONAL, token, 0, path);
+		ENSURE(SUCCEEDED(ret));
+		if(GetLastError() == ERROR_NO_TOKEN)	// avoid polluting last error
+			SetLastError(0);
+		personalPath = new(wutil_Allocate(sizeof(OsPath))) OsPath(path);
+	}
 }
 
 
@@ -306,8 +342,12 @@
 	wutil_Free(systemPath);
 	executablePath->~OsPath();
 	wutil_Free(executablePath);
-	appdataPath->~OsPath();
-	wutil_Free(appdataPath);
+	localAppdataPath->~OsPath();
+	wutil_Free(localAppdataPath);
+	roamingAppdataPath->~OsPath();
+	wutil_Free(roamingAppdataPath);
+	personalPath->~OsPath();
+	wutil_Free(personalPath);
 }
 
 
Index: source/lib/sysdep/os/win/wutil.h
===================================================================
--- source/lib/sysdep/os/win/wutil.h	(revision 11023)
+++ source/lib/sysdep/os/win/wutil.h	(working copy)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010 Wildfire Games
+/* Copyright (c) 2012 Wildfire Games
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files (the
@@ -159,7 +159,9 @@
 
 extern const OsPath& wutil_SystemPath();
 extern const OsPath& wutil_ExecutablePath();
-extern const OsPath& wutil_AppdataPath();
+extern const OsPath& wutil_LocalAppdataPath();
+extern const OsPath& wutil_RoamingAppdataPath();
+extern const OsPath& wutil_PersonalPath();
 
 
 //-----------------------------------------------------------------------------
Index: source/ps/GameSetup/GameSetup.cpp
===================================================================
--- source/ps/GameSetup/GameSetup.cpp	(revision 11023)
+++ source/ps/GameSetup/GameSetup.cpp	(working copy)
@@ -441,8 +441,8 @@
 	const size_t cacheSize = ChooseCacheSize();
 	g_VFS = CreateVfs(cacheSize);
 
-	g_VFS->Mount(L"screenshots/", paths.Data()/"screenshots"/"");
-	g_VFS->Mount(L"saves/", paths.Data()/"saves"/"");
+	g_VFS->Mount(L"screenshots/", paths.UserData()/"screenshots"/"");
+	g_VFS->Mount(L"saves/", paths.UserData()/"saves"/"");
 	const OsPath readonlyConfig = paths.RData()/"config"/"";
 	g_VFS->Mount(L"config/", readonlyConfig);
 	if(readonlyConfig != paths.Config())
Index: source/ps/GameSetup/Paths.cpp
===================================================================
--- source/ps/GameSetup/Paths.cpp	(revision 11023)
+++ source/ps/GameSetup/Paths.cpp	(working copy)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009 Wildfire Games.
+/* Copyright (C) 2012 Wildfire Games.
  * This file is part of 0 A.D.
  *
  * 0 A.D. is free software: you can redistribute it and/or modify
@@ -22,7 +22,9 @@
 #include "lib/sysdep/sysdep.h"	// sys_get_executable_name
 #include "lib/sysdep/filesystem.h"	// wrealpath
 #if OS_WIN
-# include "lib/sysdep/os/win/wutil.h"	// wutil_AppdataPath
+# include "lib/sysdep/os/win/wutil.h"	// wutil_*Path
+#elif OS_MACOSX
+# include "lib/sysdep/os/osx/osxpaths.h" // osx_*Path
 #endif
 
 
@@ -38,33 +40,122 @@
 
 	const char* subdirectoryName = args.Has("writableRoot")? 0 : "0ad";
 
-	// everything is a subdirectory of the root
+	// Note: Only if writableRoot option is passed to the game, then
+	//	all the data is a subdirectory of the root
 	if(!subdirectoryName)
 	{
-		m_data = m_rdata;
-		m_config = m_data/"config"/"";
-		m_cache = m_data/"cache"/"";
-		m_logs = m_root/"logs"/"";
+		m_gameData = m_rdata;
+		m_userData = m_gameData;
+		m_config = m_gameData / "config"/"";
+		m_cache = m_gameData / "cache"/"";
+		m_logs = m_root / "logs"/"";
 	}
-	else
+	else // OS-specific path handling
 	{
+
 #if OS_WIN
-		const OsPath appdata = wutil_AppdataPath() / subdirectoryName/"";
-		m_data = appdata/"data"/"";
-		m_config = appdata/"config"/"";
-		m_cache = appdata/"cache"/"";
-		m_logs = appdata/"logs"/"";
-#else
+
+		/* For reasoning behind our Windows paths, see the discussion here:
+		 * http://www.wildfiregames.com/forum/index.php?showtopic=14759
+		 * 
+		 * Summary:
+		 *	1. Local appdata: for bulky unfriendly data like the cache,
+		 *      which can be recreated if deleted; doesn't need backing up.
+		 *  2. Roaming appdata: for slightly less unfriendly data like config
+		 *      files that might theoretically be shared between different
+		 *      machines on a domain.
+		 *  3. Personal / My Documents: for data explicitly created by the user,
+		 *      and which should be visible and easily accessed. We use a non-
+		 *      localized My Games subfolder for improved organization.
+		 */
+
+		// %localappdata%/0ad/
+		const OsPath localAppdata = wutil_LocalAppdataPath() / subdirectoryName/"";
+		// %appdata%/0ad/
+		const OsPath roamingAppData = wutil_RoamingAppdataPath() / subdirectoryName/"";
+		// My Documents/My Games/0ad/
+		const OsPath personalData = wutil_PersonalPath() / "My Games" / subdirectoryName/"";
+
+		m_cache = localAppdata / "cache"/"";
+		m_gameData = roamingAppData / "data"/"";
+		m_userData = personalData/"";
+		m_config = roamingAppData / "config"/"";
+		m_logs = personalData / "logs"/"";
+
+#elif OS_MACOSX
+
+		/* For reasoning behind our OS X paths, see the discussion here:
+		 * http://www.wildfiregames.com/forum/index.php?showtopic=15511
+		 *
+		 * Summary:
+		 * 1. Application Support - This is where data created by the app
+		 *     is supposed to be stored, with a few exceptions. However,
+		 *     on Lion this folder is hidden in Finder, it doesn't make sense
+		 *     to store screenshots and saved games in a hidden location!
+		 * 2. Caches - This is where cached data for the app is stored,
+		 *     and unlike App Support it is not automatically backed up.
+		 * 3. Documents - This is a friendly easy-to-access folder that seems
+		 *     appropriate for files created on-demand by the user and which
+		 *     they want to access directory e.g. screenshots.
+		 *
+		 * Note: the paths returned by osx_*Path are not guaranteed to exist,
+		 *     but that's OK since we always create them on demand.
+		 * The returned CStringRef is our responsibility to release
+		 *     (so it's not automatically cleaned up when leaving Cocoa)
+		 */
+
+		// TODO: Use bundle name instead of 0ad?
+		OsPath appSupportPath;  // ~/Library/Application Support/0ad
+		OsPath cachePath;       // ~/Library/Caches/0ad
+		OsPath documentsPath;   // ~/Documents/0ad
+		{
+			CFStringRef pathRef = osx_AppSupportPath();
+			ENSURE(pathRef != NULL && CFStringGetLength(pathRef) > 0);
+			// There's no PATH_MAX on OS X
+			char path[CFStringGetMaximumSizeOfFileSystemRepresentation(pathRef)];
+			// Convert path CFStringRef to char*
+			CFStringGetFileSystemRepresentation(pathRef, path, sizeof(path));
+			CFRelease(pathRef);
+			appSupportPath = OsPath(path) / subdirectoryName;
+		}
+		{
+			CFStringRef pathRef = osx_CachesPath();
+			ENSURE(pathRef != NULL && CFStringGetLength(pathRef) > 0);
+			char path[CFStringGetMaximumSizeOfFileSystemRepresentation(pathRef)];
+			CFStringGetFileSystemRepresentation(pathRef, path, sizeof(path));
+			CFRelease(pathRef);
+			cachePath = OsPath(path) / subdirectoryName;
+		}
+		{
+			CFStringRef pathRef = osx_DocumentsPath();
+			ENSURE(pathRef != NULL && CFStringGetLength(pathRef) > 0);
+			char path[CFStringGetMaximumSizeOfFileSystemRepresentation(pathRef)];
+			CFStringGetFileSystemRepresentation(pathRef, path, sizeof(path));
+			CFRelease(pathRef);
+			documentsPath = OsPath(path) / subdirectoryName;
+		}
+
+		m_gameData = appSupportPath / "data"/"";
+		m_userData = documentsPath/"";
+		m_cache = cachePath/"";
+		m_config = appSupportPath / "config"/"";
+		m_logs = appSupportPath / "logs"/"";
+
+#else // OS_UNIX
+
 		const char* envHome = getenv("HOME");
 		ENSURE(envHome);
 		const OsPath home(envHome);
 		const OsPath xdgData   = XDG_Path("XDG_DATA_HOME",   home, home/".local/share/") / subdirectoryName;
 		const OsPath xdgConfig = XDG_Path("XDG_CONFIG_HOME", home, home/".config/"     ) / subdirectoryName;
 		const OsPath xdgCache  = XDG_Path("XDG_CACHE_HOME",  home, home/".cache/"      ) / subdirectoryName;
-		m_data   = xdgData/"";
+
+		// We don't make the game vs. user data distinction on Unix
+		m_gameData = xdgData/"";
+		m_userData = m_gameData;
 		m_cache  = xdgCache/"";
-		m_config = xdgConfig/"config"/"";
-		m_logs   = xdgConfig/"logs"/"";
+		m_config = xdgConfig / "config"/"";
+		m_logs   = xdgConfig / "logs"/"";
 #endif
 	}
 }
Index: source/ps/GameSetup/Paths.h
===================================================================
--- source/ps/GameSetup/Paths.h	(revision 11023)
+++ source/ps/GameSetup/Paths.h	(working copy)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009 Wildfire Games.
+/* Copyright (C) 2012 Wildfire Games.
  * This file is part of 0 A.D.
  *
  * 0 A.D. is free software: you can redistribute it and/or modify
@@ -21,36 +21,66 @@
 #include "lib/os_path.h"
 #include "CmdLineArgs.h"
 
+/**
+ * Wrapper class for OS paths used by the game
+ */
 class Paths
 {
 public:
 	Paths(const CmdLineArgs& args);
 
+	/**
+	 * Returns the game's root directory
+	 */
 	const OsPath& Root() const
 	{
 		return m_root;
 	}
 
+	/**
+	 * Returns directory for read-only data installed with the game
+	 */
 	const OsPath& RData() const
 	{
 		return m_rdata;
 	}
 
-	const OsPath& Data() const
+	/**
+	 * Returns directory for game-managed data and mods
+	 */
+	const OsPath& GameData() const
 	{
-		return m_data;
+		return m_gameData;
 	}
 
+	/**
+	 * Returns directory for user-created data (e.g. screenshots)
+	 * Only things created in reponse to an explicit user action should go here.
+	 */
+	const OsPath& UserData() const
+	{
+		return m_userData;
+	}
+
+	/**
+	 * Returns config file directory
+	 */
 	const OsPath& Config() const
 	{
 		return m_config;
 	}
 
+	/**
+	 * Returns cache directory
+	 */
 	const OsPath& Cache() const
 	{
 		return m_cache;
 	}
 
+	/**
+	 * Returns logs directory
+	 */
 	const OsPath& Logs() const
 	{
 		return m_logs;
@@ -65,7 +95,8 @@
 	OsPath m_rdata;
 
 	// writable directories
-	OsPath m_data;
+	OsPath m_gameData;
+	OsPath m_userData;
 	OsPath m_config;
 	OsPath m_cache;
 	OsPath m_logs;	// special-cased in single-root-folder installations
