Unverified Commit 6ba89d27 authored by Rob Loach's avatar Rob Loach Committed by GitHub
Browse files

Merge pull request #320 from libretro/dev

0.27.0
parents 14a66ea2 dc89fd41
......@@ -4,6 +4,18 @@ All notable changes to [ChaiLove](https://github.com/RobLoach/ChaiLove) will be
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## 0.27.0 - 2018-09-25
### Features
- Live updating of core options
- Mounts `/libretro/core` as the directory where the core was loaded from
- Adds `love.filesystem.getSaveDirectory()`
- Adds `love.filesystem.newFileData(contents, name)`
- Adds `love.filesystem.getExecutablePath()`
- Adds `love.filesystem.remove()`
### Fixes
- Fixed loading ChaiLove without active content
## 0.26.1 - 2018-09-19
### Fixes
- Fixed `mount()` with relative paths
......
......@@ -23,7 +23,7 @@ PROJECT_NAME = "ChaiLove API"
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = "0.26.1"
PROJECT_NUMBER = "0.27.0"
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
......
......@@ -47,7 +47,7 @@ global T
def conf(t) {
t.window.width = WIDTH * gridScale
t.window.height = HEIGHT * gridScale
t.version = "0.26.1"
t.version = "0.27.0"
}
/**
......
......@@ -140,6 +140,8 @@ std::string ChaiLove::demo() {
love.graphics.print(text, 10, love.graphics.getHeight() / 2.0f - 100)
var ver = love.system.getVersionString()
love.graphics.print(ver, love.graphics.getWidth() - defaultFont.getWidth(ver), love.graphics.getHeight() - defaultFont.getHeight(ver) * 2.0f)
love.graphics.print("FPS: " + to_string(love.timer.getFPS()), 10, love.graphics.getHeight() - defaultFont.getHeight(ver) * 2.0f)
}
def update(dt) {
......
......@@ -47,9 +47,9 @@
#define SRC_CHAILOVE_H_
#define CHAILOVE_VERSION_MAJOR 0
#define CHAILOVE_VERSION_MINOR 26
#define CHAILOVE_VERSION_PATCH 1
#define CHAILOVE_VERSION_STRING "0.26.1"
#define CHAILOVE_VERSION_MINOR 27
#define CHAILOVE_VERSION_PATCH 0
#define CHAILOVE_VERSION_STRING "0.27.0"
#include "SDL.h"
#include "libretro.h"
......
......@@ -79,6 +79,8 @@ void retro_set_environment(retro_environment_t cb) {
*/
static void update_variables(void) {
std::cout << "[ChaiLove] [libretro] update_variables()" << std::endl;
ChaiLove* app = ChaiLove::getInstance();
app->system.updateVariables(app->config);
}
#ifdef __cplusplus
......@@ -328,7 +330,11 @@ bool retro_load_game(const struct retro_game_info *info) {
if (gamePath == ".") {
gamePath = "main.chai";
}
return ChaiLove::getInstance()->load(gamePath, info->data);
void* data = NULL;
if (info != NULL) {
data = (void*)info->data;
}
return ChaiLove::getInstance()->load(gamePath, data);
}
/**
......@@ -423,6 +429,11 @@ void retro_run(void) {
return;
}
bool updated = false;
if (ChaiLove::environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) {
update_variables();
}
// Update the game.
app->update();
......
......@@ -16,6 +16,10 @@ FileData::FileData(const std::string& filepath) : m_filepath(filepath) {
// Nothing.
}
FileData::FileData(const std::string& contents, const std::string& name) : m_contents(contents), m_filepath(name) {
// Nothing.
}
int FileData::getSize() {
ChaiLove* app = ChaiLove::getInstance();
return app->filesystem.getSize(m_filepath);
......@@ -26,8 +30,11 @@ std::string FileData::getFilename() {
}
std::string FileData::getString() {
if (!m_contents.empty()) {
return m_contents;
}
ChaiLove* app = ChaiLove::getInstance();
return app->filesystem.read(m_filepath);
return m_contents = app->filesystem.read(m_filepath);
}
std::string FileData::getExtension() {
......
......@@ -20,6 +20,13 @@ class FileData {
* @param filepath Path to the file.
*/
FileData(const std::string& filepath);
/**
* Creates a new FileData from the given data and filename.
*
* @param contents The data for the file.
* @param name The filename of the file.
*/
FileData(const std::string& contents, const std::string& name);
/**
* The size of the Data in bytes.
......@@ -42,6 +49,7 @@ class FileData {
std::string getExtension();
std::string m_filepath;
std::string m_contents;
};
} // namespace FileSystem
......
......@@ -19,7 +19,7 @@ namespace love {
*
* @code
* def conf(t) {
* t.version = "0.26.1" // Version of ChaiLove
* t.version = "0.27.0" // Version of ChaiLove
* t.identity = "mygame" // Machine name of your game
* t.window.title = "My Game" // Human-readable name
* t.window.width = 1024 // Game width
......
......@@ -18,8 +18,8 @@ namespace love {
* Initialize the file system.
*/
bool filesystem::init(const std::string& file, const void* data) {
// Initialize PhysFS
if (PHYSFS_isInit() == 0) {
// Initialize PhysFS
if (PHYSFS_init(NULL) == 0) {
std::cout << "[ChaiLove] [filesystem] Error loading PhysFS - " << getLastError() << std::endl;
return false;
......@@ -28,27 +28,27 @@ bool filesystem::init(const std::string& file, const void* data) {
// Check if we are simply running ChaiLove.
if (file.empty()) {
return mount(".", "/");
return mount(".", "/", false);
}
// Find the parent and extension of the given file.
::filesystem::path p(file.c_str());
std::string extension(p.extension());
::filesystem::path parent(p.parent_path());
std::string parentPath(parent.str());
// Allow loading from an Archive.
if (extension == "chaigame" || extension == "chailove" || extension == "zip") {
return mount(file.c_str(), "/");
return mount(file.c_str(), "/", false);
}
// If we are just running the cour, load the base path.
// If we are just running the core, load the base path.
::filesystem::path parent(p.parent_path());
std::string parentPath(parent.str());
if (parentPath.empty()) {
return mount(".", "/");
return mount(".", "/", false);
}
// Otherwise, we are loading a .chai file directly. Load it.
return mount(parentPath.c_str(), "/");
return mount(parentPath.c_str(), "/", false);
}
void filesystem::mountlibretro() {
......@@ -56,17 +56,24 @@ void filesystem::mountlibretro() {
const char *system_dir = NULL;
const char *assets_dir = NULL;
const char *save_dir = NULL;
const char *core_dir = NULL;
if (ChaiLove::environ_cb(RETRO_ENVIRONMENT_GET_LIBRETRO_PATH, &core_dir) && core_dir) {
// Make sure to get the directory of the core.
::filesystem::path p(core_dir);
mount(p.parent_path().str(), "/libretro/core", false);
}
if (ChaiLove::environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_dir) && system_dir) {
mount(system_dir, "/libretro/system");
mount(system_dir, "/libretro/system", false);
}
if (ChaiLove::environ_cb(RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY, &assets_dir) && assets_dir) {
mount(assets_dir, "/libretro/assets");
mount(assets_dir, "/libretro/assets", false);
}
if (ChaiLove::environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &save_dir) && save_dir) {
save_dir = *save_dir ? save_dir : system_dir;
mount(save_dir, "/libretro/saves");
mount(save_dir, "/libretro/saves", false);
} else {
mount(save_dir = system_dir, "/libretro/saves");
mount(save_dir = system_dir, "/libretro/saves", false);
}
// Ensure the write directory is set to the Save Directory.
......@@ -78,7 +85,7 @@ bool filesystem::load(const std::string& file) {
}
bool filesystem::exists(const std::string& file) {
return PHYSFS_exists(file.c_str()) > 0;
return PHYSFS_exists(file.c_str()) != 0;
}
int filesystem::getSize(const std::string& file) {
......@@ -215,16 +222,22 @@ bool filesystem::unmount(const std::string& archive) {
}
bool filesystem::mount(const char *archive, const std::string& mountpoint) {
// Allow mounting character pointers safely.
if (archive == NULL) {
return false;
}
return mount(std::string(archive), mountpoint);
}
std::string filesystem::getExecutablePath() {
return std::string(PHYSFS_getBaseDir());
}
bool filesystem::mount(const std::string& archive, const std::string& mountpoint) {
return mount(archive, mountpoint, true);
}
bool filesystem::mount(const std::string& archive, const std::string& mountpoint, bool appendToPath) {
// Protect against empty archive/mount points.
if (archive.length() <= 0 || mountpoint.length() <= 0) {
int append = appendToPath ? 1 : 0;
if (archive.length() <= 0) {
std::cout << "[ChaiLove] [filesystem] Mounting failed because archive was empty." << std::endl;
return false;
}
......@@ -233,7 +246,7 @@ bool filesystem::mount(const std::string& archive, const std::string& mountpoint
// Use the simple mount method if we're mounting the root directory.
if (mountpoint == "/") {
int returnValue = PHYSFS_mount(archive.c_str(), mountpoint.c_str(), 0);
int returnValue = PHYSFS_mount(archive.c_str(), mountpoint.c_str(), append);
if (returnValue == 0) {
std::cout << "[ChaiLove] [filesystem] Error mounting /: " << getLastError() << std::endl;
return false;
......@@ -246,7 +259,7 @@ bool filesystem::mount(const std::string& archive, const std::string& mountpoint
// Mount using a handle instead, since we're doing another archive.
PHYSFS_File* file = openFile(archive);
if (file != NULL) {
if (PHYSFS_mountHandle(file, archive.c_str(), mountpoint.c_str(), 1) == 0) {
if (PHYSFS_mountHandle(file, archive.c_str(), mountpoint.c_str(), append) == 0) {
std::cout << "[ChaiLove] [filesystem] Error mounting file: " << getLastError() << std::endl;
return false;
}
......@@ -256,15 +269,12 @@ bool filesystem::mount(const std::string& archive, const std::string& mountpoint
}
// Check if we're mounting a directory.
if (isDirectory(archive)) {
int returnVal = PHYSFS_mount(archive.c_str(), mountpoint.c_str(), 0);
if (returnVal == 0) {
std::cout << "[ChaiLove] [filesystem] Error mounting directory: " << getLastError() << std::endl;
return false;
}
return true;
int returnVal = PHYSFS_mount(archive.c_str(), mountpoint.c_str(), append);
if (returnVal == 0) {
std::cout << "[ChaiLove] [filesystem] Error mounting directory: " << getLastError() << std::endl;
return false;
}
return false;
return true;
}
/**
......@@ -338,6 +348,15 @@ std::vector<std::string> filesystem::lines(const std::string& filename, const st
return strings;
}
bool filesystem::remove(const std::string& name) {
int ret = PHYSFS_delete(name.c_str());
if (ret == 0) {
std::cout << "[ChaiLove] [filesystem] Failed to remove file or directory: " << getLastError() << std::endl;
return false;
}
return true;
}
FileInfo filesystem::getInfo(const std::string& path) {
FileInfo fileInfo;
PHYSFS_Stat stat;
......@@ -369,6 +388,15 @@ FileData filesystem::newFileData(const std::string& filepath) {
return f;
}
FileData filesystem::newFileData(const std::string& contents, const std::string& name) {
FileData f(contents, name);
return f;
}
std::string filesystem::getSaveDirectory() {
return "/libretro/saves";
}
bool filesystem::createDirectory(const std::string& name) {
int ret = PHYSFS_mkdir(name.c_str());
if (ret == 0) {
......
......@@ -78,6 +78,19 @@ class filesystem {
*/
int getSize(const std::string& file);
/**
* Removes a file or empty directory.
*
* The directory must be empty before removal or else it will fail. Simply remove all files and folders in the directory beforehand.
* If the file exists in the .love but not in the save directory, it returns false as well.
* An opened File prevents removal of the underlying file. Simply close the File to remove it.
*
* @param name The file or directory to remove.
*
* @return True if the file or directory was removed, false otherwise.
*/
bool remove(const std::string& name);
/**
* Gets information about the specified file or directory.
*
......@@ -100,6 +113,18 @@ class filesystem {
*/
FileData newFileData(const std::string& filepath);
/**
* Creates a new FileData object.
*
* @param contents The contents fof the file.
* @param name The name of the file.
*
* @return Your new FileData.
*
* @see love::Types::FileSystem::FileData
*/
FileData newFileData(const std::string& contents, const std::string& name);
/**
* Unmounts a zip file or folder previously mounted with love.filesystem.mount.
*
......@@ -114,13 +139,25 @@ class filesystem {
/**
* Mounts a zip file or folder in the game's save directory for reading.
*
* @return bool True, when mounting was a success.
* @param archive The folder or zip file in the game's save directory to mount.
* @param mountpoint The new path the archive will be mounted to.
* @param appendToPath (true) Whether the archive will be searched when reading a filepath before or after already-mounted archives. This includes the game's source and save directories.
*
* @return bool True if the archive was successfully mounted, false otherwise.
*
* @see unmount
*/
bool mount(const std::string& archive, const std::string& mountpoint, bool appendToPath);
bool mount(const std::string& archive, const std::string& mountpoint);
bool mount(const char *archive, const std::string& mountpoint);
/**
* Gets the path to the designated save directory.
*
* @return The path to the save directory.
*/
std::string getSaveDirectory();
PHYSFS_sint64 getSize(PHYSFS_File* file);
PHYSFS_file* openFile(const std::string& filename);
......@@ -187,6 +224,13 @@ class filesystem {
std::vector<std::string> lines(const std::string& filename, const std::string& delimiter);
std::vector<std::string> lines(const std::string& filename);
/**
* Get the path to the executable that was used to run this application.
*
* @return The base path of the application.
*/
std::string getExecutablePath();
std::string getLastError();
};
......
......@@ -291,10 +291,15 @@ script::script(const std::string& file) {
chai.add(fun(&filesystem::isSymlink), "isSymlink");
chai.add(fun(&filesystem::isFile), "isFile");
chai.add(fun(&filesystem::write), "write");
chai.add(fun(&filesystem::remove), "remove");
chai.add(fun(&filesystem::exists), "exists");
chai.add(fun(&filesystem::getExecutablePath), "getExecutablePath");
chai.add(fun(&filesystem::getSaveDirectory), "getSaveDirectory");
chai.add(fun(&filesystem::getInfo), "getInfo");
chai.add(fun(&filesystem::newFileData), "newFileData");
chai.add(fun<FileData, filesystem, const std::string&>(&filesystem::newFileData), "newFileData");
chai.add(fun<FileData, filesystem, const std::string&, const std::string&>(&filesystem::newFileData), "newFileData");
chai.add(fun(&filesystem::getDirectoryItems), "getDirectoryItems");
chai.add(fun<bool, filesystem, const std::string&, const std::string&, bool>(&filesystem::mount), "mount");
chai.add(fun<bool, filesystem, const std::string&, const std::string&>(&filesystem::mount), "mount");
chai.add(fun<int, filesystem, const std::string&>(&filesystem::getSize), "getSize");
chai.add(fun<std::vector<std::string>, filesystem, const std::string&>(&filesystem::lines), "lines");
......
......@@ -53,7 +53,7 @@ class script {
* t.console = false
*
* // The ChaiLove version this game was made for.
* t.version = "0.26.1"
* t.version = "0.27.0"
*
* // The width and height of the game.
* t.window.width = 1024
......
......@@ -17,6 +17,7 @@ class sound {
bool hasAudio();
void unload();
void update();
~sound();
/**
......
......@@ -62,7 +62,7 @@ std::string system::getVersionString() {
return CHAILOVE_VERSION_STRING;
}
bool system::load(config& t) {
void system::updateVariables(config& t) {
// Update core option from the libretro variables.
struct retro_variable var = {0};
......@@ -85,6 +85,11 @@ bool system::load(config& t) {
t.options["highquality"] = false;
}
}
}
bool system::load(config& t) {
// Ensure we retrieve the latest variable states.
updateVariables(t);
// Load the semantic version string.
semver_t chailoveVersion = {};
......
......@@ -121,6 +121,7 @@ class system {
* @see love.system.getClipboardText
*/
system& setClipboardText(const std::string& text);
void updateVariables(config& t);
bool load(config& t);
......
/test
/createDirectoryTest
createDirectoryTest
......@@ -15,7 +15,7 @@ def conf(t) {
t.window.width = 460
t.window.height = 320
t.console = true
t.version = "0.26.1"
t.version = "0.27.0"
}
def load() {
......
......@@ -55,15 +55,28 @@ assert_equal(loadedFileData.getFilename(), "filesystem.chai", " .getFilename(
var fileDataContents = loadedFileData.getString()
var fileDataFind = fileDataContents.find("// newFileData()")
assert_greater(fileDataFind, 50, " .getString()")
var newFileData = love.filesystem.newFileData("Hello World!", "helloworld.txt")
assert_equal(newFileData.getString(), "Hello World!", " newFileData.getString()")
// getSaveDirectory()
assert_equal(love.filesystem.getSaveDirectory(), "/libretro/saves", "love.filesystem.getSaveDirectory()")
// createDirectory()
var createDirectoryReturn = love.filesystem.createDirectory("test/createDirectoryTest")
var createDirectoryReturn = love.filesystem.createDirectory("test/unittests/createDirectoryTest")
assert(createDirectoryReturn, "love.filesystem.createDirectory()")
// write()
var writeFileReturn = love.filesystem.write("test/createDirectoryTest/test.md", "# Test\n\nHello World!");
var writeFileReturn = love.filesystem.write("test/unittests/createDirectoryTest/test.md", "# Test\n\nHello World!");
assert(writeFileReturn, "love.filesystem.write()")
// remove()
assert(love.filesystem.exists("createDirectoryTest/test.md"), "love.filesystem.remove()")
assert(love.filesystem.exists("createDirectoryTest"), " directory exists")
assert(love.filesystem.remove("test/unittests/createDirectoryTest/test.md"), " remove file")
assert_not(love.filesystem.exists("createDirectoryTest/test.md"), " file is gone")
assert(love.filesystem.remove("test/unittests/createDirectoryTest"), " remove directory")
assert_not(love.filesystem.exists("createDirectoryTest"), " directory is gone")
// mount()
var mountResult = love.filesystem.mount("assets/hello.zip", "hello")
assert(mountResult, "love.filesystem.mount()")
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment