Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
105 KB
Referenced Files
None
Subscribers
None
diff --git a/util/configuration.cpp b/util/configuration.cpp
index 1d659f3c..8ca2d9b7 100644
--- a/util/configuration.cpp
+++ b/util/configuration.cpp
@@ -1,998 +1,1006 @@
#include "configuration.h"
#include "input/keyboard.h"
#include "exceptions/load_exception.h"
#include "tokenreader.h"
#include "token.h"
#include "file-system.h"
#include "input/input.h"
#include "debug.h"
#include <sstream>
#include <fstream>
#include <stdlib.h>
#include <string>
#include <map>
#ifdef USE_SDL
#if SDL_VERSION_ATLEAST(1, 3, 0)
static const std::string INPUT_TYPE = "SDL1.3";
#else
static const std::string INPUT_TYPE = "SDL";
#endif
#endif
#ifdef USE_ALLEGRO
static const std::string INPUT_TYPE = "Allegro";
#endif
#ifdef USE_ALLEGRO5
static const std::string INPUT_TYPE = "Allegro5";
#endif
bool Configuration::save = true;
Util::ReferenceCount<Token> Configuration::data;
/* text that appears in the config file */
#define define_config(n,str) static const char * config_##n = str
define_config(attack1, "attack1");
define_config(attack2, "attack2");
define_config(attack3, "attack3");
define_config(attack4, "attack4");
define_config(attack5, "attack5");
define_config(attack6, "attack6");
define_config(configuration, "configuration");
define_config(cooperative, "cooperative");
// define_config(current_game, "current-game");
define_config(down, "down");
define_config(free_for_all, "free-for-all");
define_config(fullscreen, "fullscreen");
define_config(game_speed, "game-speed");
define_config(invincible, "invincible");
define_config(jump, "jump");
define_config(quit, "quit");
define_config(keyboard_configuration, "keyboard-configuration");
define_config(input, "input");
define_config(joystick_configuration, "joystick-configuration");
define_config(left, "left");
define_config(lives, "lives");
define_config(menu_font, "menu-font");
define_config(menu_font_width, "menu-font-width");
define_config(menu_font_height, "menu-font-height");
define_config(npc_buddies, "npc-buddies");
define_config(number, "number");
define_config(play_mode, "play-mode");
define_config(right, "right");
define_config(screen_size, "screen-size");
define_config(sound, "sound");
define_config(music, "music");
define_config(up, "up");
define_config(quality_filter, "quality-filter");
define_config(fps, "frames-per-second");
define_config(language, "language");
define_config(mugen_motif, "mugen-motif");
/* version of the game: 3.3, 3.4, 4.24 */
define_config(version, "version");
#undef def_config
using namespace std;
static const int InvalidKey = 0;
static const Joystick::Key InvalidJoystick = Joystick::Invalid;
/* don't save the configuration while loading it */
class Disable{
public:
Disable(){
last = Configuration::getSave();
Configuration::disableSave();
}
~Disable(){
Configuration::setSave(last);
}
bool last;
};
/* FIXME: move this to some utils module */
static std::vector<std::string> split(std::string str, char splitter){
std::vector<std::string> strings;
size_t next = str.find(splitter);
while (next != std::string::npos){
strings.push_back(str.substr(0, next));
str = str.substr(next+1);
next = str.find(splitter);
}
if (str != ""){
strings.push_back(str);
}
return strings;
}
static std::string last(const vector<string> & what){
return what.at(what.size() - 1);
}
/* Create a token that contains the entire path.
* path = "foo/bar/baz"
* out = (foo (bar (baz)))
*/
static Token * createToken(const string & path){
vector<string> paths = split(path, '/');
Token * out = new Token();
Token * current = out;
for (vector<string>::iterator it = paths.begin(); it != paths.end(); it++){
current->addToken(new Token(*it, false));
if (paths.end() - it > 1){
Token * next = new Token();
current->addToken(next);
current = next;
}
}
return out;
}
/* Create a token with the given path and give it a value */
template <class Value>
static Token * createToken(const string & path, const Value & value){
Token * out = createToken(path);
*out << value;
return out;
}
template <class Value, class Value2>
static Token * createToken(const string & path, const Value & value, const Value2 & value2){
Token * out = createToken(path);
*out << value;
*out << value2;
return out;
}
/* remove a path if it exists */
static void removeToken(Token * data, const std::string & path){
Token * found = data->findToken(path);
/* See if the token already exists. If it does then remove it from its
* parent and add a new token to the parent with the updated value.
*/
if (found != NULL){
Token * parent = found->getParent();
parent->removeToken(found);
}
}
static void updateToken(Token * data, const std::string & path, Token * add){
if (data == NULL){
delete add;
}
Token * found = data->findToken(path);
/* See if the token already exists. If it does then remove it from its
* parent and add a new token to the parent with the updated value.
*/
if (found != NULL){
Token * parent = found->getParent();
parent->removeToken(found);
parent->addToken(add);
} else {
const vector<string> paths = split(path, '/');
Token * start = data;
for (int index = 1; index < paths.size() - 1; index++){
string where = paths[index];
Token * next = start->findToken(string("_/") + where);
if (next == NULL){
ostringstream out;
bool first = true;
for (int from = index; from < paths.size(); from++){
if (!first){
out << "/";
}
out << paths[from];
first = false;
}
/* If we run out of found paths then create a token with an empty
* value for the unfound paths we have so far and call updateToken
* again. This time the entire path will be there
* so the 'found = ...' logic will work.
*/
start->addToken(createToken(out.str(), string("")));
updateToken(data, path, add);
return;
} else {
start = next;
}
}
/* It probably shouldn't be possible to get here. If the entire
* path was there then the findToken() logic above should have worked.
*/
start->addToken(add);
}
}
template <class Value>
static void updateToken(Token * data, const string & path, const Value & value){
if (data == NULL){
return;
}
updateToken(data, path, createToken(last(split(path, '/')), value));
}
void Configuration::setProperty(const string & name, const string & value){
updateToken(getRawData(), string(config_configuration) + "/" + name, value);
saveConfiguration();
}
static Token * getPropertyX(Token * data, const std::string & path){
return data->findToken(string(config_configuration) + "/" + path);
}
template <class Out>
static Out getPropertyX(Token * data, const std::string & path, const Out & defaultValue){
if (data == NULL){
return defaultValue;
}
Out out;
if (data->match(string(config_configuration) + "/" + path, out)){
return out;
}
updateToken(data, string(config_configuration) + "/" + path, defaultValue);
return defaultValue;
}
void Configuration::setDefaultKeys(int config){
/* Set the keys token to an empty value */
ostringstream path;
path << config_input << "/" << config << "/keys";
setProperty(path.str(), createToken("keys"));
/* Call the get methods to reset the defaults */
int ignore;
ignore = getRight(config);
ignore = getLeft(config);
ignore = getUp(config);
ignore = getDown(config);
ignore = getAttack1(config);
ignore = getAttack2(config);
ignore = getAttack3(config);
ignore = getAttack4(config);
ignore = getAttack5(config);
ignore = getAttack6(config);
ignore = getJump(config);
}
/* hopefully this is only used right before setting all the values
* since the key mappings are bogus in here'
*/
Configuration::Configuration(){
}
Configuration::Configuration(const Configuration & config){
menuFont = config.menuFont;
}
Configuration & Configuration::operator=(const Configuration & config){
Disable disable;
setMenuFont(config.getMenuFont());
return *this;
}
Configuration::~Configuration(){
}
Util::ReferenceCount<Menu::FontInfo> Configuration::getMenuFont(){
return menuFont;
}
bool Configuration::hasMenuFont(){
return menuFont != NULL;
}
void Configuration::setMenuFont(const Util::ReferenceCount<Menu::FontInfo> & info){
menuFont = info;
saveConfiguration();
}
void Configuration::setMenuFontWidth(int x){
setProperty(config_menu_font_width, x);
saveConfiguration();
}
int Configuration::getMenuFontWidth(){
return getProperty(config_menu_font_width, 24);
}
void Configuration::setMenuFontHeight(int x){
setProperty(config_menu_font_height, x);
saveConfiguration();
}
int Configuration::getMenuFontHeight(){
return getProperty(config_menu_font_height, 24);
}
static string joystickKeyName(Joystick::Key key){
switch (key){
case Joystick::Up: return "up";
case Joystick::Down: return "down";
case Joystick::Left: return "left";
case Joystick::Right: return "right";
case Joystick::Button1: return "button1";
case Joystick::Button2: return "button2";
case Joystick::Button3: return "button3";
case Joystick::Button4: return "button4";
case Joystick::Button5: return "button5";
case Joystick::Button6: return "button6";
case Joystick::Start: return "start";
case Joystick::Quit: return "quit";
case Joystick::Invalid: return "invalid";
}
return "?";
}
+static string removeQuotes(string input){
+ size_t find = input.find('"');
+ while (find != string::npos){
+ input.erase(find, 1);
+ }
+ return input;
+}
+
static string joystickPath(Joystick::Key key, int config, const std::string & name){
ostringstream base;
base << config_configuration << "/" << config_input << "/" << INPUT_TYPE << "/";
base << "joystick" << "/" << config << "/";
- base << name << "/" << joystickKeyName(key);
+ base << removeQuotes(name) << "/" << joystickKeyName(key);
return base.str();
}
void Configuration::setCustomButton(Joystick::Key key, int config, const std::string & name, int button){
if (key != Joystick::Invalid){
std::string base = joystickPath(key, config, name);
removeToken(getRawData(), base);
updateToken(getRawData(), base + "/button", button);
saveConfiguration();
}
}
bool Configuration::getCustomButton(Joystick::Key key, int config, const std::string & name, int & button){
return getRawData()->match(joystickPath(key, config, name) + "/button", button);
}
bool Configuration::getCustomAxis(Joystick::Key key, int config, const std::string & name, int & stick, int & axis, double & low, double & high){
std::string base = joystickPath(key, config, name);
return getRawData()->match(base + "/stick", stick) &&
getRawData()->match(base + "/axis", axis) &&
getRawData()->match(base + "/low", low) &&
getRawData()->match(base + "/high", high);
}
void Configuration::setCustomAxis(Joystick::Key key, int config, const string & name, int stick, int axis, double low, double high){
if (key != Joystick::Invalid){
std::string base = joystickPath(key, config, name);
removeToken(getRawData(), base);
updateToken(getRawData(), base + "/stick", stick);
updateToken(getRawData(), base + "/axis", axis);
updateToken(getRawData(), base + "/low", low);
updateToken(getRawData(), base + "/high", high);
saveConfiguration();
}
}
void Configuration::setKey(int config, const string & name, int value){
if (value != InvalidKey){
ostringstream path;
path << config_configuration << "/" << config_input << "/" << config << "/keys/" << name;
updateToken(getRawData(), path.str(), value);
saveConfiguration();
}
}
void Configuration::setRight(int config, int i){
setKey(config, "right", i);
}
void Configuration::setLeft(int config, int i){
setKey(config, "left", i);
}
void Configuration::setUp(int config, int i){
setKey(config, "up", i);
}
void Configuration::setDown(int config, int i){
setKey(config, "down", i);
}
void Configuration::setAttack1(int config, int i){
setKey(config, "attack1", i);
}
void Configuration::setAttack2(int config, int i){
setKey(config, "attack2", i);
}
void Configuration::setAttack3(int config, int i){
setKey(config, "attack3", i);
}
void Configuration::setAttack4(int config, int i){
setKey(config, "attack4", i);
}
void Configuration::setAttack5(int config, int i){
setKey(config, "attack5", i);
}
void Configuration::setAttack6(int config, int i){
setKey(config, "attack6", i);
}
void Configuration::setJump(int config, int i){
setKey(config, "jump", i);
}
int Configuration::getKey(int config, const string & name, int defaultValue){
ostringstream path;
path << config_input << "/" << INPUT_TYPE << "/" << config << "/keys/" << name;
return getProperty(path.str(), defaultValue);
}
/* this nonsense is just to convert a regular integer into an enum */
/*
static Configuration::JoystickInput intToJoystick(int a){
switch (a){
case Joystick::Up : return Joystick::Up;
case Joystick::Down : return Joystick::Down;
case Joystick::Left : return Joystick::Left;
case Joystick::Right : return Joystick::Right;
case Joystick::Button1 : return Joystick::Button1;
case Joystick::Button2 : return Joystick::Button2;
case Joystick::Button3 : return Joystick::Button3;
case Joystick::Button4 : return Joystick::Button4;
case Joystick::Button5 : return Joystick::Button5;
case Joystick::Button6 : return Joystick::Button6;
case Joystick::Start : return Joystick::Start;
case Joystick::Quit : return Joystick::Quit;
}
return Joystick::Invalid;
}
*/
/*
Configuration::JoystickInput Configuration::getJoystickKey(int config, const string & name, JoystickInput defaultValue){
ostringstream path;
path << config_input << "/" << config << "/joystick/" << name;
return intToJoystick(getProperty(path.str(), defaultValue));
}
*/
int Configuration::getRight(int config){
int normal = 0;
switch (config){
case 0: normal = Keyboard::Key_RIGHT; break;
case 1: normal = Keyboard::Key_L; break;
}
return getKey(config, "right", normal);
}
int Configuration::getLeft(int config){
int normal = 0;
switch (config){
case 0: normal = Keyboard::Key_LEFT; break;
case 1: normal = Keyboard::Key_J; break;
}
return getKey(config, "left", normal);
}
int Configuration::getUp(int config){
int normal = 0;
switch (config){
case 0: normal = Keyboard::Key_UP; break;
case 1: normal = Keyboard::Key_I; break;
}
return getKey(config, "up", normal);
}
int Configuration::getDown(int config){
int normal = 0;
switch (config){
case 0: normal = Keyboard::Key_DOWN; break;
case 1: normal = Keyboard::Key_COMMA; break;
}
return getKey(config, "down", normal);
}
int Configuration::getAttack1(int config){
int normal = 0;
switch (config){
case 0: normal = Keyboard::Key_A; break;
case 1: normal = Keyboard::Key_R; break;
}
return getKey(config, "attack1", normal);
}
int Configuration::getAttack2(int config){
int normal = 0;
switch (config){
case 0: normal = Keyboard::Key_S; break;
case 1: normal = Keyboard::Key_T; break;
}
return getKey(config, "attack2", normal);
}
int Configuration::getAttack3(int config){
int normal = 0;
switch (config){
case 0: normal = Keyboard::Key_D; break;
case 1: normal = Keyboard::Key_Y; break;
}
return getKey(config, "attack3", normal);
}
int Configuration::getAttack4(int config){
int normal = 0;
switch (config){
case 0: normal = Keyboard::Key_Z; break;
case 1: normal = Keyboard::Key_F; break;
}
return getKey(config, "attack4", normal);
}
int Configuration::getAttack5(int config){
int normal = 0;
switch (config){
case 0: normal = Keyboard::Key_X; break;
case 1: normal = Keyboard::Key_G; break;
}
return getKey(config, "attack5", normal);
}
int Configuration::getAttack6(int config){
int normal = 0;
switch (config){
case 0: normal = Keyboard::Key_C; break;
case 1: normal = Keyboard::Key_H; break;
}
return getKey(config, "attack6", normal);
}
int Configuration::getJump(int config){
int normal = 0;
switch (config){
case 0: normal = Keyboard::Key_SPACE; break;
case 1: normal = Keyboard::Key_B; break;
}
return getKey(config, "jump", normal);
}
/*
void Configuration::setJoystickKey(int config, const std::string & name, const Configuration::JoystickInput & what){
if (what != Joystick::Invalid){
ostringstream path;
path << config_configuration << "/" << config_input << "/" << config << "/joystick/" << name;
updateToken(data.raw(), path.str(), what);
saveConfiguration();
}
}
void Configuration::setJoystickRight(int config, Configuration::JoystickInput i){
setJoystickKey(config, "right", i);
}
void Configuration::setJoystickLeft(int config, Configuration::JoystickInput i){
setJoystickKey(config, "left", i);
}
void Configuration::setJoystickUp(int config, Configuration::JoystickInput i){
setJoystickKey(config, "up", i);
}
void Configuration::setJoystickDown(int config, Configuration::JoystickInput i){
setJoystickKey(config, "down", i);
}
void Configuration::setJoystickAttack1(int config, Configuration::JoystickInput i){
setJoystickKey(config, "attack1", i);
}
void Configuration::setJoystickAttack2(int config, Configuration::JoystickInput i){
setJoystickKey(config, "attack2", i);
}
void Configuration::setJoystickAttack3(int config, Configuration::JoystickInput i){
setJoystickKey(config, "attack3", i);
}
void Configuration::setJoystickAttack4(int config, Configuration::JoystickInput i){
setJoystickKey(config, "attack4", i);
}
void Configuration::setJoystickAttack5(int config, Configuration::JoystickInput i){
setJoystickKey(config, "attack5", i);
}
void Configuration::setJoystickAttack6(int config, Configuration::JoystickInput i){
setJoystickKey(config, "attack6", i);
}
void Configuration::setJoystickJump(int config, Configuration::JoystickInput i){
setJoystickKey(config, "jump", i);
}
void Configuration::setJoystickQuit(int config, Configuration::JoystickInput i){
setJoystickKey(config, "quit", i);
}
void Configuration::setJoystickStart(int config, Configuration::JoystickInput i){
setJoystickKey(config, "start", i);
}
Configuration::JoystickInput Configuration::getJoystickRight(int config){
return getJoystickKey(config, "right", Joystick::Right);
}
Configuration::JoystickInput Configuration::getJoystickLeft(int config){
return getJoystickKey(config, "left", Joystick::Left);
}
Configuration::JoystickInput Configuration::getJoystickUp(int config){
return getJoystickKey(config, "up", Joystick::Up);
}
Configuration::JoystickInput Configuration::getJoystickDown(int config){
return getJoystickKey(config, "down", Joystick::Down);
}
Configuration::JoystickInput Configuration::getJoystickAttack1(int config){
return getJoystickKey(config, "attack1", Joystick::Button1);
}
Configuration::JoystickInput Configuration::getJoystickAttack2(int config){
return getJoystickKey(config, "attack2", Joystick::Button2);
}
Configuration::JoystickInput Configuration::getJoystickAttack3(int config){
return getJoystickKey(config, "attack3", Joystick::Button3);
}
Configuration::JoystickInput Configuration::getJoystickAttack4(int config){
return getJoystickKey(config, "attack4", Joystick::Button4);
}
Configuration::JoystickInput Configuration::getJoystickAttack5(int config){
return getJoystickKey(config, "attack5", Joystick::Button5);
}
Configuration::JoystickInput Configuration::getJoystickAttack6(int config){
return getJoystickKey(config, "attack6", Joystick::Button6);
}
Configuration::JoystickInput Configuration::getJoystickJump(int config){
return getJoystickKey(config, "jump", Joystick::Button4);
}
Configuration::JoystickInput Configuration::getJoystickQuit(int config){
return getJoystickKey(config, "quit", Joystick::Quit);
}
Configuration::JoystickInput Configuration::getJoystickStart(int config){
return getJoystickKey(config, "start", Joystick::Start);
}
*/
/*
string Configuration::getCurrentGame(){
return getProperty(config_current_game, string(""));
}
/ * assumes the directory is readable and has a dir/dir.txt in it * /
void Configuration::setCurrentGame(const std::string & str){
setProperty(config_current_game, str);
saveConfiguration();
}
*/
void Configuration::disableSave(){
save = false;
}
void Configuration::setSave(bool what){
save = what;
}
bool Configuration::getSave(){
return save;
}
static Token * removeDuplicates(Token * token){
/* TODO */
return token;
}
Token * Configuration::getRawData(){
if (data == NULL){
data = new Token();
}
return data.raw();
}
void Configuration::loadConfigurations(){
data = new Token();
*data << config_configuration;
try{
Filesystem::AbsolutePath file = Storage::instance().configFile();
TokenReader tr;
Token * head = tr.readTokenFromFile(file.path());
if (*head != config_configuration){
throw LoadException(__FILE__, __LINE__, string("Config file ") + Storage::instance().configFile().path() + " does not use the configuration format" );
}
/* Store the entire configuration tree */
data = removeDuplicates(head->copy());
} catch ( const LoadException & le ){
Global::debug( 0 ) << "Notice: Could not load configuration file " << Storage::instance().configFile().path() << ": " << le.getTrace() << endl;
} catch ( const TokenException & t ){
Global::debug( 0 ) << "Notice: could not open configuration file '" << Storage::instance().configFile().path() << "': " << t.getTrace() << endl;
}
}
void Configuration::saveConfiguration(){
if (!save){
return;
}
if (data != NULL){
ofstream out(Storage::instance().configFile().path().c_str(), ios::trunc | ios::out);
if (! out.bad()){
data->toString(out, string(""));
out << endl;
out.close();
}
}
}
Util::ReferenceCount<Menu::FontInfo> Configuration::menuFont;
bool Configuration::joystickEnabled = true;
int Configuration::getProperty(const std::string & path, int defaultValue){
return getPropertyX(getRawData(), path, defaultValue);
}
void Configuration::setProperty(const std::string & path, int value){
updateToken(getRawData(), string(config_configuration) + "/" + path, value);
saveConfiguration();
}
void Configuration::setProperty(const std::string & path, Token * value){
updateToken(getRawData(), string(config_configuration) + "/" + path, value);
saveConfiguration();
}
Token * Configuration::getProperty(const std::string & path){
return getPropertyX(getRawData(),path);
}
double Configuration::getProperty(const std::string & path, double defaultValue){
return getPropertyX(getRawData(), path, defaultValue);
}
void Configuration::setProperty(const std::string & path, double value){
updateToken(getRawData(), string(config_configuration) + "/" + path, value);
saveConfiguration();
}
bool Configuration::getProperty(const std::string & path, bool defaultValue){
return getPropertyX(getRawData(), path, defaultValue);
}
void Configuration::setProperty(const std::string & path, bool value){
updateToken(getRawData(), string(config_configuration) + "/" + path, value);
saveConfiguration();
}
std::string Configuration::getProperty(const std::string & path, const std::string & defaultValue){
return getPropertyX(getRawData(), path, defaultValue);
}
double Configuration::getGameSpeed(){
return getProperty(config_game_speed, 1.0);
}
void Configuration::setGameSpeed(double s){
setProperty(config_game_speed, s);
saveConfiguration();
}
bool Configuration::getInvincible(){
return getProperty(config_invincible, false);
}
void Configuration::setInvincible(bool i){
setProperty(config_invincible, i);
saveConfiguration();
}
bool Configuration::getFullscreen(){
/* Defaults for all configuration options */
#if defined(PS3) || defined(ANDROID) || defined(IPHONE)
return getProperty(config_fullscreen, true);
#else
return getProperty(config_fullscreen, false);
#endif
}
void Configuration::setFullscreen(bool f){
setProperty(config_fullscreen, f);
saveConfiguration();
}
int Configuration::getLives(){
return getProperty(config_lives, 4);
}
void Configuration::setLives(int l){
setProperty(config_lives, l);
saveConfiguration();
}
int Configuration::getNpcBuddies(){
return getProperty(config_npc_buddies, 1);
}
void Configuration::setNpcBuddies(int i){
setProperty(config_npc_buddies,i);
saveConfiguration();
}
Configuration::PlayMode Configuration::getPlayMode(){
string mode = getProperty("play-mode", string(config_cooperative));
if (mode == config_cooperative){
return Cooperative;
}
if (mode == config_free_for_all){
return FreeForAll;
}
return Cooperative;
}
void Configuration::setPlayMode(Configuration::PlayMode mode){
if (mode == Configuration::Cooperative){
setProperty("play-mode", string(config_cooperative));
} else if (mode == Configuration::FreeForAll){
setProperty("play-mode", string(config_free_for_all));
}
}
std::string Configuration::getLanguage(){
return getProperty("language", string());
}
void Configuration::setLanguage(const std::string & str){
setProperty("language", str);
saveConfiguration();
}
/* In case this is ever useful again */
/*
#ifdef MINPSPW
// default resolution for the psp is 480x272
int Configuration::screen_width = 480;
int Configuration::screen_height = 272;
#else
int Configuration::screen_width = 640;
int Configuration::screen_height = 480;
#endif
*/
#ifdef GCW0
static int DefaultWidth = 320;
static int DefaultHeight = 240;
#else
static int DefaultWidth = 640;
static int DefaultHeight = 480;
#endif
/* TODO: All the screen width/height stuff shares a lot of code. Refactor */
void Configuration::setScreenWidth(int i){
int width = DefaultWidth;
int height = DefaultHeight;
Token * screen = getPropertyX(getRawData(), config_screen_size);
if (screen != NULL){
try{
screen->view() >> width >> height;
} catch (const TokenException & fail){
}
}
width = i;
setProperty(config_screen_size, createToken(config_screen_size, width, height));
saveConfiguration();
}
int Configuration::getScreenWidth(){
int width = DefaultWidth;
int height = DefaultHeight;
Token * screen = getPropertyX(getRawData(), config_screen_size);
if (screen != NULL){
try{
screen->view() >> width >> height;
} catch (const TokenException & fail){
}
} else {
setProperty(config_screen_size, createToken(config_screen_size, width, height));
}
return width;
}
void Configuration::setScreenHeight(int i){
int width = DefaultWidth;
int height = DefaultHeight;
Token * screen = getPropertyX(getRawData(), config_screen_size);
if (screen != NULL){
try{
screen->view() >> width >> height;
} catch (const TokenException & fail){
}
}
height = i;
setProperty(config_screen_size, createToken(config_screen_size, width, height));
saveConfiguration();
}
int Configuration::getScreenHeight(){
int width = DefaultWidth;
int height = DefaultHeight;
Token * screen = getPropertyX(getRawData(), config_screen_size);
if (screen != NULL){
try{
screen->view() >> width >> height;
} catch (const TokenException & fail){
}
} else {
setProperty(config_screen_size, createToken(config_screen_size, width, height));
}
return height;
}
int Configuration::getSoundVolume(){
return getProperty(config_sound, 70);
}
void Configuration::setSoundVolume(int volume){
setProperty(config_sound, volume);
saveConfiguration();
}
int Configuration::getMusicVolume(){
return getProperty(config_music, 70);
}
void Configuration::setMusicVolume(int volume){
setProperty(config_music, volume);
saveConfiguration();
}
bool Configuration::isJoystickEnabled(){
return joystickEnabled;
}
void Configuration::setJoystickEnabled(bool enabled){
joystickEnabled = enabled;
saveConfiguration();
}
std::string Configuration::getQualityFilter(){
return getProperty(config_quality_filter, string("none"));
}
void Configuration::setQualityFilter(const std::string & filter){
setProperty(config_quality_filter, filter);
saveConfiguration();
}
int Configuration::getFps(){
return getProperty(config_fps, 50);
}
void Configuration::setFps(int what){
setProperty(config_fps, what);
saveConfiguration();
}
diff --git a/util/file-system.cpp b/util/file-system.cpp
index 8f2e41df..6249763c 100644
--- a/util/file-system.cpp
+++ b/util/file-system.cpp
@@ -1,1876 +1,1883 @@
#ifdef USE_ALLEGRO
#include <allegro.h>
/* FIXME: replace with <winalleg.h> */
#ifdef _WIN32
#define BITMAP dummyBITMAP
#include <windows.h>
#undef BITMAP
#endif
#endif
#include <algorithm>
#include "funcs.h"
#include "file-system.h"
#include "thread.h"
#include "system.h"
#include "utf.h"
// #include "globals.h"
#include <dirent.h>
#include <sstream>
#include <exception>
#include <string>
#include <fstream>
#include <ostream>
#include "token.h"
#include "zip/unzip.h"
#include "zip/ioapi.h"
#include "7z/7zFile.h"
#include "7z/7z.h"
#include "7z/7zAlloc.h"
#include "7z/7zCrc.h"
#ifndef USE_ALLEGRO
/* some sfl symbols conflict with allegro */
#include "sfl/sfl.h"
#include "sfl/sfldir.h"
#endif
#ifdef _WIN32
#define _WIN32_IE 0x400
#include <shlobj.h>
#endif
using namespace std;
/* some filesystem access can only be done by one thread at a time. specifically, sfl
* has its own allocator that is meant to be used in a single-threaded manner.
* rather than try to add locks to sfl we just wrap all sfl calls with a lock.
*
* initialize() must be called to initialize this lock
*/
// Util::Thread::Lock lock;
namespace Path{
/* remove extra path separators (/) */
string sanitize(string path){
size_t double_slash = path.find("//");
while (double_slash != string::npos){
path.erase(double_slash, 1);
double_slash = path.find("//");
}
/* Remove /./ from paths because its redundant */
size_t useless = path.find("/./");
while (useless != string::npos){
path.erase(useless, 2);
useless = path.find("/./");
}
return path;
}
static string removeEndSlashes(string path){
size_t last = path.rfind("/");
while (path.size() > 0 && last == path.size() - 1){
path.erase(last, 1);
last = path.rfind("/");
}
return path;
}
static int invert(int c){
if (c == '\\'){
return '/';
}
return c;
}
std::string invertSlashes(string str){
transform(str.begin(), str.end(), str.begin(), invert);
return str;
}
const string & Path::path() const {
return mypath;
}
const string Path::getExtension() const {
size_t dot = mypath.rfind('.');
if (dot == string::npos){
return "";
} else {
return mypath.substr(dot + 1);
}
}
bool Path::isEmpty() const {
return mypath.empty();
}
Path::~Path(){
}
Path::Path(){
}
Path::Path(const std::string & path):
mypath(sanitize(invertSlashes(path))){
}
Path::Path(const Path & path):
mypath(sanitize(invertSlashes(path.path()))){
}
RelativePath::RelativePath(){
}
RelativePath::RelativePath(const std::string & path):
Path(path){
if (! path.empty() && path[0] == '/'){
ostringstream out;
out << "Relative path '" << path << "' cannot start with a /. Only absolute paths can start with /";
throw Storage::IllegalPath(__FILE__, __LINE__, out.str());
}
}
/* a/b/c/d -> b/c/d */
std::string stripFirstDir(const std::string & str){
if (str.find("/") != std::string::npos || str.find( "\\") != std::string::npos){
std::string temp = str;
size_t rem = temp.find("/");
if (rem != std::string::npos){
return str.substr(rem+1,str.size());
}
rem = temp.find("\\");
if( rem != std::string::npos ){
return str.substr(rem+1,str.size());
}
}
return str;
}
static vector<string> splitPath(string path){
vector<string> all;
if (path.size() > 0 && path[0] == '/'){
all.push_back("/");
}
size_t found = path.find('/');
while (found != string::npos){
if (found > 0){
all.push_back(path.substr(0, found));
}
path.erase(0, found + 1);
found = path.find('/');
}
if (path.size() != 0){
all.push_back(path);
}
return all;
}
static string joinPath(const vector<string> & paths){
ostringstream out;
bool first = true;
for (vector<string>::const_iterator it = paths.begin(); it != paths.end(); it++){
if (!first){
out << '/';
}
out << *it;
first = false;
}
return out.str();
}
/* a/b/c/d -> a/b/c
* a/b/c/d/ -> a/b/c
*/
static string dirname(string path){
vector<string> paths = splitPath(path);
if (paths.size() > 1){
paths.pop_back();
return joinPath(paths);
} else if (paths.size() == 1){
if (paths[0] == "/"){
return "/";
}
return ".";
} else {
return ".";
}
/*
while (path.size() > 0 && path[path.size() - 1] == '/'){
path.erase(path.size() - 1);
}
if (path.find("/") != string::npos ||
path.find("\\") != string::npos){
size_t rem = path.find_last_of("/");
if (rem != string::npos){
return path.substr(0, rem + 1);
}
rem = path.find_last_of("\\");
if (rem != string::npos){
return path.substr(0, rem + 1);
}
}
return "";
*/
}
RelativePath::RelativePath(const RelativePath & path):
Path(path){
}
RelativePath RelativePath::removeFirstDirectory() const {
return RelativePath(stripFirstDir(path()));
}
bool RelativePath::isFile() const {
vector<string> paths = splitPath(path());
return paths.size() == 1;
}
RelativePath RelativePath::firstDirectory() const {
vector<string> paths = splitPath(path());
if (paths.size() > 1){
return RelativePath(paths[0]);
}
return RelativePath();
}
RelativePath RelativePath::getDirectory() const {
return RelativePath(dirname(path()));
}
RelativePath RelativePath::getFilename() const {
return RelativePath(stripDir(path()));
}
bool RelativePath::operator<(const RelativePath & path) const {
return this->path() < path.path();
}
bool RelativePath::operator==(const RelativePath & path) const {
return this->path() == path.path();
}
bool RelativePath::operator!=(const RelativePath & path) const {
return !(*this == path);
}
RelativePath RelativePath::join(const RelativePath & him) const {
return RelativePath(path() + "/" + him.path());
}
RelativePath & RelativePath::operator=(const RelativePath & copy){
setPath(copy.path());
return *this;
}
AbsolutePath::AbsolutePath(){
}
AbsolutePath::AbsolutePath(const std::string & path):
Path(path){
}
AbsolutePath::AbsolutePath(const AbsolutePath & path):
Path(path){
}
AbsolutePath & AbsolutePath::operator=(const AbsolutePath & copy){
setPath(copy.path());
return *this;
}
AbsolutePath AbsolutePath::removeFirstDirectory() const {
return AbsolutePath(stripFirstDir(path()));
}
bool AbsolutePath::operator==(const AbsolutePath & path) const {
return removeEndSlashes(this->path()) == removeEndSlashes(path.path());
}
bool AbsolutePath::operator!=(const AbsolutePath & path) const {
return !(*this == path);
}
bool AbsolutePath::operator<(const AbsolutePath & path) const {
return this->path() < path.path();
}
string AbsolutePath::firstDirectory() const {
vector<string> paths = splitPath(path());
if (paths.size() > 1){
return paths[0];
}
return removeEndSlashes(path());
}
bool AbsolutePath::isFile() const {
vector<string> paths = splitPath(path());
return path().find("/") == string::npos;
// paths.size() == 1;
}
RelativePath AbsolutePath::remove(const AbsolutePath & what) const {
string real = path();
real.erase(0, what.path().size());
while (real.find("/") == 0){
real.erase(0, 1);
}
return RelativePath(real);
}
AbsolutePath AbsolutePath::getDirectory() const {
return AbsolutePath(dirname(path()));
}
AbsolutePath AbsolutePath::getFilename() const {
return AbsolutePath(stripDir(path()));
}
std::string AbsolutePath::getLastComponent() const {
if (getFilename().path() == ""){
return stripDir(removeEndSlashes(path()));
}
return getFilename().path();
}
AbsolutePath AbsolutePath::join(const RelativePath & path) const {
return AbsolutePath(this->path() + "/" + path.path());
}
InsensitivePath::InsensitivePath(const Path & what):
Path(what){
}
bool InsensitivePath::operator==(const Path & path) const {
return Util::upperCaseAll(this->path()) == Util::upperCaseAll(path.path());
}
std::string removeExtension(const std::string & str){
if (str.find(".") != std::string::npos){
return str.substr(0, str.find_last_of("."));
}
return str;
}
AbsolutePath replaceExtension(const Filesystem::AbsolutePath & input, const std::string & extension){
return AbsolutePath(removeExtension(input.path()) + "." + extension);
}
/* a/b/c/d -> d */
std::string stripDir(const std::string & str){
if (str.find("/") != std::string::npos || str.find("\\") != std::string::npos){
std::string temp = str;
size_t rem = temp.find_last_of( "/" );
if (rem != std::string::npos){
return str.substr(rem+1,str.size());
}
rem = temp.find_last_of( "\\" );
if( rem != std::string::npos ){
return str.substr(rem+1,str.size());
}
}
return str;
}
/* a/b/c/d -> a/b/c/ */
std::string stripFilename(const std::string & str){
std::string temp = str;
if( str.find( "/") != std::string::npos || str.find( "\\") != std::string::npos ){
size_t rem = temp.find_last_of( "/" );
if( rem != std::string::npos ){
return str.substr(0,rem+1);
}
rem = temp.find_last_of( "\\" );
if( rem != std::string::npos ){
return str.substr(0,rem+1);
}
}
return "";
}
}
namespace Storage{
Exception::Exception(const std::string & where, int line, const std::string & file):
Exc::Base(where, line),
reason(file){
}
Exception::Exception(const std::string & where, int line, const Exc::Base & nested, const std::string & file):
Exc::Base(where, line, nested),
reason(file){
}
Exception::Exception(const Exception & copy):
Exc::Base(copy),
reason(copy.reason){
}
Exception::~Exception() throw (){
}
const std::string Exception::getReason() const {
return reason;
}
NotFound::NotFound(const std::string & where, int line, const std::string & file):
Exception(where, line, file){
}
NotFound::NotFound(const std::string & where, int line, const Exc::Base & nested, const std::string & file):
Exception(where, line, nested, file){
}
NotFound::NotFound(const NotFound & copy):
Exception(copy){
}
NotFound::~NotFound() throw (){
}
IllegalPath::IllegalPath(const std::string & where, int line, const std::string & file):
Exception(where, line, file){
}
IllegalPath::IllegalPath(const std::string & where, int line, const Exc::Base & nested, const std::string & file):
Exception(where, line, nested, file){
}
IllegalPath::IllegalPath(const IllegalPath & copy):
Exception(copy){
}
IllegalPath::~IllegalPath() throw(){
}
System::System(){
}
System::~System(){
}
vector<Filesystem::AbsolutePath> System::getFiles(const Filesystem::AbsolutePath & dataPath, const Filesystem::RelativePath & find, bool caseInsensitive){
if (find.isFile()){
return getFiles(dataPath, find.path(), caseInsensitive);
}
/* split the path into its consituent parts
* a/b/c -> a/b and c
* search for a/b, then search for c in the results
*/
Filesystem::RelativePath directory = find.getDirectory();
Filesystem::RelativePath file = find.getFilename();
vector<Filesystem::AbsolutePath> more = getFiles(dataPath, directory, caseInsensitive);
vector<Filesystem::AbsolutePath> out;
for (vector<Filesystem::AbsolutePath>::iterator it = more.begin(); it != more.end(); it++){
Filesystem::AbsolutePath path = *it;
/* if its not a directory then we can't keep searching */
if (::System::isDirectory(path.path())){
vector<Filesystem::AbsolutePath> findMore = getFiles(path, file, caseInsensitive);
out.insert(out.end(), findMore.begin(), findMore.end());
}
}
return out;
}
Filesystem::AbsolutePath System::findContainer(const RelativePath & dataPath){
try{
return find(RelativePath(dataPath.path() + ".zip"));
} catch (const NotFound & fail){
}
try{
return find(RelativePath(dataPath.path() + ".7z"));
} catch (const NotFound & fail){
}
throw NotFound(dataPath.path(), __LINE__, __FILE__);
}
vector<Filesystem::AbsolutePath> System::getContainerFilesRecursive(const Filesystem::AbsolutePath & dataPath){
vector<Filesystem::AbsolutePath> out;
vector<Filesystem::AbsolutePath> zips = getFilesRecursive(dataPath, "*.zip");
out.insert(out.end(), zips.begin(), zips.end());
zips = getFilesRecursive(dataPath, "*.7z");
out.insert(out.end(), zips.begin(), zips.end());
return out;
}
vector<Path::AbsolutePath> System::getContainerFiles(const AbsolutePath & dataPath){
vector<Filesystem::AbsolutePath> out;
vector<Filesystem::AbsolutePath> zips = getFiles(dataPath, "*.zip");
out.insert(out.end(), zips.begin(), zips.end());
zips = getFiles(dataPath, "*.7z");
out.insert(out.end(), zips.begin(), zips.end());
return out;
}
vector<Filesystem::AbsolutePath> System::getContainerFiles(const RelativePath & path){
vector<Filesystem::AbsolutePath> out;
vector<Filesystem::AbsolutePath> zips = getFiles(path, Filesystem::RelativePath("*.zip"), false);
out.insert(out.end(), zips.begin(), zips.end());
zips = getFiles(path, Filesystem::RelativePath("*.7z"), false);
out.insert(out.end(), zips.begin(), zips.end());
return out;
}
static Util::ReferenceCount<System> self;
System & instance(){
if (self != NULL){
return *self;
}
self = new Filesystem(Util::getDataPath2());
return *self;
}
File::File(){
}
File::~File(){
}
class NormalFile: public File {
public:
NormalFile(const Path::AbsolutePath & path, Access mode = Read):
path(path){
ios_base::openmode iosMode = fstream::in;
switch (mode){
case Read: iosMode = fstream::in; break;
case Write: iosMode = fstream::out; break;
case ReadWrite: iosMode = fstream::in | fstream::out; break;
}
in.open(path.path().c_str(), iosMode | fstream::binary);
in >> noskipws;
}
Token * location(){
Token * head = new Token();
*head << "file";
*head << path.path();
return head;
}
long getModificationTime(){
return ::System::getModificationTime(path.path());
}
bool canStream(){
return true;
}
long tell(){
return in.tellg();
}
void reset(){
in.clear();
}
off_t seek(off_t position, int whence){
switch (whence){
case SEEK_SET: in.seekg(position, ios::beg); break;
case SEEK_CUR: in.seekg(position, ios::cur); break;
case SEEK_END: in.seekg(position, ios::end); break;
}
return in.tellg();
}
bool eof(){
return in.eof();
}
int getSize(){
streampos here = in.tellg();
in.seekg(0, ios::end);
int length = in.tellg();
in.seekg(here, ios::beg);
return length;
}
bool good(){
return in.good();
}
File & operator>>(unsigned char & c){
in >> c;
return *this;
}
int readLine(char * output, int size){
in.read(output, size);
return in.gcount();
}
~NormalFile(){
in.close();
}
protected:
const Path::AbsolutePath path;
std::fstream in;
};
StringFile::StringFile(const std::string & start):
data(start),
stream(start){
stream >> noskipws;
}
Token * StringFile::location(){
Token * head = new Token();
*head << "<string file>";
return head;
}
long StringFile::getModificationTime(){
/* FIXME: maybe return INT_MAX or something? */
return 0;
}
void StringFile::reset(){
/* TODO or nothing..? */
}
int StringFile::readLine(char * output, int size){
stream.read(output, size);
return stream.gcount();
}
bool StringFile::canStream(){
return true;
}
int StringFile::getSize(){
return data.size();
}
long StringFile::tell(){
return stream.tellg();
}
off_t StringFile::seek(off_t position, int whence){
switch (whence){
case SEEK_SET: stream.seekg(position, ios::beg); break;
case SEEK_CUR: stream.seekg(position, ios::cur); break;
case SEEK_END: stream.seekg(position, ios::end); break;
}
return stream.tellg();
}
bool StringFile::eof(){
return stream.eof();
}
bool StringFile::good(){
return stream.good();
}
File & StringFile::operator>>(unsigned char & c){
stream >> c;
return *this;
}
StringFile::~StringFile(){
}
/* For 7z */
class LzmaContainer{
public:
LzmaContainer(const string & path, const Filesystem::AbsolutePath & start):
path(path),
start(start){
allocator.Alloc = SzAlloc;
allocator.Free = SzFree;
allocatorTemporary.Alloc = SzAllocTemp;
allocatorTemporary.Free = SzFreeTemp;
if (InFile_Open(&archiveStream.file, path.c_str())){
/* Error */
}
FileInStream_CreateVTable(&archiveStream);
LookToRead_CreateVTable(&lookStream, False);
lookStream.realStream = &archiveStream.s;
LookToRead_Init(&lookStream);
CrcGenerateTable();
SzArEx_Init(&database);
int ok = SzArEx_Open(&database, &lookStream.s, &allocator, &allocatorTemporary);
if (ok == SZ_OK){
/* Can read files */
UInt16 * name = NULL;
int tempSize = 0;
for (int index = 0; index < database.db.NumFiles; index++){
size_t offset = 0;
size_t outSizeProcessed = 0;
const CSzFileItem * file = database.db.Files + index;
size_t length;
if (file->IsDir){
continue;
}
length = SzArEx_GetFileNameUtf16(&database, index, NULL);
if (length > tempSize){
delete[] name;
name = new UInt16[length];
memset(name, 0, length);
}
SzArEx_GetFileNameUtf16(&database, index, name);
files[Utf::utf16_to_utf8(name)] = index;
}
delete[] name;
}
}
void readFile(const Path::AbsolutePath & path, unsigned char ** buffer, size_t * size){
Path::RelativePath find(path.remove(start));
*buffer = 0;
*size = 0;
if (files.find(find.path()) != files.end()){
int index = files[find.path()];
UInt32 block = 0;
size_t offset = 0;
size_t processed = 0;
int ok = SzArEx_Extract(&database, &lookStream.s, index,
&block, buffer, size, &offset, &processed,
&allocator, &allocatorTemporary);
if (ok != SZ_OK){
Global::debug(0) << "Could not read file from 7z archive: " << path.path() << endl;
return;
}
if (offset != 0){
memmove(*buffer, *buffer + offset, processed);
}
/* The original size is the size of the block buffer, but we only care
* about the size of the actual file.
*/
*size = processed;
}
}
vector<string> getFiles(){
vector<string> names;
for (map<string, int>::iterator it = files.begin(); it != files.end(); it++){
names.push_back(it->first);
}
return names;
}
string getPath() const {
return path;
}
string getMount() const {
return start.path();
}
long getModificationTime(const Path::AbsolutePath & path){
if (files.find(path.path()) != files.end()){
int index = files[path.path()];
const CSzFileItem * file = database.db.Files + index;
if (file->MTimeDefined){
CNtfsFileTime time = file->MTime;
/* Divide by 10 million here? */
return (time.Low | ((UInt64)time.High << 32)) / 10000000;
/*
unsigned year, mon, day, hour, min, sec;
UInt64 v64 = (time.Low | ((UInt64)time.High << 32)) / 10000000;
Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
unsigned t;
UInt32 v;
sec = (unsigned)(v64 % 60); v64 /= 60;
min = (unsigned)(v64 % 60); v64 /= 60;
hour = (unsigned)(v64 % 24); v64 /= 24;
v = (UInt32)v64;
const int PERIOD_4 = (4 * 365 + 1);
const int PERIOD_100 = (PERIOD_4 * 25 - 1);
const int PERIOD_400 = (PERIOD_100 * 4 + 1);
year = (unsigned)(1601 + v / PERIOD_400 * 400);
v %= PERIOD_400;
t = v / PERIOD_100; if (t == 4) t = 3; year += t * 100; v -= t * PERIOD_100;
t = v / PERIOD_4; if (t == 25) t = 24; year += t * 4; v -= t * PERIOD_4;
t = v / 365; if (t == 4) t = 3; year += t; v -= t * 365;
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)){
ms[1] = 29;
}
for (mon = 1; mon <= 12; mon++){
unsigned s = ms[mon - 1];
if (v < s)
break;
v -= s;
}
day = (unsigned)v + 1;
struct tm outTime;
memset(&outTime, 0, sizeof(outTime));
outTime.tm_sec = sec;
outTime.tm_min = min;
outTime.tm_hour = hour;
outTime.tm_mday = day;
outTime.tm_mon = mon;
/ * tm_year The number of years since 1900. * /
outTime.tm_year = year - 1900;
outTime.tm_isdst = -1;
return mktime(&outTime);
*/
}
}
return 0;
}
virtual ~LzmaContainer(){
SzArEx_Free(&database, &allocator);
File_Close(&archiveStream.file);
}
string path;
Path::AbsolutePath start;
CFileInStream archiveStream;
CLookToRead lookStream;
CSzArEx database;
ISzAlloc allocator;
ISzAlloc allocatorTemporary;
/* Map the filename to its index in the 7z database */
map<string, int> files;
};
/* overlays:
* add x/y.zip
* y.zip contains
* example.txt
* try to read x/example.txt, goto y.zip
* or y.zip could contain y/
*
* z.zip contains
* example.txt
*
* how can you prevent two zip files from providing the same files?
*/
class ZipContainer{
public:
ZipContainer(const string & path, const Filesystem::AbsolutePath & start):
path(path),
start(start),
locked(false){
zipFile = unzOpen(path.c_str());
if (zipFile == NULL){
throw Exception(__FILE__, __LINE__, "Could not open zip file");
}
if (unzGoToFirstFile(zipFile) != UNZ_OK){
throw Exception(__FILE__, __LINE__, "Could not get to first file");
}
do{
char filename[1024];
unz_file_info fileInfo;
unzGetCurrentFileInfo(zipFile, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0);
files.push_back(string(filename));
} while (unzGoToNextFile(zipFile) != UNZ_END_OF_LIST_OF_FILE);
/*
zlib_filefunc64_32_def functions;
functions.open_file_func = __real_open;
functions.tell_file_func = __real_lseek;
functions.seek_file_func = __real_lseek;
*/
/*
unzFile zip = unzOpen(path.c_str());
unz_global_info info;
if (unzGetGlobalInfo(zip, &info) == UNZ_OK){
Global::debug(0) << "Entries: " << info.number_entry << std::endl;
}
if (unzGoToFirstFile(zip) != UNZ_OK){
throw Exception(__FILE__, __LINE__, "Could not get to first file");
}
do{
char filename[1024];
unz_file_info fileInfo;
unzGetCurrentFileInfo(zip, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0);
Global::debug(0) << "Got file " << filename << std::endl;
Global::debug(0) << " Compressed " << fileInfo.compressed_size << " uncompressed " << fileInfo.uncompressed_size << std::endl;
} while (unzGoToNextFile(zip) != UNZ_END_OF_LIST_OF_FILE);
unzClose(zip);
*/
}
~ZipContainer(){
if (zipFile != NULL){
unzClose(zipFile);
}
}
string getPath() const {
return path;
}
string getMount() const {
return start.path();
}
long modificationTime(){
char filename[1024];
unz_file_info fileInfo;
unzGetCurrentFileInfo(zipFile, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0);
struct tm outTime;
memset(&outTime, 0, sizeof(outTime));
outTime.tm_sec = fileInfo.tmu_date.tm_sec;
outTime.tm_min = fileInfo.tmu_date.tm_min;
outTime.tm_hour = fileInfo.tmu_date.tm_hour;
outTime.tm_mday = fileInfo.tmu_date.tm_mday;
outTime.tm_mon = fileInfo.tmu_date.tm_mon;
/* tm_year The number of years since 1900. */
outTime.tm_year = fileInfo.tmu_date.tm_year - 1900;
outTime.tm_isdst = -1;
return mktime(&outTime);
}
void findFile(const Path::AbsolutePath & file){
Path::RelativePath find(file.remove(start));
if (unzLocateFile(zipFile, find.path().c_str(), 2) != UNZ_OK){
Global::debug(0) << "Could not find " << find.path() << std::endl;
} else {
Global::debug(1) << "Found " << find.path() << " in zip file " << path << std::endl;
}
}
int currentFileSize(){
char filename[1024];
unz_file_info fileInfo;
unzGetCurrentFileInfo(zipFile, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0);
return fileInfo.uncompressed_size;
}
int read(char * buffer, int size){
// Global::debug(0) << "offset before read " << unzGetOffset(zipFile) << " tell " << unztell(zipFile) << std::endl;
int got = unzReadCurrentFile(zipFile, buffer, size);
// Global::debug(0) << "offset after read " << unzGetOffset(zipFile) << " tell " << unztell(zipFile) << std::endl;
if (got <= 0){
throw Exception(__FILE__, __LINE__, "Could not read bytes from zip");
}
return got;
}
long tell(){
return unztell(zipFile);
}
void open(const Path::AbsolutePath & file){
if (locked){
std::ostringstream out;
char filename[1024];
unz_file_info fileInfo;
unzGetCurrentFileInfo(zipFile, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0);
out << "Could not open zip file entry " << file.path() << " because a zip file is already open: " << filename;
throw Exception(__FILE__, __LINE__, out.str());
}
findFile(file);
if (unzOpenCurrentFile(zipFile) != UNZ_OK){
std::ostringstream out;
out << "Could not open zip file entry " << file.path();
throw Exception(__FILE__, __LINE__, out.str());
}
locked = true;
}
void close(){
unzCloseCurrentFile(zipFile);
locked = false;
}
vector<string> getFiles() const {
return files;
}
protected:
/* Path to the zip file itself */
const string path;
/* Where overlay starts */
const Path::AbsolutePath start;
unzFile zipFile;
vector<string> files;
/* Only one file can be opened at a time, so if another file is opened
* while locked=true then throw an error
*/
bool locked;
};
class ZipFile: public File {
public:
ZipFile(const Path::AbsolutePath & path, const Util::ReferenceCount<ZipContainer> & zip):
path(path),
zip(zip),
atEof(false),
position(0){
zip->open(path);
}
virtual ~ZipFile(){
zip->close();
}
long getModificationTime(){
return zip->modificationTime();
}
Token * location(){
Token * head = new Token();
*head << "container";
/* container zipfile mount-point file-inside-zip */
*head << zip->getPath();
*head << zip->getMount();
*head << path.path();
return head;
}
off_t seek(off_t where, int whence){
/* It seems that minizip is not capable of seeking in a specific file in a zip
* container so we have to re-open the file and read `position' bytes to
* emulate the seek behavior.
*/
switch (whence){
case SEEK_SET: {
if (where >= position){
/* Read bytes until we hit the offset */
position += skipBytes(where - position);
} else {
zip->close();
zip->open(path);
/* re-open file and read where bytes */
position = skipBytes(where);
}
break;
}
case SEEK_CUR: {
position += skipBytes(where);
break;
}
case SEEK_END: {
return seek(getSize() + where, SEEK_SET);
break;
}
}
if (position > getSize()){
position = getSize();
}
atEof = position == getSize();
return position;
}
bool eof(){
return atEof;
}
long tell(){
return zip->tell();
}
void reset(){
/* TODO or nothing..? */
}
bool canStream(){
return false;
}
bool good(){
/* FIXME */
return true;
}
File & operator>>(unsigned char & c){
readLine((char*) &c, 1);
return *this;
}
int getSize(){
return zip->currentFileSize();
}
int readLine(char * output, int size){
try{
int read = zip->read(output, size);
position += read;
return read;
} catch (const Exception & nomore){
atEof = true;
return 0;
}
}
protected:
/* skips `bytes'.
* returns number of bytes skipped (may be less than bytes)
*/
int skipBytes(int bytes){
char dummy[1024];
int total = 0;
while (bytes > 0){
/* Calls the zip file directly here so we don't mess up position */
int read = zip->read(dummy, bytes > sizeof(dummy) ? sizeof(dummy) : bytes);
total += read;
if (read == 0){
return total;
}
bytes -= read;
}
return total;
}
const Path::AbsolutePath path;
const Util::ReferenceCount<ZipContainer> zip;
bool atEof;
/* keep track of bytes read so we can seek easier */
int position;
};
Descriptor::Descriptor(){
}
Descriptor::~Descriptor(){
}
class ZipDescriptor: public Descriptor {
public:
ZipDescriptor(const Path::AbsolutePath & path, const Util::ReferenceCount<ZipContainer> & container):
path(path),
container(container){
}
Path::AbsolutePath path;
Util::ReferenceCount<ZipContainer> container;
using Descriptor::open;
virtual Util::ReferenceCount<File> open(File::Access mode){
return Util::ReferenceCount<File>(new ZipFile(path, container));
}
virtual ~ZipDescriptor(){
}
};
/* Extracts an entire file from a 7z archive into memory */
class LzmaFile: public File {
public:
LzmaFile(const Path::AbsolutePath & path, const Util::ReferenceCount<LzmaContainer> & container):
path(path),
container(container),
position(0),
memory(NULL),
size(0){
container->readFile(path, &memory, &size);
}
virtual ~LzmaFile(){
SzFree(NULL, memory);
}
bool eof(){
return position >= size;
}
virtual bool good(){
return memory != NULL;
}
virtual int getSize(){
return size;
}
virtual bool canStream(){
return true;
}
virtual void reset(){
/* nothing */
}
virtual long tell(){
return position;
}
virtual Token * location(){
Token * head = new Token();
*head << "container";
/* container zipfile mount-point file-inside-zip */
*head << container->getPath();
*head << container->getMount();
*head << path.path();
return head;
}
virtual long getModificationTime(){
return container->getModificationTime(path);
}
virtual off_t seek(off_t position, int whence){
switch (whence){
case SEEK_SET: this->position = position; break;
case SEEK_CUR: this->position += position; break;
case SEEK_END: this->position = this->size + position; break;
}
if (this->position < 0){
this->position = 0;
}
if (this->position > size){
this->position = size;
}
return this->position;
}
virtual File & operator>>(unsigned char & out){
if (this->position < size){
out = memory[position];
position += 1;
}
return *this;
}
virtual int readLine(char * output, int size){
if (size > (this->size - this->position)){
size = this->size - this->position;
}
memmove(output, memory + this->position, size);
this->position += size;
return size;
}
protected:
const Path::AbsolutePath path;
const Util::ReferenceCount<LzmaContainer> container;
/* keep track of bytes read so we can seek easier */
int position;
unsigned char * memory;
size_t size;
};
class LzmaDescriptor: public Descriptor {
public:
LzmaDescriptor(const Path::AbsolutePath & path, const Util::ReferenceCount<LzmaContainer> & container):
path(path),
container(container){
}
Path::AbsolutePath path;
Util::ReferenceCount<LzmaContainer> container;
using Descriptor::open;
virtual Util::ReferenceCount<File> open(File::Access mode){
return Util::ReferenceCount<File>(new LzmaFile(path, container));
}
virtual ~LzmaDescriptor(){
}
};
/* Keep this updated with all the supported container types */
bool isContainer(const Path::AbsolutePath & path){
return path.getExtension() == "zip" ||
path.getExtension() == "7z";
}
+
+vector<std::string> containerTypes(){
+ vector<string> types;
+ types.push_back("zip");
+ types.push_back("7z");
+ return types;
+}
bool System::isDirectory(const AbsolutePath & path){
return virtualDirectory.isDirectory(path) || systemIsDirectory(path);
}
bool System::exists(const AbsolutePath & path){
return virtualDirectory.exists(path) || systemExists(path);
}
void System::overlayFile(const AbsolutePath & where, Util::ReferenceCount<LzmaContainer> container){
virtualDirectory.addFile(where, Util::ReferenceCount<LzmaDescriptor>(new LzmaDescriptor(where, container)).convert<Descriptor>());
}
void System::overlayFile(const AbsolutePath & where, Util::ReferenceCount<ZipContainer> zip){
virtualDirectory.addFile(where, Util::ReferenceCount<ZipDescriptor>(new ZipDescriptor(where, zip)).convert<Descriptor>());
}
void System::unoverlayFile(const AbsolutePath & where){
virtualDirectory.removeFile(where);
}
vector<string> System::containerFileList(const AbsolutePath & container){
Util::ReferenceCount<ZipContainer> zip(new ZipContainer(container.path(), Filesystem::AbsolutePath()));
return zip->getFiles();
}
static bool isZipFile(const Filesystem::AbsolutePath & path){
return path.path().find(".zip") != string::npos;
}
static bool is7zFile(const Filesystem::AbsolutePath & path){
return path.path().find(".7z") != string::npos;
}
template <class Container>
static void addOverlayFiles(System & system, const Filesystem::AbsolutePath & where, const Util::ReferenceCount<Container> & container){
vector<string> files = container->getFiles();
for (vector<string>::const_iterator it = files.begin(); it != files.end(); it++){
string path = *it;
Global::debug(1) << "Add overlay to " << where.join(Filesystem::RelativePath(path)).path() << std::endl;
system.overlayFile(where.join(Filesystem::RelativePath(path)), container);
}
}
void System::addOverlay(const AbsolutePath & container, const AbsolutePath & where){
if (isZipFile(container)){
Global::debug(1) << "Opening zip file " << container.path() << std::endl;
addOverlayFiles(*this, where, Util::ReferenceCount<ZipContainer>(new ZipContainer(container.path(), where)));
} else if (is7zFile(container)){
addOverlayFiles(*this, where, Util::ReferenceCount<LzmaContainer>(new LzmaContainer(container.path(), where)));
}
}
void System::removeOverlay(const AbsolutePath & container, const AbsolutePath & where){
Util::ReferenceCount<ZipContainer> zip(new ZipContainer(container.path(), where));
vector<string> files = zip->getFiles();
for (vector<string>::const_iterator it = files.begin(); it != files.end(); it++){
string path = *it;
Global::debug(1) << "Remove overlay from " << where.join(Filesystem::RelativePath(path)).path() << std::endl;
unoverlayFile(where.join(Filesystem::RelativePath(path)));
}
}
Util::ReferenceCount<File> System::open(const AbsolutePath & path, File::Access mode){
Util::ReferenceCount<Descriptor> descriptor = virtualDirectory.lookup(path);
if (descriptor != NULL){
return descriptor->open(mode);
} else {
return Util::ReferenceCount<File>(new NormalFile(path, mode));
}
}
std::string readFile(const Path::AbsolutePath & path){
Util::ReferenceCount<Storage::File> file = Storage::instance().open(path);
if (file != NULL){
char * data = new char[file->getSize() + 1];
file->readLine(data, file->getSize());
data[file->getSize()] = 0;
std::string out(data);
delete[] data;
return out;
}
std::ostringstream out;
out << "Could not open file " << path.path();
throw Exception(__FILE__, __LINE__, out.str());
}
bool hasInstance(){
// return true;
return self != NULL;
}
System & setInstance(const Util::ReferenceCount<System> & what){
self = what;
return *self;
}
/* will read upto 'length' bytes unless a null byte is seen first */
string EndianReader::readStringX(int length){
ostringstream out;
uint8_t letter = readByte1();
while (letter != 0 && length > 0){
out << letter;
letter = readByte1();
length -= 1;
}
return out.str();
}
/* unconditionally reads 'length' bytes */
std::string EndianReader::readString2(int length){
ostringstream out;
vector<uint8_t> bytes = readBytes(length);
for (vector<uint8_t>::iterator it = bytes.begin(); it != bytes.end(); it++){
char byte = *it;
if (byte == 0){
break;
}
out << *it;
}
return out.str();
}
void EndianReader::seekEnd(streamoff where){
internal->seekEnd(where);
}
void EndianReader::seek(streampos where){
internal->seek(where);
}
int EndianReader::position(){
return internal->tell();
}
void EndianReader::readBytes(uint8_t * out, int length){
internal->read((char*) out, length);
}
vector<uint8_t> EndianReader::readBytes(int length){
vector<uint8_t> bytes;
for (int i = 0; i < length; i++){
uint8_t byte = 0;
internal->read((char*) &byte, 1);
if (internal->eof()){
throw Eof();
} else {
}
bytes.push_back(byte);
}
return bytes;
}
EndianReader::Internal::Internal(){
}
EndianReader::Internal::~Internal(){
}
bool EndianReader::StreamInternal::eof(){
return stream.eof();
}
int EndianReader::StreamInternal::read(char * data, int length){
stream.read(data, length);
return stream.gcount();
}
void EndianReader::StreamInternal::seekEnd(std::streamoff where){
stream.seekg(where, ios::end);
}
void EndianReader::StreamInternal::seek(std::streamoff where){
stream.seekg(where);
}
int EndianReader::StreamInternal::tell(){
return stream.tellg();
}
EndianReader::StreamInternal::~StreamInternal(){
}
bool EndianReader::FileInternal::eof(){
return file->eof();
}
void EndianReader::FileInternal::seekEnd(std::streamoff where){
file->seek(where, SEEK_END);
}
void EndianReader::FileInternal::seek(std::streamoff where){
file->seek(where, SEEK_SET);
}
int EndianReader::FileInternal::read(char * data, int length){
return file->readLine(data, length);
}
int EndianReader::FileInternal::tell(){
return file->tell();
}
EndianReader::FileInternal::~FileInternal(){
}
}
Filesystem::Filesystem(const AbsolutePath & path):
dataPath(path){
}
#ifdef _WIN32
Filesystem::AbsolutePath Filesystem::userDirectory(){
ostringstream str;
char path[MAX_PATH];
SHGetSpecialFolderPathA(0, path, CSIDL_APPDATA, false);
str << path << "/paintown/";
return Filesystem::AbsolutePath(str.str());
}
Filesystem::AbsolutePath Filesystem::configFile(){
ostringstream str;
char path[MAX_PATH];
SHGetSpecialFolderPathA(0, path, CSIDL_APPDATA, false);
str << path << "/paintown_configuration.txt";
return Filesystem::AbsolutePath(str.str());
}
#else
Filesystem::AbsolutePath Filesystem::configFile(){
ostringstream str;
/* what if HOME isn't set? */
str << getenv("HOME") << "/.paintownrc";
return Filesystem::AbsolutePath(str.str());
}
Filesystem::AbsolutePath Filesystem::userDirectory(){
ostringstream str;
char * home = getenv("HOME");
if (home == NULL){
str << "/tmp/paintown";
} else {
str << home << "/.paintown/";
}
return Filesystem::AbsolutePath(str.str());
}
#endif
Filesystem::AbsolutePath Filesystem::lookup(const RelativePath path){
vector<Filesystem::AbsolutePath> places;
#define push(x) try{ places.push_back(x); } catch (const Storage::IllegalPath & fail){ }
push(dataPath.join(path));
push(userDirectory().join(path));
push(Filesystem::AbsolutePath(path.path()));
#undef push
/* start error stuff early */
ostringstream out;
out << "Cannot find " << path.path() << ". I looked in ";
bool first = true;
for (vector<Filesystem::AbsolutePath>::iterator it = places.begin(); it != places.end(); it++){
const Filesystem::AbsolutePath & final = *it;
if (exists(final)){
return final;
}
if (!first){
out << ", ";
} else {
first = false;
}
out << "'" << final.path() << "'";
}
// out << "Cannot find " << path.path() << ". I looked in '" << dataPath.join(path).path() << "', '" << userDirectory().join(path).path() << "', and '" << path.path() << "'";
throw NotFound(__FILE__, __LINE__, out.str());
#if 0
/* first try the main data directory */
Filesystem::AbsolutePath final = dataPath.join(path);
if (::System::readable(final.path())){
return final;
}
/* then try the user directory, like ~/.paintown */
final = userDirectory().join(path);
if (::System::readable(final.path())){
return final;
}
/* then just look in the cwd */
if (::System::readable(path.path())){
return Filesystem::AbsolutePath(path.path());
}
ostringstream out;
out << "Cannot find " << path.path() << ". I looked in '" << dataPath.join(path).path() << "', '" << userDirectory().join(path).path() << "', and '" << path.path() << "'";
throw NotFound(__FILE__, __LINE__, out.str());
#endif
}
Filesystem::AbsolutePath Filesystem::lookupInsensitive(const Filesystem::AbsolutePath & directory, const Filesystem::RelativePath & path){
if (path.path() == ""){
throw NotFound(__FILE__, __LINE__, "Given empty path to lookup");
}
if (path.path() == "."){
return directory;
}
if (path.path() == ".."){
return directory.getDirectory();
}
if (path.isFile()){
vector<AbsolutePath> all = getFiles(directory, "*", true);
for (vector<AbsolutePath>::iterator it = all.begin(); it != all.end(); it++){
AbsolutePath & check = *it;
if (InsensitivePath(check.getFilename()) == path){
return check;
}
}
ostringstream out;
out << "Cannot find " << path.path() << " in " << directory.path();
throw NotFound(__FILE__, __LINE__, out.str());
} else {
return lookupInsensitive(lookupInsensitive(directory, path.firstDirectory()), path.removeFirstDirectory());
}
}
vector<Filesystem::AbsolutePath> Filesystem::findDirectoriesIn(const Filesystem::AbsolutePath & path){
vector<Filesystem::AbsolutePath> dirs = virtualDirectory.findDirectories(path, "*", false);
DIR * dir = opendir(path.path().c_str());
if (dir == NULL){
return dirs;
}
struct dirent * entry = readdir(dir);
while (entry != NULL){
if (string(entry->d_name) != "." && string(entry->d_name) != ".."){
string total = path.path() + "/" + entry->d_name;
if (::System::isDirectory(total)){
dirs.push_back(AbsolutePath(total));
}
}
entry = readdir(dir);
}
closedir(dir);
return dirs;
}
vector<Filesystem::AbsolutePath> Filesystem::findDirectories(const RelativePath & path){
typedef vector<AbsolutePath> Paths;
Paths dirs;
Paths main_dirs = findDirectoriesIn(dataPath.join(path));
Paths user_dirs = findDirectoriesIn(userDirectory().join(path));
Paths here_dirs = findDirectoriesIn(Filesystem::AbsolutePath(path.path()));
dirs.insert(dirs.end(), main_dirs.begin(), main_dirs.end());
dirs.insert(dirs.end(), user_dirs.begin(), user_dirs.end());
dirs.insert(dirs.end(), here_dirs.begin(), here_dirs.end());
return dirs;
}
/* a/b/c/ -> a/b/c */
static string removeTrailingSlash(string str){
while (str.size() > 0 && str[str.size() - 1] == '/'){
str = str.erase(str.size() - 1);
}
return str;
}
vector<Filesystem::AbsolutePath> Filesystem::getFiles(const AbsolutePath & dataPath, const string & find, bool caseInsensitive){
#ifdef USE_ALLEGRO
struct al_ffblk info;
vector<AbsolutePath> files;
if (al_findfirst((dataPath.path() + "/" + find).c_str(), &info, FA_ALL ) != 0){
return files;
}
files.push_back(AbsolutePath(dataPath.path() + "/" + string(info.name)));
while ( al_findnext( &info ) == 0 ){
files.push_back(AbsolutePath(dataPath.path() + "/" + string(info.name)));
}
al_findclose( &info );
return files;
#else
Util::Thread::ScopedLock scoped(lock);
vector<AbsolutePath> files;
vector<AbsolutePath> more = virtualDirectory.findFiles(dataPath, find, caseInsensitive);
files.insert(files.end(), more.begin(), more.end());
DIRST sflEntry;
// bool ok = open_dir(&sflEntry, removeTrailingSlash(dataPath.path()).c_str());
bool ok = open_dir(&sflEntry, dataPath.path().c_str());
if (!ok){
/* sfldir.c claims that you have to call close_dir even if
* open_dir fails but close_dir will do ASSERT(dir->dir_handle) which is sometimes
* NULL when open_dir fails so we first check if the dir_handle is non-NULL and
* then call close_dir.
*/
if (sflEntry._dir_handle != NULL){
close_dir(&sflEntry);
}
return files;
}
while (ok){
if (file_matches(sflEntry.file_name, find.c_str())){
files.push_back(AbsolutePath(dataPath.path() + "/" + string(sflEntry.file_name)));
}
ok = read_dir(&sflEntry);
}
close_dir(&sflEntry);
/*
for (map<AbsolutePath, Util::ReferenceCount<Storage::ZipContainer> >::iterator it = overlays.begin(); it != overlays.end(); it++){
AbsolutePath path = it->first;
if (it->second == NULL){
continue;
}
// Global::debug(0) << "Check " << path.path() << " (" << path.getDirectory().path() << ") vs directory " << dataPath.path() << " wildcard " << find << " to " << path.getFilename().path() << std::endl;
if (path.getDirectory() == dataPath &&
file_matches(path.getLastComponent().c_str(), find.c_str())){
// Global::debug(0) << "Found overlay " << path.path() << " in " << dataPath.path() << " for wildcard " << find << std::endl;
files.push_back(path);
}
}
*/
// Global::debug(0) << "Warning: Filesystem::getFiles() is not implemented yet for SDL" << endl;
return files;
#endif
}
std::vector<Filesystem::AbsolutePath> Filesystem::getFiles(const RelativePath & path, const RelativePath & find, bool caseInsensitive){
vector<AbsolutePath> directories;
directories.push_back(dataPath.join(path));
directories.push_back(userDirectory().join(path));
directories.push_back(Filesystem::AbsolutePath(path.path()));
vector<AbsolutePath> files;
for (vector<AbsolutePath>::iterator it = directories.begin(); it != directories.end(); it++){
Global::debug(0) << "Search for " << find.path() << " in " << it->path() << std::endl;
vector<AbsolutePath> found = getFiles(*it, find, caseInsensitive);
files.insert(files.end(), found.begin(), found.end());
}
return files;
}
template <class X>
static void append(vector<X> & destination, const vector<X> & source){
/*
for (typename vector<X>::const_iterator it = source.begin(); it != source.end(); it++){
destination.push_back(*it);
}
*/
copy(source.begin(), source.end(), back_insert_iterator<vector<X> >(destination));
}
vector<Filesystem::AbsolutePath> Filesystem::getAllDirectories(const AbsolutePath & path){
vector<AbsolutePath> all = findDirectoriesIn(path);
vector<AbsolutePath> final;
append(final, all);
for (vector<AbsolutePath>::iterator it = all.begin(); it != all.end(); it++){
vector<AbsolutePath> more = getAllDirectories(*it);
append(final, more);
}
return final;
}
vector<Filesystem::AbsolutePath> Filesystem::getFilesRecursive(const AbsolutePath & dataPath, const string & find, bool caseInsensitive){
if (!exists(dataPath)){
return vector<AbsolutePath>();
}
vector<AbsolutePath> directories = getAllDirectories(dataPath);
directories.push_back(dataPath);
vector<AbsolutePath> files;
for (vector<AbsolutePath>::iterator it = directories.begin(); it != directories.end(); it++){
vector<AbsolutePath> found = getFiles(*it, find, caseInsensitive);
append(files, found);
}
return files;
}
/*
std::string find(const std::string path){
if (path.length() == 0){
throw NotFound("No path given");
}
if (path[0] == '/'){
string str(path);
str.erase(0, 1);
string out = lookup(str);
if (System::isDirectory(out)){
return sanitize(out + "/");
}
return sanitize(out);
}
string out = lookup(path);
if (System::isDirectory(out)){
return sanitize(out + "/");
}
return sanitize(out);
}
*/
Filesystem::AbsolutePath Filesystem::find(const RelativePath & path){
if (path.isEmpty()){
throw NotFound(__FILE__, __LINE__, "No path given");
}
AbsolutePath out = lookup(path);
if (::System::isDirectory(out.path())){
return AbsolutePath(out.path() + "/");
}
return AbsolutePath(out.path());
}
Filesystem::AbsolutePath Filesystem::findInsensitive(const RelativePath & path){
try{
/* try sensitive lookup first */
return lookup(path);
} catch (const NotFound & fail){
}
/* get the base directory */
AbsolutePath directory = lookup(path.getDirectory());
return lookupInsensitive(directory, path.getFilename());
}
bool Filesystem::exists(const RelativePath & path){
try{
AbsolutePath absolute = find(path);
return true;
} catch (const NotFound & found){
return false;
}
}
bool Filesystem::systemIsDirectory(const AbsolutePath & path){
return ::System::isDirectory(path.path());
}
bool Filesystem::systemExists(const AbsolutePath & path){
return ::System::readable(path.path());
}
Filesystem::RelativePath Filesystem::cleanse(const AbsolutePath & path){
string str = path.path();
if (str.find(dataPath.path()) == 0){
str.erase(0, dataPath.path().length());
} else if (str.find(userDirectory().path()) == 0){
str.erase(0, userDirectory().path().length());
}
return RelativePath(str);
}
diff --git a/util/file-system.h b/util/file-system.h
index 2144467b..4232069e 100644
--- a/util/file-system.h
+++ b/util/file-system.h
@@ -1,603 +1,605 @@
#ifndef _paintown_file_system_h
#define _paintown_file_system_h
#include "exceptions/exception.h"
#include "pointer.h"
#include "thread.h"
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include <fstream>
#include <stdint.h>
class Token;
struct stat;
/* path utilities */
namespace Path{
class Path{
public:
const std::string & path() const;
const std::string getExtension() const;
bool isEmpty() const;
virtual ~Path();
protected:
Path();
Path(const std::string & path);
Path(const Path & path);
virtual inline void setPath(const std::string & s){
mypath = s;
}
std::string mypath;
};
class InsensitivePath: public Path {
public:
InsensitivePath(const Path & what);
bool operator==(const Path & path) const;
};
/* relative path should not have the leading data directory on it, just
* the path within the paintown system.
*/
class RelativePath: public Path {
public:
explicit RelativePath();
explicit RelativePath(const std::string & path);
RelativePath(const RelativePath & path);
bool operator<(const RelativePath & path) const;
virtual RelativePath getDirectory() const;
virtual RelativePath getFilename() const;
RelativePath removeFirstDirectory() const;
RelativePath firstDirectory() const;
/* true if there are no directory parts to this path
* foo is a file
* bar/foo is not a file
*/
bool isFile() const;
/* a/ + b/ = a/b/ */
RelativePath join(const RelativePath & path) const;
RelativePath & operator=(const RelativePath & copy);
bool operator==(const RelativePath & path) const;
bool operator!=(const RelativePath & path) const;
};
/* absolute paths should have the entire filesystem path on it */
class AbsolutePath: public Path {
public:
explicit AbsolutePath();
explicit AbsolutePath(const std::string & path);
AbsolutePath(const AbsolutePath & path);
AbsolutePath & operator=(const AbsolutePath & copy);
bool operator<(const AbsolutePath & path) const;
bool operator==(const AbsolutePath & path) const;
bool operator!=(const AbsolutePath & path) const;
/* Remove a given path from the start of this path */
virtual RelativePath remove(const AbsolutePath & path) const;
virtual AbsolutePath getDirectory() const;
virtual AbsolutePath getFilename() const;
AbsolutePath removeFirstDirectory() const;
std::string firstDirectory() const;
bool isFile() const;
/* If the filename is empty then get the name of the directory.
* a/b -> b
* a/b/ -> b
*
* Otherwise (a/b/).getFilename() will be ""
*/
virtual std::string getLastComponent() const;
AbsolutePath join(const RelativePath & path) const;
};
std::string invertSlashes(std::string str);
std::string sanitize(std::string path);
/* remove extension. foo.txt -> foo */
std::string removeExtension(const std::string & str);
/* Replace the extension on `input' with `extension'. Don't put the . on the extension,
* so call it as replaceExtension(path, "foo")
* instead of replaceExtension(path, ".foo")
*/
AbsolutePath replaceExtension(const AbsolutePath & input, const std::string & extension);
/* basename, just get the filename and remove the directory part */
std::string stripDir(const std::string & str);
/* dirname, just get the directory and remove the filename part */
std::string stripFilename(const std::string & str);
}
namespace Storage{
/* sorry for the crappy abbreviation, but can't collide with the
* Exception class here
*/
namespace Exc = ::Exception;
class Exception: public Exc::Base {
public:
Exception(const std::string & where, int line, const std::string & file);
Exception(const std::string & where, int line, const Exc::Base & nested, const std::string & file);
Exception(const Exception & copy);
virtual ~Exception() throw ();
virtual void throwSelf() const {
throw *this;
}
protected:
virtual const std::string getReason() const;
virtual Exc::Base * copy() const {
return new Exception(*this);
}
private:
std::string reason;
};
class NotFound: public Exception {
public:
NotFound(const std::string & where, int line, const std::string & file);
NotFound(const std::string & where, int line, const Exc::Base & nested, const std::string & file);
virtual ~NotFound() throw();
NotFound(const NotFound & copy);
virtual void throwSelf() const {
throw *this;
}
protected:
virtual Exc::Base * copy() const {
return new NotFound(*this);
}
};
class IllegalPath: public Exception {
public:
IllegalPath(const std::string & where, int line, const std::string & file);
IllegalPath(const std::string & where, int line, const Exc::Base & nested, const std::string & file);
virtual ~IllegalPath() throw();
IllegalPath(const IllegalPath & copy);
virtual void throwSelf() const {
throw *this;
}
protected:
virtual Exc::Base * copy() const {
return new IllegalPath(*this);
}
};
class Eof: public std::exception {
public:
Eof(){
}
virtual ~Eof() throw (){
}
};
/* Abstraction for files. Should be used instead of FILE, ifstream, SDL_RWOps, anything else */
class File{
public:
enum Access{
Read,
Write,
ReadWrite
};
File();
virtual ~File();
/* Returns the number of bytes read */
virtual int readLine(char * output, int size) = 0;
/* Mostly for normal files, reset their error flags so
* we can seek after eof
*/
virtual void reset() = 0;
/* true if the underlying object can be streamed. generally
* this should be true if the object can be kept around while other files
* of the same type can be opened.
* zip files aren't streamable because only one zip entry can be
* open at a time so it must be closed as soon as possible.
*/
virtual bool canStream() = 0;
virtual long getModificationTime() = 0;
/* seek to an absolute position */
virtual off_t seek(off_t position, int whence) = 0;
virtual int getSize() = 0;
virtual long tell() = 0;
/* Returns a token that represents the path. For normal files it will just be
* a single string containing the path -- "data/x/y/z.txt"
* For zip files it will be (container "x/y/z.zip" "mount/point" "a.txt")
*/
virtual Token * location() = 0;
/* if the file is at eof and can't read anymore */
virtual bool eof() = 0;
/* if the file can still be read */
virtual bool good() = 0;
/* read one unsigned byte */
virtual File & operator>>(unsigned char &) = 0;
};
class EndianReader{
public:
EndianReader(const Util::ReferenceCount<Storage::File> & file){
internal = new FileInternal(file);
}
EndianReader(std::ifstream & stream){
internal = new StreamInternal(stream);
}
class Internal{
public:
Internal();
virtual bool eof() = 0;
virtual int read(char * data, int length) = 0;
virtual void seekEnd(std::streamoff where) = 0;
virtual void seek(std::streamoff where) = 0;
virtual int tell() = 0;
virtual ~Internal();
};
class StreamInternal: public Internal {
public:
StreamInternal(std::ifstream & stream):
stream(stream){
}
virtual bool eof();
virtual int read(char * data, int length);
virtual void seekEnd(std::streamoff where);
virtual void seek(std::streamoff where);
virtual int tell();
virtual ~StreamInternal();
/* Yes, use a reference here */
std::ifstream & stream;
};
class FileInternal: public Internal {
public:
FileInternal(const Util::ReferenceCount<Storage::File> & file):
file(file){
}
virtual bool eof();
virtual void seekEnd(std::streamoff where);
virtual void seek(std::streamoff where);
virtual int read(char * data, int length);
virtual int tell();
virtual ~FileInternal();
Util::ReferenceCount<Storage::File> file;
};
virtual ~EndianReader(){
}
virtual int8_t readByte1(){
return convert(readBytes(sizeof(int8_t)));
}
virtual int16_t readByte2(){
return convert(readBytes(sizeof(int16_t)));
}
virtual int32_t readByte4(){
return convert(readBytes(sizeof(int32_t)));
}
virtual std::string readStringX(int length);
virtual std::string readString2(int length);
virtual void readBytes(uint8_t * out, int length);
virtual void seekEnd(std::streamoff where);
virtual void seek(std::streampos where);
virtual int position();
protected:
virtual int32_t convert(const std::vector<uint8_t> & bytes) = 0;
std::vector<uint8_t> readBytes(int length);
Util::ReferenceCount<Internal> internal;
};
/* combines bytes b0 b1 b2 b3 as b0 + b1*2^8 + b2*2^16 + b3*2^24 */
class LittleEndianReader: public EndianReader {
public:
LittleEndianReader(const Util::ReferenceCount<Storage::File> & file):
EndianReader(file){
}
LittleEndianReader(std::ifstream & stream):
EndianReader(stream){
}
protected:
virtual int32_t convert(const std::vector<uint8_t> & bytes){
uint32_t out = 0;
for (std::vector<uint8_t>::const_reverse_iterator it = bytes.rbegin(); it != bytes.rend(); it++){
out = (out << 8) + *it;
}
return out;
}
};
/* combines bytes b0 b1 b2 b3 as b0*2^24 + b1*2^16 + b2*2^8 + b3 */
class BigEndianReader: public EndianReader {
public:
BigEndianReader(const Util::ReferenceCount<Storage::File> & file):
EndianReader(file){
}
BigEndianReader(std::ifstream & stream):
EndianReader(stream){
}
protected:
virtual int32_t convert(const std::vector<uint8_t> & bytes){
uint32_t out = 0;
for (std::vector<uint8_t>::const_iterator it = bytes.begin(); it != bytes.end(); it++){
out = (out << 8) + *it;
}
return out;
}
};
class LzmaContainer;
class ZipContainer;
class StringFile: public File {
public:
StringFile(const std::string & start);
virtual int readLine(char * output, int size);
virtual bool eof();
virtual bool good();
virtual int getSize();
virtual long getModificationTime();
virtual bool canStream();
virtual void reset();
virtual long tell();
virtual Token * location();
virtual off_t seek(off_t position, int whence);
virtual File & operator>>(unsigned char &);
virtual ~StringFile();
protected:
std::string data;
std::istringstream stream;
};
/* Contains information about an abstract File. */
class Descriptor{
public:
Descriptor();
virtual Util::ReferenceCount<File> open(File::Access mode) = 0;
virtual ~Descriptor();
};
class Traverser;
class Directory{
public:
Directory();
virtual ~Directory();
/* Finds any path in the given directory that matches find, files and directories */
std::vector<Path::AbsolutePath> findFiles(const Path::AbsolutePath & dataPath, const std::string & find, bool caseInsensitive);
/* Only finds directories */
std::vector<Path::AbsolutePath> findDirectories(const Path::AbsolutePath & dataPath, const std::string & find, bool caseInsensitive);
void addFile(const Path::AbsolutePath & path, const Util::ReferenceCount<Descriptor> & file);
void removeFile(const Path::AbsolutePath & path);
/* FIXME: maybe filenames() should just return files? */
/* Files + directories */
std::vector<std::string> filenames() const;
/* Just directories */
std::vector<std::string> directoryNames() const;
/* Finds a file with the given path. Will not find directories.
* Might return NULL if the path can't be found
*/
Util::ReferenceCount<Descriptor> lookup(const Path::AbsolutePath & path);
/* true if the path (either file or directory) exists */
bool exists(const Path::AbsolutePath & path);
/* true if path is a directory */
bool isDirectory(const Path::AbsolutePath & path);
protected:
void doTraverse(const Path::AbsolutePath & path, Traverser & traverser);
void traverse(const Path::AbsolutePath & path, Traverser & traverser);
// Util::ReferenceCount<File> doLookup(const Path::AbsolutePath & path);
std::map<std::string, Util::ReferenceCount<Directory> > directories;
std::map<std::string, Util::ReferenceCount<Descriptor> > files;
Util::Thread::LockObject lock;
};
class System{
public:
System();
virtual ~System();
typedef Path::AbsolutePath AbsolutePath;
typedef Path::RelativePath RelativePath;
virtual AbsolutePath find(const RelativePath & path) = 0;
virtual RelativePath cleanse(const AbsolutePath & path) = 0;
virtual bool exists(const RelativePath & path) = 0;
virtual bool exists(const AbsolutePath & path);
virtual bool isDirectory(const AbsolutePath & path);
virtual std::vector<AbsolutePath> getFilesRecursive(const AbsolutePath & dataPath, const std::string & find, bool caseInsensitive = false) = 0;
/* Finds all supported container files. Currently
* .zip
*/
virtual std::vector<AbsolutePath> getContainerFilesRecursive(const AbsolutePath & dataPath);
/* Gets container files only in the specified directory */
virtual std::vector<AbsolutePath> getContainerFiles(const AbsolutePath & dataPath);
/* Gets container files in <user>/path ./path and <data>/path */
virtual std::vector<AbsolutePath> getContainerFiles(const RelativePath & path);
/* Given a path with no extension, find a container file that is <name>.zip or
* <name>.7z or whatever exists.
* So if dataPath is "mugen/foo" this function will look for
* mugen/foo.zip
* mugen/foo.7z
* mugen/foo.rar
* ...
*
* and return the first one it finds
*/
virtual AbsolutePath findContainer(const RelativePath & dataPath);
/* search for a pattern of a single file within a directory */
virtual std::vector<AbsolutePath> getFiles(const AbsolutePath & dataPath, const std::string & find, bool caseInsensitive = false) = 0;
/* Container should be a path to a zip file */
virtual void addOverlay(const AbsolutePath & container, const AbsolutePath & where);
virtual void removeOverlay(const AbsolutePath & container, const AbsolutePath & where);
virtual std::vector<std::string> containerFileList(const AbsolutePath & container);
/* search for some path which may contain wildcards in a directory */
virtual std::vector<AbsolutePath> getFiles(const AbsolutePath & dataPath, const RelativePath & find, bool caseInsensitive);
/* Gets all the files in all directories matched by the relative path */
virtual std::vector<AbsolutePath> getFiles(const RelativePath & dataPath, const RelativePath & find, bool caseInsensitive) = 0;
virtual AbsolutePath configFile() = 0;
virtual AbsolutePath userDirectory() = 0;
virtual std::vector<AbsolutePath> findDirectories(const RelativePath & path) = 0;
virtual AbsolutePath findInsensitive(const RelativePath & path) = 0;
virtual AbsolutePath lookupInsensitive(const AbsolutePath & directory, const RelativePath & path) = 0;
virtual Util::ReferenceCount<File> open(const AbsolutePath & path, File::Access mode = File::Read);
/* Should be protected but needs to be public so we can use it in template methods
* in the implementation file.
*/
public:
virtual void overlayFile(const AbsolutePath & where, Util::ReferenceCount<ZipContainer> zip);
virtual void overlayFile(const AbsolutePath & where, Util::ReferenceCount<LzmaContainer> container);
protected:
virtual bool systemExists(const AbsolutePath & path) = 0;
virtual bool systemIsDirectory(const AbsolutePath & path) = 0;
virtual void unoverlayFile(const AbsolutePath & where);
// std::map<AbsolutePath, Util::ReferenceCount<ZipContainer> > overlays;
Storage::Directory virtualDirectory;
};
System & instance();
bool hasInstance();
System & setInstance(const Util::ReferenceCount<System> & what);
bool isContainer(const Path::AbsolutePath & path);
std::string readFile(const Path::AbsolutePath & path);
+
+ std::vector<std::string> containerTypes();
}
/*
* class Filesystem
* class NetworkStorage
* class ZipStorage
*/
class Filesystem: public Storage::System {
public:
Filesystem(const Path::AbsolutePath & dataPath);
typedef Path::AbsolutePath AbsolutePath;
typedef Path::RelativePath RelativePath;
typedef Path::InsensitivePath InsensitivePath;
typedef Storage::Exception Exception;
typedef Storage::NotFound NotFound;
/* given a relative path like sounds/arrow.png, prepend the proper
* data path to it to give data/sounds/arrow.png
*/
AbsolutePath find(const RelativePath & path);
/* like `find' but ignores case */
AbsolutePath findInsensitive(const RelativePath & path);
/* findInsensitive but starts in the given absolute directory path */
AbsolutePath lookupInsensitive(const AbsolutePath & directory, const RelativePath & path);
// void initialize();
/* whether the file exists at all */
using System::exists;
bool exists(const RelativePath & path);
/* remove the data path from a string
* data/sounds/arrow.png -> sounds/arrow.png
*/
RelativePath cleanse(const AbsolutePath & path);
/* returns all the directories starting with the given path.
* will look in the main data directory, the user directory, and
* the current working directory.
*/
std::vector<AbsolutePath> findDirectories(const RelativePath & path);
/* user specific directory to hold persistent data */
AbsolutePath userDirectory();
/* user specific path to store the configuration file */
AbsolutePath configFile();
using Storage::System::getFiles;
/* search a directory for some files matching pattern `find' */
virtual std::vector<AbsolutePath> getFiles(const AbsolutePath & dataPath, const std::string & find, bool caseInsensitive = false);
virtual std::vector<AbsolutePath> getFiles(const RelativePath & dataPath, const RelativePath & find, bool caseInsensitive);
/* same as getFiles but search directories recursively */
std::vector<AbsolutePath> getFilesRecursive(const AbsolutePath & dataPath, const std::string & find, bool caseInsensitive = false);
protected:
virtual bool systemExists(const AbsolutePath & path);
virtual bool systemIsDirectory(const AbsolutePath & path);
AbsolutePath lookup(const RelativePath path);
std::vector<AbsolutePath> findDirectoriesIn(const AbsolutePath & path);
std::vector<AbsolutePath> getAllDirectories(const AbsolutePath & path);
protected:
Util::Thread::LockObject lock;
AbsolutePath dataPath;
};
#endif

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 16, 12:24 AM (2 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
71332
Default Alt Text
(105 KB)

Event Timeline