mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
Compare commits
8 commits
7cd26a2bb0
...
71400e8045
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71400e8045 | ||
|
|
43e7dbc6da | ||
|
|
2f2cbb96bf | ||
|
|
a4b580991b | ||
|
|
babe9e5c18 | ||
|
|
cbce748b8c | ||
|
|
1ab55e7f2e | ||
|
|
b4fe426963 |
12 changed files with 136 additions and 414 deletions
|
|
@ -6,9 +6,6 @@ export function BaseAI(settings)
|
|||
return;
|
||||
|
||||
this.player = settings.player;
|
||||
|
||||
// played turn, in case you don't want the AI to play every turn.
|
||||
this.turn = 0;
|
||||
}
|
||||
|
||||
/** Return a simple object (using no classes etc) that will be serialized into saved games */
|
||||
|
|
@ -23,7 +20,6 @@ BaseAI.prototype.Serialize = function()
|
|||
*/
|
||||
BaseAI.prototype.Deserialize = function(data, sharedScript)
|
||||
{
|
||||
this.isDeserialized = true;
|
||||
};
|
||||
|
||||
BaseAI.prototype.Init = function(state, playerID, sharedAI)
|
||||
|
|
@ -49,14 +45,7 @@ BaseAI.prototype.CustomInit = function()
|
|||
BaseAI.prototype.HandleMessage = function(state, playerID, sharedAI)
|
||||
{
|
||||
PlayerID = playerID;
|
||||
this.events = sharedAI.events;
|
||||
this.territoryMap = sharedAI.territoryMap;
|
||||
|
||||
if (this.isDeserialized)
|
||||
{
|
||||
this.Init(state, playerID, sharedAI);
|
||||
this.isDeserialized = false;
|
||||
}
|
||||
this.OnUpdate(sharedAI);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ export function PetraBot(settings)
|
|||
{
|
||||
BaseAI.call(this, settings);
|
||||
|
||||
// played turn, because Petra doesn't play every turn.
|
||||
this.turn = 0;
|
||||
this.playedTurn = 0;
|
||||
this.elapsedTime = 0;
|
||||
|
||||
|
|
@ -89,17 +91,20 @@ PetraBot.prototype.CustomInit = function(gameState)
|
|||
|
||||
PetraBot.prototype.OnUpdate = function(sharedScript)
|
||||
{
|
||||
if (this.isDeserialized)
|
||||
this.Init(state, playerID, sharedAI);
|
||||
|
||||
if (this.gameFinished || this.gameState.playerData.state == "defeated")
|
||||
return;
|
||||
|
||||
for (const i in this.events)
|
||||
for (const i in sharedScript.events)
|
||||
{
|
||||
if (i == "AIMetadata") // not used inside petra
|
||||
continue;
|
||||
if (this.savedEvents[i] !== undefined)
|
||||
this.savedEvents[i] = this.savedEvents[i].concat(this.events[i]);
|
||||
this.savedEvents[i] = this.savedEvents[i].concat(sharedScript.events[i]);
|
||||
else
|
||||
this.savedEvents[i] = this.events[i];
|
||||
this.savedEvents[i] = sharedScript.events[i];
|
||||
}
|
||||
|
||||
// Run the update every n turns, offset depending on player ID to balance the load
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"name": "Petra Bot",
|
||||
"description": "Petra is the default 0 A.D. AI bot. Please report issues to Wildfire Games.\n\nThe AI has six levels of difficulty: Sandbox, Very Easy, Easy, Medium, Hard, and Very Hard. Besides adjusting its strategy depending on the level, the AI also receives advantages or handycaps for certain rates, such as trade gain and efficiency of resource gathering. The AI can be quite challenging for beginners, so it is recommended to start with Very Easy or Easy until until you feel confident.",
|
||||
"moduleName" : "PETRA",
|
||||
"constructor": "PetraBot",
|
||||
"filename": "_petrabot.js",
|
||||
"useShared": true
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
|
@ -26,28 +26,23 @@
|
|||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "file_system.h"
|
||||
|
||||
#include "lib/debug.h"
|
||||
#include "lib/file/file_system.h"
|
||||
#include "lib/posix/posix_filesystem.h"
|
||||
#include "lib/sysdep/filesystem.h"
|
||||
#include "lib/sysdep/os.h"
|
||||
|
||||
#include <boost/version.hpp>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
struct WDIR;
|
||||
|
||||
bool DirectoryExists(const OsPath& path)
|
||||
{
|
||||
WDIR* dir = wopendir(path);
|
||||
if(dir)
|
||||
try
|
||||
{
|
||||
wclosedir(dir);
|
||||
return true;
|
||||
return std::filesystem::is_directory(path.string());
|
||||
}
|
||||
catch (std::filesystem::filesystem_error& err)
|
||||
{
|
||||
debug_printf("DirectoryExists: failed to check if directory '%s' exists, reason: %s\n", path.string8().c_str(), err.what());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -55,158 +50,152 @@ bool DirectoryExists(const OsPath& path)
|
|||
|
||||
bool FileExists(const OsPath& pathname)
|
||||
{
|
||||
struct stat s;
|
||||
const bool exists = wstat(pathname, &s) == 0;
|
||||
return exists;
|
||||
try
|
||||
{
|
||||
return std::filesystem::is_regular_file(pathname.string());
|
||||
}
|
||||
catch (std::filesystem::filesystem_error& err)
|
||||
{
|
||||
debug_printf("FileExists: failed to check if file '%s' exists, reason: %s\n", pathname.string8().c_str(), err.what());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
u64 FileSize(const OsPath& pathname)
|
||||
{
|
||||
struct stat s;
|
||||
ENSURE(wstat(pathname, &s) == 0);
|
||||
return s.st_size;
|
||||
try
|
||||
{
|
||||
return static_cast<u64>(std::filesystem::file_size(pathname.string()));
|
||||
}
|
||||
catch (std::filesystem::filesystem_error& err)
|
||||
{
|
||||
debug_printf("FileSize: failed to get filesize for '%s', reason: %s\n", pathname.string8().c_str(), err.what());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Status GetFileInfo(const OsPath& pathname, CFileInfo* pPtrInfo)
|
||||
{
|
||||
errno = 0;
|
||||
struct stat s;
|
||||
memset(&s, 0, sizeof(s));
|
||||
if(wstat(pathname, &s) != 0)
|
||||
WARN_RETURN(StatusFromErrno());
|
||||
|
||||
*pPtrInfo = CFileInfo(pathname.Filename(), s.st_size, s.st_mtime);
|
||||
try
|
||||
{
|
||||
const std::filesystem::path path{pathname.string()};
|
||||
*pPtrInfo = CFileInfo(path.filename().wstring(), static_cast<u64>(std::filesystem::file_size(path)),
|
||||
static_cast<time_t>(std::chrono::duration_cast<std::chrono::seconds>(std::filesystem::last_write_time(path).time_since_epoch()).count()));
|
||||
}
|
||||
catch (std::filesystem::filesystem_error& err)
|
||||
{
|
||||
debug_printf("GetFileInfo: failed to get file info for '%s', reason: %s\n", pathname.string8().c_str(), err.what());
|
||||
return ERR::EXCEPTION;
|
||||
}
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
struct DirDeleter
|
||||
{
|
||||
void operator()(WDIR* osDir) const
|
||||
{
|
||||
const int ret = wclosedir(osDir);
|
||||
ENSURE(ret == 0);
|
||||
}
|
||||
};
|
||||
|
||||
Status GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames* subdirectoryNames)
|
||||
{
|
||||
// open directory
|
||||
errno = 0;
|
||||
WDIR* pDir = wopendir(path);
|
||||
if(!pDir)
|
||||
return StatusFromErrno(); // NOWARN
|
||||
std::shared_ptr<WDIR> osDir(pDir, DirDeleter());
|
||||
|
||||
for(;;)
|
||||
try
|
||||
{
|
||||
errno = 0;
|
||||
struct wdirent* osEnt = wreaddir(osDir.get());
|
||||
if(!osEnt)
|
||||
for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(path.string()))
|
||||
{
|
||||
// no error, just no more entries to return
|
||||
if(!errno)
|
||||
return INFO::OK;
|
||||
WARN_RETURN(StatusFromErrno());
|
||||
}
|
||||
|
||||
for(size_t i = 0; osEnt->d_name[i] != '\0'; i++)
|
||||
RETURN_STATUS_IF_ERR(Path::Validate(osEnt->d_name[i]));
|
||||
|
||||
const std::wstring_view name{osEnt->d_name};
|
||||
|
||||
// get file information (mode, size, mtime)
|
||||
struct stat s;
|
||||
#if OS_WIN
|
||||
// .. return wdirent directly (much faster than calling stat).
|
||||
RETURN_STATUS_IF_ERR(wreaddir_stat_np(osDir.get(), &s));
|
||||
#else
|
||||
// .. call regular stat().
|
||||
errno = 0;
|
||||
const OsPath pathname = path / OsPath(osEnt->d_name);
|
||||
if(wstat(pathname, &s) != 0)
|
||||
{
|
||||
if(errno == ENOENT)
|
||||
if (entry.is_directory() && entry.path().filename() != "." && entry.path().filename() != ".." && subdirectoryNames)
|
||||
{
|
||||
// TODO: This should be displayed to the user as a LOGWARNING when this code is
|
||||
// moved to ps/
|
||||
debug_printf("The path \"%s\" cannot be found. It is probably a dangling link "
|
||||
"pointing to a non-existent path.\n", pathname.string8().c_str());
|
||||
continue;
|
||||
subdirectoryNames->emplace_back(entry.path().filename());
|
||||
}
|
||||
else if (entry.is_regular_file() && files)
|
||||
{
|
||||
files->emplace_back(entry.path().filename().wstring(), static_cast<u64>(entry.file_size()),
|
||||
static_cast<time_t>(std::chrono::duration_cast<std::chrono::seconds>(entry.last_write_time().time_since_epoch()).count()));
|
||||
}
|
||||
WARN_RETURN(StatusFromErrno());
|
||||
}
|
||||
#endif
|
||||
|
||||
if(files && S_ISREG(s.st_mode))
|
||||
files->emplace_back(osEnt->d_name, s.st_size, s.st_mtime);
|
||||
else if(subdirectoryNames && S_ISDIR(s.st_mode) && name != L"." && name != L"..")
|
||||
subdirectoryNames->emplace_back(osEnt->d_name);
|
||||
}
|
||||
catch (std::filesystem::filesystem_error& err)
|
||||
{
|
||||
debug_printf("GetDirectoryEntries: failed to get directory entries for'%s', reason: %s\n", path.string8().c_str(), err.what());
|
||||
return ERR::EXCEPTION;
|
||||
}
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::filesystem::perms ModeTToPerms(mode_t mode)
|
||||
{
|
||||
using std::filesystem::perms;
|
||||
perms perm{perms::none};
|
||||
|
||||
if (mode | S_IRUSR)
|
||||
perm |= perms::owner_read;
|
||||
if (mode | S_IWUSR)
|
||||
perm |= perms::owner_write;
|
||||
if (mode | S_IXUSR)
|
||||
perm |= perms::owner_exec;
|
||||
|
||||
if (mode | S_IRGRP)
|
||||
perm |= perms::group_read;
|
||||
if (mode | S_IWGRP)
|
||||
perm |= perms::group_write;
|
||||
if (mode | S_IXGRP)
|
||||
perm |= perms::group_exec;
|
||||
|
||||
if (mode | S_IROTH)
|
||||
perm |= perms::others_read;
|
||||
if (mode | S_IWOTH)
|
||||
perm |= perms::others_write;
|
||||
if (mode | S_IXOTH)
|
||||
perm |= perms::others_exec;
|
||||
|
||||
return perm;
|
||||
}
|
||||
|
||||
Status CreateDirectoriesImpl(const std::filesystem::path& path, const std::filesystem::perms& perms)
|
||||
{
|
||||
if (std::filesystem::exists(path))
|
||||
return ERR::FAIL;
|
||||
|
||||
if (!std::filesystem::is_directory(path.parent_path()))
|
||||
{
|
||||
const Status status = CreateDirectoriesImpl(path.parent_path(), perms);
|
||||
if (status != INFO::OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
std::filesystem::create_directory(path);
|
||||
std::filesystem::permissions(path, perms);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Status CreateDirectories(const OsPath& path, mode_t mode, bool breakpoint)
|
||||
{
|
||||
if(path.empty())
|
||||
return INFO::OK;
|
||||
|
||||
struct stat s;
|
||||
if(wstat(path, &s) == 0)
|
||||
try
|
||||
{
|
||||
if(!S_ISDIR(s.st_mode)) // encountered a file
|
||||
WARN_RETURN(ERR::FAIL);
|
||||
return INFO::OK;
|
||||
return CreateDirectoriesImpl(std::filesystem::path(path.string()), ModeTToPerms(mode));
|
||||
}
|
||||
|
||||
// If we were passed a path ending with '/', strip the '/' now so that
|
||||
// we can consistently use Parent to find parent directory names
|
||||
if(path.IsDirectory())
|
||||
return CreateDirectories(path.Parent(), mode, breakpoint);
|
||||
|
||||
RETURN_STATUS_IF_ERR(CreateDirectories(path.Parent(), mode));
|
||||
|
||||
errno = 0;
|
||||
if(wmkdir(path, mode) != 0)
|
||||
catch (std::filesystem::filesystem_error& err)
|
||||
{
|
||||
debug_printf("CreateDirectories: failed to mkdir %s (mode %d)\n", path.string8().c_str(), mode);
|
||||
debug_printf("CreateDirectories: failed to create directories '%s', reason: %s\n", path.string8().c_str(), err.what());
|
||||
if (breakpoint)
|
||||
WARN_RETURN(StatusFromErrno());
|
||||
else
|
||||
return StatusFromErrno();
|
||||
WARN_RETURN(ERR::EXCEPTION);
|
||||
else
|
||||
return ERR::EXCEPTION;
|
||||
}
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
Status DeleteDirectory(const OsPath& path)
|
||||
{
|
||||
// note: we have to recursively empty the directory before it can
|
||||
// be deleted (required by Windows and POSIX rmdir()).
|
||||
|
||||
CFileInfos files; DirectoryNames subdirectoryNames;
|
||||
RETURN_STATUS_IF_ERR(GetDirectoryEntries(path, &files, &subdirectoryNames));
|
||||
|
||||
// delete files
|
||||
for(size_t i = 0; i < files.size(); i++)
|
||||
try
|
||||
{
|
||||
const OsPath pathname = path / files[i].Name();
|
||||
errno = 0;
|
||||
if(wunlink(pathname) != 0)
|
||||
WARN_RETURN(StatusFromErrno());
|
||||
std::filesystem::remove_all(path.string());
|
||||
}
|
||||
catch (std::filesystem::filesystem_error& err)
|
||||
{
|
||||
debug_printf("DeleteDirectory: failed to delete directory '%s', reason: %s\n", path.string8().c_str(), err.what());
|
||||
return ERR::EXCEPTION;
|
||||
}
|
||||
|
||||
// recurse over subdirectoryNames
|
||||
for(size_t i = 0; i < subdirectoryNames.size(); i++)
|
||||
RETURN_STATUS_IF_ERR(DeleteDirectory(path / subdirectoryNames[i]));
|
||||
|
||||
errno = 0;
|
||||
if(wrmdir(path) != 0)
|
||||
WARN_RETURN(StatusFromErrno());
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2022 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
|
@ -31,32 +31,6 @@
|
|||
#include "lib/posix/posix_filesystem.h" // mode_t
|
||||
|
||||
|
||||
//
|
||||
// dirent.h
|
||||
//
|
||||
|
||||
struct WDIR;
|
||||
|
||||
struct wdirent
|
||||
{
|
||||
// note: SUSv3 describes this as a "char array" but of unspecified size.
|
||||
// we declare as a pointer to avoid having to copy the string.
|
||||
wchar_t* d_name;
|
||||
};
|
||||
|
||||
extern WDIR* wopendir(const OsPath& path);
|
||||
|
||||
extern wdirent* wreaddir(WDIR*);
|
||||
|
||||
// return status for the file returned by the last successful
|
||||
// wreaddir call from the given directory stream.
|
||||
// currently sets st_size, st_mode, and st_mtime; the rest are zeroed.
|
||||
// non-portable, but considerably faster than stat(). used by dir_ForEachSortedEntry.
|
||||
extern int wreaddir_stat_np(WDIR*, struct stat*);
|
||||
|
||||
extern int wclosedir(WDIR*);
|
||||
|
||||
|
||||
//
|
||||
// fcntl.h
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
|
@ -42,13 +42,6 @@
|
|||
#include <fcntl.h>
|
||||
#include <string>
|
||||
|
||||
struct WDIR
|
||||
{
|
||||
DIR* d;
|
||||
wchar_t name[PATH_MAX];
|
||||
wdirent ent;
|
||||
};
|
||||
|
||||
#if OS_ANDROID
|
||||
|
||||
// The Crystax NDK seems to do weird things with opendir etc.
|
||||
|
|
@ -84,36 +77,6 @@ void init_libc() { }
|
|||
|
||||
#endif
|
||||
|
||||
WDIR* wopendir(const OsPath& path)
|
||||
{
|
||||
init_libc();
|
||||
DIR* d = opendir(OsString(path).c_str());
|
||||
if(!d)
|
||||
return 0;
|
||||
WDIR* wd = new WDIR;
|
||||
wd->d = d;
|
||||
wd->name[0] = '\0';
|
||||
wd->ent.d_name = wd->name;
|
||||
return wd;
|
||||
}
|
||||
|
||||
struct wdirent* wreaddir(WDIR* wd)
|
||||
{
|
||||
dirent* ent = readdir(wd->d);
|
||||
if(!ent)
|
||||
return 0;
|
||||
wcscpy_s(wd->name, ARRAY_SIZE(wd->name), OsPath(ent->d_name).string().c_str());
|
||||
return &wd->ent;
|
||||
}
|
||||
|
||||
int wclosedir(WDIR* wd)
|
||||
{
|
||||
int ret = closedir(wd->d);
|
||||
delete wd;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int wopen(const OsPath& pathname, int oflag)
|
||||
{
|
||||
ENSURE(!(oflag & O_CREAT));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
|
@ -30,55 +30,6 @@
|
|||
|
||||
#include <atomic>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// WDIR suballocator
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// most applications only need a single WDIR at a time. we avoid expensive
|
||||
// heap allocations by reusing a single static instance. if it is already
|
||||
// in use, we allocate further instances dynamically.
|
||||
// NB: this is thread-safe due to CAS.
|
||||
|
||||
struct WDIR // POD
|
||||
{
|
||||
HANDLE hFind;
|
||||
|
||||
WIN32_FIND_DATAW findData; // indeterminate if hFind == INVALID_HANDLE_VALUE
|
||||
|
||||
// wreaddir will return the address of this member.
|
||||
// (must be stored in WDIR to allow multiple independent
|
||||
// wopendir/wreaddir sequences).
|
||||
struct wdirent ent;
|
||||
|
||||
// used by wreaddir to skip the first FindNextFileW. (a counter is
|
||||
// easy to test/update and also provides useful information.)
|
||||
size_t numCalls;
|
||||
};
|
||||
|
||||
static WDIR wdir_storage;
|
||||
static std::atomic<bool> wdir_in_use{ false };
|
||||
|
||||
static inline WDIR* wdir_alloc()
|
||||
{
|
||||
if(!wdir_in_use.exchange(true)) // gained ownership
|
||||
return &wdir_storage;
|
||||
|
||||
// already in use (rare) - allocate from heap
|
||||
return new WDIR;
|
||||
}
|
||||
|
||||
static inline void wdir_free(WDIR* d)
|
||||
{
|
||||
if(d == &wdir_storage)
|
||||
{
|
||||
const bool ok = wdir_in_use.exchange(false); // relinquish ownership
|
||||
ENSURE(ok); // ensure it wasn't double-freed
|
||||
}
|
||||
else // allocated from heap
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// dirent.h
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -127,112 +78,6 @@ static bool IsValidDirectory(const OsPath& path)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Return owning pointer or nullptr on error.
|
||||
[[nodiscard]] WDIR* wopendir(const OsPath& path)
|
||||
{
|
||||
WinScopedPreserveLastError s;
|
||||
|
||||
if(!IsValidDirectory(path))
|
||||
{
|
||||
errno = ENOENT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
WDIR* d = wdir_alloc();
|
||||
d->numCalls = 0;
|
||||
|
||||
// NB: "c:\\path" only returns information about that directory;
|
||||
// trailing slashes aren't allowed. append "\\*" to retrieve its entries.
|
||||
OsPath searchPath = path/"*";
|
||||
|
||||
// (we don't defer FindFirstFileW until wreaddir because callers
|
||||
// expect us to return 0 if directory reading will/did fail.)
|
||||
d->hFind = FindFirstFileW(OsString(searchPath).c_str(), &d->findData);
|
||||
if(d->hFind != INVALID_HANDLE_VALUE)
|
||||
return d; // success
|
||||
|
||||
const DWORD nativeError{GetLastError()};
|
||||
if(nativeError == ERROR_NO_MORE_FILES)
|
||||
return d; // success, but directory is empty
|
||||
|
||||
Status status = StatusFromWin();
|
||||
|
||||
// release the WDIR allocated above (this is preferable to
|
||||
// always copying the large WDIR or findData from a temporary)
|
||||
wdir_free(d);
|
||||
|
||||
if(nativeError == ERROR_PATH_NOT_FOUND)
|
||||
// TODO: This should be displayed to the user as a LOGWARNING when this code is moved to ps/
|
||||
debug_printf("The path \"%s\" cannot be found. It is probably a dangling link "
|
||||
"pointing to a non-existent path.\n", path.string8().c_str());
|
||||
else
|
||||
WARN_IF_ERR(status);
|
||||
|
||||
errno = ErrnoFromStatus(status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct wdirent* wreaddir(WDIR* d)
|
||||
{
|
||||
// directory is empty and d->findData is indeterminate
|
||||
if(d->hFind == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
|
||||
WinScopedPreserveLastError s;
|
||||
|
||||
// until end of directory or a valid entry was found:
|
||||
for(;;)
|
||||
{
|
||||
if(d->numCalls++ != 0) // (skip first call to FindNextFileW - see wopendir)
|
||||
{
|
||||
if(!FindNextFileW(d->hFind, &d->findData))
|
||||
{
|
||||
if(GetLastError() == ERROR_NO_MORE_FILES)
|
||||
SetLastError(0);
|
||||
else // unexpected error
|
||||
DEBUG_WARN_ERR(StatusFromWin());
|
||||
return 0; // end of directory or error
|
||||
}
|
||||
}
|
||||
|
||||
// only accept non-hidden and non-system entries - otherwise,
|
||||
// callers might encounter errors when attempting to open them.
|
||||
if((d->findData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)) == 0)
|
||||
{
|
||||
d->ent.d_name = d->findData.cFileName; // (NB: d_name is a pointer)
|
||||
return &d->ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int wreaddir_stat_np(WDIR* d, struct stat* s)
|
||||
{
|
||||
// NTFS stores UTC but FAT stores local times, which are incorrectly
|
||||
// translated to UTC based on the _current_ DST settings. we no longer
|
||||
// bother checking the filesystem, since that's either unreliable or
|
||||
// expensive. timestamps may therefore be off after a DST transition,
|
||||
// which means our cached files would be regenerated.
|
||||
FILETIME* filetime = &d->findData.ftLastWriteTime;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->st_size = (off_t)u64_from_u32(d->findData.nFileSizeHigh, d->findData.nFileSizeLow);
|
||||
s->st_mode = (unsigned short)((d->findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG);
|
||||
s->st_mtime = wtime_utc_filetime_to_time_t(filetime);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wclosedir(WDIR* d)
|
||||
{
|
||||
FindClose(d->hFind);
|
||||
|
||||
wdir_free(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// fcntl.h
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@
|
|||
#include <wx/checkbox.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/clntdata.h>
|
||||
#include <wx/collpane.h>
|
||||
#include <wx/debug.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/log.h>
|
||||
|
|
@ -569,19 +568,6 @@ MapSidebar::MapSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContaine
|
|||
}
|
||||
}
|
||||
|
||||
void MapSidebar::OnCollapse(wxCollapsiblePaneEvent& WXUNUSED(evt))
|
||||
{
|
||||
Freeze();
|
||||
|
||||
// Toggling the collapsing doesn't seem to update the sidebar layout
|
||||
// automatically, so do it explicitly here
|
||||
Layout();
|
||||
|
||||
Refresh(); // fixes repaint glitch on Windows
|
||||
|
||||
Thaw();
|
||||
}
|
||||
|
||||
void MapSidebar::OnFirstDisplay()
|
||||
{
|
||||
// We do this here becase messages are used which requires simulation to be init'd
|
||||
|
|
@ -860,7 +846,6 @@ void MapSidebar::OnResizeMap(wxCommandEvent& WXUNUSED(evt))
|
|||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(MapSidebar, Sidebar)
|
||||
EVT_COLLAPSIBLEPANE_CHANGED(wxID_ANY, MapSidebar::OnCollapse)
|
||||
EVT_BUTTON(ID_SimPlay, MapSidebar::OnSimPlay)
|
||||
EVT_BUTTON(ID_SimFast, MapSidebar::OnSimPlay)
|
||||
EVT_BUTTON(ID_SimSlow, MapSidebar::OnSimPlay)
|
||||
|
|
@ -871,4 +856,4 @@ BEGIN_EVENT_TABLE(MapSidebar, Sidebar)
|
|||
EVT_BUTTON(ID_ResizeMap, MapSidebar::OnResizeMap)
|
||||
EVT_BUTTON(ID_OpenPlayerPanel, MapSidebar::OnOpenPlayerPanel)
|
||||
EVT_CHOICE(ID_RandomScript, MapSidebar::OnRandomScript)
|
||||
END_EVENT_TABLE();
|
||||
END_EVENT_TABLE()
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
class MapSettingsControl;
|
||||
class ScenarioEditor;
|
||||
class wxCollapsiblePaneEvent;
|
||||
class wxWindow;
|
||||
|
||||
class MapSidebar : public Sidebar
|
||||
|
|
@ -37,7 +36,6 @@ protected:
|
|||
private:
|
||||
MapSettingsControl* m_MapSettingsCtrl;
|
||||
|
||||
void OnCollapse(wxCollapsiblePaneEvent& evt);
|
||||
void OnOpenPlayerPanel(wxCommandEvent& evt);
|
||||
void OnRandomScript(wxCommandEvent& evt);
|
||||
void OnRandomReseed(wxCommandEvent& evt);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@
|
|||
#include <wx/choice.h>
|
||||
#include <wx/choicebk.h>
|
||||
#include <wx/clntdata.h>
|
||||
#include <wx/collpane.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/colourdata.h>
|
||||
#include <wx/debug.h>
|
||||
|
|
@ -250,9 +249,6 @@ public:
|
|||
|
||||
// TODO: possibly have advanced panel where each player's diplomacy can be set?
|
||||
// Advanced panel
|
||||
/*wxCollapsiblePane* advPane = new wxCollapsiblePane(this, wxID_ANY, _("Advanced"));
|
||||
wxWindow* pane = advPane->GetPane();
|
||||
diplomacySizer->Add(advPane, 0, wxGROW | wxALL, 2);*/
|
||||
|
||||
sizer->Add(diplomacySizer, wxSizerFlags().Expand().Border(wxTOP, 10));
|
||||
}
|
||||
|
|
@ -999,19 +995,6 @@ PlayerSidebar::PlayerSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarCo
|
|||
scrollSizer->Add(m_PlayerSettingsCtrl, wxSizerFlags().Expand());
|
||||
}
|
||||
|
||||
void PlayerSidebar::OnCollapse(wxCollapsiblePaneEvent& WXUNUSED(evt))
|
||||
{
|
||||
Freeze();
|
||||
|
||||
// Toggling the collapsing doesn't seem to update the sidebar layout
|
||||
// automatically, so do it explicitly here
|
||||
Layout();
|
||||
|
||||
Refresh(); // fixes repaint glitch on Windows
|
||||
|
||||
Thaw();
|
||||
}
|
||||
|
||||
void PlayerSidebar::OnFirstDisplay()
|
||||
{
|
||||
// We do this here becase messages are used which requires simulation to be init'd
|
||||
|
|
@ -1032,7 +1015,3 @@ void PlayerSidebar::OnMapReload()
|
|||
m_PlayerSettingsCtrl->ReadFromEngine();
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(PlayerSidebar, Sidebar)
|
||||
EVT_COLLAPSIBLEPANE_CHANGED(wxID_ANY, PlayerSidebar::OnCollapse)
|
||||
END_EVENT_TABLE();
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ class PlayerSettingsControl;
|
|||
class ScenarioEditor;
|
||||
class wxButton;
|
||||
class wxChoice;
|
||||
class wxCollapsiblePaneEvent;
|
||||
class wxSpinCtrl;
|
||||
class wxTextCtrl;
|
||||
class wxWindow;
|
||||
|
|
@ -47,11 +46,7 @@ protected:
|
|||
private:
|
||||
PlayerSettingsControl* m_PlayerSettingsCtrl;
|
||||
|
||||
void OnCollapse(wxCollapsiblePaneEvent& evt);
|
||||
|
||||
bool m_Loaded;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
// Controls present on each player page
|
||||
|
|
|
|||
|
|
@ -48,11 +48,11 @@
|
|||
#include <wx/chartype.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/choicebk.h>
|
||||
#include <wx/clntdata.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/object.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/scrolwin.h>
|
||||
|
|
@ -546,13 +546,14 @@ BEGIN_EVENT_TABLE(TextureNotebookPage, wxPanel)
|
|||
END_EVENT_TABLE();
|
||||
|
||||
|
||||
class TextureNotebook : public wxNotebook
|
||||
class TextureNotebook : public wxChoicebook
|
||||
{
|
||||
public:
|
||||
TextureNotebook(ScenarioEditor& scenarioEditor, wxWindow *parent)
|
||||
: wxNotebook(parent, wxID_ANY/*, wxDefaultPosition, wxDefaultSize, wxNB_FIXEDWIDTH*/),
|
||||
: wxChoicebook(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxCHB_TOP),
|
||||
m_ScenarioEditor(scenarioEditor)
|
||||
{
|
||||
GetChoiceCtrl()->SetMaxSize(wxSize{300, 100});
|
||||
}
|
||||
|
||||
void LoadTerrain()
|
||||
|
|
@ -607,8 +608,8 @@ private:
|
|||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(TextureNotebook, wxNotebook)
|
||||
EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY, TextureNotebook::OnPageChanged)
|
||||
BEGIN_EVENT_TABLE(TextureNotebook, wxChoicebook)
|
||||
EVT_CHOICEBOOK_PAGE_CHANGED(wxID_ANY, TextureNotebook::OnPageChanged)
|
||||
END_EVENT_TABLE();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
Loading…
Reference in a new issue