Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
333 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/configuration.cpp b/src/configuration.cpp
index bd3a0074..b380b796 100644
--- a/src/configuration.cpp
+++ b/src/configuration.cpp
@@ -1,1009 +1,1010 @@
#include "r-tech1/configuration.h"
#include "r-tech1/input/keyboard.h"
#include "r-tech1/exceptions/load_exception.h"
#include "r-tech1/tokenreader.h"
#include "r-tech1/token.h"
#include "r-tech1/file-system.h"
#include "r-tech1/input/input.h"
#include "r-tech1/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 (unsigned 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++){
+ for (unsigned 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);
+ ignore = ignore;
}
/* 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 << 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 << "/" << INPUT_TYPE << "/" << 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){
loadConfigurations();
// 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;
} catch (const Filesystem::NotFound & fail){
Global::debug(0) << "Notice: could not open configuration file '" << Storage::instance().configFile().path() << "': " << fail.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/src/exceptions/exception.cpp b/src/exceptions/exception.cpp
index 1467c93b..46d8866f 100644
--- a/src/exceptions/exception.cpp
+++ b/src/exceptions/exception.cpp
@@ -1,119 +1,120 @@
#include "r-tech1/exceptions/exception.h"
#include <string>
#include <exception>
#include <sstream>
namespace Exception{
Base::Base(const std::string & file, int line):
file(file),
line(line),
nested(NULL){
}
Base::Base(const std::string & file, int line, const Base & nested):
file(file),
line(line),
nested(nested.copy()){
}
Base::~Base() throw (){
if (nested){
delete nested;
}
}
const std::string Base::getReason() const {
return "reason not given";
}
const std::string Base::getTrace() const {
std::ostringstream out;
out << file << ":" << line << " " << getReason();
if (nested != NULL){
out << "\n";
out << nested->getTrace();
}
return out.str();
}
Base::Base(const Base & copy):
file(copy.file),
line(copy.line),
nested(NULL){
if (copy.nested != NULL){
nested = copy.nested->copy();
}
}
void Base::set(const Base & him){
nested = him.copy();
}
Base * Base::copy() const {
return new Base(*this);
}
Return::Return(const std::string & file, int line):
Base(file, line){
}
Return::Return(const std::string & file, int line, const Base & nested):
Base(file, line, nested){
}
Return::~Return() throw(){
}
void Return::throwSelf() const {
throw *this;
}
Base * Return::copy() const {
return new Return(*this);
}
Quit::Quit(const std::string & file, int line):
Base(file, line){
}
Quit::Quit(const std::string & file, int line, const Base & nested):
Base(file, line, nested){
}
Quit::~Quit() throw(){
}
void Quit::throwSelf() const {
throw *this;
}
Base * Quit::copy() const {
return new Quit(*this);
}
FontException::FontException(const std::string & file, int line, const std::string & reason):
Base(file, line),
reason(reason){
}
FontException::FontException(const std::string & file, int line, const Base & nested, const std::string & reason):
Base(file, line, nested),
reason(reason){
}
FontException::~FontException() throw(){
}
const std::string FontException::getReason() const {
return reason;
}
void FontException::throwSelf() const {
throw *this;
}
Base * FontException::copy() const {
+ return nullptr;
}
}
diff --git a/src/graphics/bitmap.cpp b/src/graphics/bitmap.cpp
index 5a089359..ad8f4826 100644
--- a/src/graphics/bitmap.cpp
+++ b/src/graphics/bitmap.cpp
@@ -1,581 +1,581 @@
#include "r-tech1/funcs.h"
#include "r-tech1/graphics/bitmap.h"
#include "r-tech1/file-system.h"
#include <string>
#include <stdio.h>
#include <math.h>
namespace Graphics{
static Bitmap * Screen = NULL;
/* bitmaps that should always be resized to the dimensions of the screen */
static std::vector<Bitmap*> needResize;
Util::Parameter<Bitmap*> screenParameter;
Util::Parameter<Util::ReferenceCount<ShaderManager> > shaderManager;
ShaderManager::ShaderManager(){
}
ShaderManager::~ShaderManager(){
}
Util::ReferenceCount<Shader> ShaderManager::getShader(const std::string & name, Util::ReferenceCount<Shader> (*create)()){
std::map<std::string, Util::ReferenceCount<Shader> >::iterator it = shaders.find(name);
if (it == shaders.end()){
shaders[name] = create();
it = shaders.find(name);
}
return it->second;
}
/* implementation independant definitions can go here */
/*
int SCALE_X = 0;
int SCALE_Y = 0;
*/
/*
const int Bitmap::MODE_TRANS = 0;
const int Bitmap::MODE_SOLID = 1;
*/
const int SPRITE_NO_FLIP = 0;
const int SPRITE_V_FLIP = 1;
const int SPRITE_H_FLIP = 2;
const int SPRITE_NORMAL = 1;
const int SPRITE_LIT = 2;
const int SPRITE_TRANS = 3;
static inline int max(int a, int b){
return a > b ? a : b;
}
INTERNAL_COLOR Color::defaultColor(){
return makeColor(0, 0, 0).color;
}
void initializeExtraStuff();
Bitmap::Bitmap(Storage::File & file):
mustResize(false),
error(false),
bit8MaskColor(makeColor(0, 0, 0)){
int length = file.getSize();
if (length == -1){
throw BitmapException(__FILE__, __LINE__, std::string("Could not read from file"));
}
char * data = new char[length];
try{
file.readLine(data, length);
loadFromMemory(data, length);
delete[] data;
} catch (const BitmapException & fail){
delete[] data;
throw;
} catch (...){
delete[] data;
throw;
}
}
QualityFilter qualityFilterName(const std::string & type){
if (type == "xbr"){
return XbrFilter;
}
if (type == "hqx"){
return HqxFilter;
}
return NoFilter;
}
Bitmap::~Bitmap(){
if (mustResize){
for (std::vector<Bitmap*>::iterator it = needResize.begin(); it != needResize.end(); it++){
Bitmap * who = *it;
if (who == this){
needResize.erase(it);
break;
}
}
}
}
static Bitmap makeTemporaryBitmap(Bitmap *& temporary, int w, int h){
if (temporary == NULL){
temporary = new Bitmap(w, h);
} else if (temporary->getWidth() < w || temporary->getHeight() < h){
int mw = max(temporary->getWidth(), w);
int mh = max(temporary->getHeight(), h);
// printf("Create temporary bitmap %d %d\n", mw, mh);
delete temporary;
temporary = new Bitmap(mw, mh);
}
if (temporary == NULL){
printf("*bug* temporary bitmap is null\n");
}
return Bitmap(*temporary, 0, 0, w, h);
}
/*
Bitmap Bitmap::temporaryBitmap(int w, int h){
return makeTemporaryBitmap(temporary_bitmap, w, h);
}
Bitmap Bitmap::temporaryBitmap2(int w, int h){
return makeTemporaryBitmap(temporary_bitmap2, w, h);
}
*/
void Bitmap::cleanupTemporaryBitmaps(){
}
Bitmap & Bitmap::operator=(const Bitmap & copy){
path = copy.getPath();
this->width = copy.getWidth();
this->height = copy.getHeight();
setData(copy.getData());
return *this;
}
double Bitmap::getScale(){
/* the game is pretty much hard coded to run at 320 scaled upto 640
* and then scaled to whatever the user wants, but as long as
* 320 and 640 remain this number will be 2.
* maybe calculate this at some point
*/
return 2;
/*
if (Scaler != NULL && Buffer != NULL){
double x1 = Scaler->getWidth();
double x2 = Buffer->getWidth();
return x2 / x1;
}
return 1;
*/
}
bool Bitmap::isEmpty() const {
return getWidth() == 0 || getHeight() == 0;
}
/* taken from the color addon from allegro 4.9 */
static void al_color_cmyk_to_rgb(float cyan, float magenta, float yellow, float key, float *red, float *green, float *blue){
float max = 1 - key;
*red = max - cyan * max;
*green = max - magenta * max;
*blue = max - yellow * max;
}
void Bitmap::cymkToRGB(int c, int y, int m, int k, int * r, int * g, int * b){
float fc = (float)c / 255.0;
float fy = (float)y / 255.0;
float fm = (float)m / 255.0;
float fk = (float)k / 255.0;
float fr, fg, fb;
al_color_cmyk_to_rgb(fc, fm, fy, fk, &fr, &fg, &fb);
*r = (int)(fr * 255.0);
*g = (int)(fg * 255.0);
*b = (int)(fb * 255.0);
}
void Bitmap::updateOnResize(){
if (!mustResize){
mustResize = true;
needResize.push_back(this);
}
}
void Bitmap::updateSize(const int width, const int height){
if (getWidth() == width && getHeight() == height){
return;
}
Bitmap created(width, height);
*this = created;
}
/* resize the internal bitmap. not guaranteed to destroy the internal bitmap */
void Bitmap::resize(const int width, const int height){
/* if internal bitmap is already the proper size, do nothing */
if (getWidth() == width && getHeight() == height){
return;
}
Bitmap created(width, height);
Stretch(created);
*this = created;
}
/* decrement bitmap reference counter and free memory if counter hits 0 */
#if 0
void Bitmap::releaseInternalBitmap(){
const int MAGIC_DEBUG = 0xa5a5a5;
if (own != NULL){
if (*own == MAGIC_DEBUG){
printf("[bitmap] Trying to delete an already deleted reference counter %p\n", own);
}
(*own) -= 1;
if ( *own == 0 ){
*own = MAGIC_DEBUG;
delete own;
destroyPrivateData();
own = NULL;
}
}
}
#endif
void Bitmap::BlitToScreen() const {
// this->Blit( *Bitmap::Screen );
this->BlitToScreen(0, 0);
}
void Bitmap::load( const std::string & str ){
// releaseInternalBitmap();
internalLoadFile( str.c_str() );
}
Bitmap Bitmap::scaleBy(const double widthRatio, const double heightRatio) const {
return scaleTo(getWidth() * widthRatio, getHeight() * heightRatio);
}
void Bitmap::border( int min, int max, Color color ) const {
int w = getWidth();
int h = getHeight();
for (int i = min; i < max; i++){
rectangle(i, i, w - 1 - i, h - 1 - i, color);
}
}
void Bitmap::drawHFlip(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, const Bitmap & where) const {
drawHFlip(x, y, startWidth, startHeight, width, height, NULL, where);
}
void Bitmap::drawHFlip(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, Filter * filter, const Bitmap & where) const {
Bitmap sub(*this, getWidth() - width, getHeight() - height, getWidth() - startWidth, getHeight() - startHeight);
sub.drawHFlip(x + startWidth, y + startHeight, filter, where);
}
void Bitmap::drawRotateCenter(const int x, const int y, const int angle, const Bitmap & where){
drawRotate(x - getWidth() / 2, y - getHeight() / 2, angle, where);
}
void Bitmap::drawCenter(const int x, const int y, const Bitmap & where) const {
draw(x - getWidth() / 2, y - getHeight() / 2, where);
}
void Bitmap::drawStretched(const Bitmap & who) const {
drawStretched(0, 0, who.getWidth(), who.getHeight(), who);
}
void Bitmap::draw(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, const Bitmap & where) const {
draw(x, y, startWidth, startHeight, width, height, NULL, where);
/*
Bitmap sub(*this, startWidth, startHeight, width, height);
sub.draw(x + startWidth, y + startHeight, where);
*/
}
void Bitmap::draw(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, Filter * filter, const Bitmap & where) const {
Bitmap sub(*this, startWidth, startHeight, width, height);
sub.draw(x + startWidth, y + startHeight, filter, where);
}
void Bitmap::horizontalLine( const int x1, const int y, const int x2, const Graphics::Color color ) const{
this->hLine(x1, y, x2, color);
}
void Bitmap::equilateralTriangle(int x, int y, int angle, int size, Color color) const {
double radians = Util::radians(angle);
int x1 = x + size / 2 * cos(radians + 2 * Util::pi / 3);
int y1 = y + size / 2 * sin(radians + 2 * Util::pi / 3);
int x2 = x + size / 2 * cos(radians - 2 * Util::pi / 3);
int y2 = y + size / 2 * sin(radians - 2 * Util::pi / 3);
int x3 = x + size / 2 * cos(radians);
int y3 = y + size / 2 * sin(radians);
triangle(x1, y1, x2, y2, x3, y3, color);
}
Bitmap Bitmap::greyScale(){
Bitmap grey(getWidth(), getHeight());
for (int x = 0; x < getWidth(); x++){
for (int y = 0; y < getHeight(); y++){
Color pixel = getPixel(x, y);
int val = (int)((0.299*getRed(pixel) + 0.587*getGreen(pixel) + 0.114*getBlue(pixel) + 0.5) + 16);
if (val > 255){
val = 255;
}
grey.putPixel(x, y, makeColor(val, val, val));
}
}
return grey;
}
bool Bitmap::inRange(int x, int y) const {
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
getClipRect(x1, y1, x2, y2);
return (x >= x1 && x <= x2 &&
y >= y1 && y <= y2);
}
void Bitmap::drawMask( const int _x, const int _y, const Bitmap & where ){
Color mask = MaskColor();
for (int x = 0; x < getWidth(); x++){
for (int y = 0; y < getHeight(); y++){
if (getPixel(x,y) == mask){
where.putPixel(x+_x, y+_y, mask);
}
}
}
}
void Bitmap::set8BitMaskColor(const Color & color){
bit8MaskColor = color;
}
Color Bitmap::get8BitMaskColor(){
return bit8MaskColor;
}
void Bitmap::setFakeGraphicsMode(int width, int height){
initializeExtraStuff();
Screen = new Bitmap(width, height);
}
void Bitmap::Blit( const std::string & xpath ) const {
Bitmap duh(xpath);
duh.Blit(*this);
}
void Bitmap::Blit(const Bitmap & where) const {
this->Blit(0, 0, where);
}
void Bitmap::Blit(const int x, const int y, const Bitmap & where) const {
Blit(0, 0, x, y, where);
}
void Bitmap::Blit(const int mx, const int my, const int wx, const int wy, const Bitmap & where) const {
Blit(mx, my, getWidth(), getHeight(), wx, wy, where);
}
void Bitmap::BlitFromScreen(const int x, const int y) const {
Screen->Blit(x, y, getWidth(), getHeight(), 0, 0, *this);
}
//! min (borrowed from allegro)
static inline int Min(int x, int y){ return (((x) < (y)) ? (x) : (y)); }
//! max (borrowed from allegro)
static inline int Max(int x, int y){ return (((x) > (y)) ? (x) : (y)); }
//! mid (borrowed from allegro)
static inline int Mid(int x,int y,int z){ return (Max((x), Min((y), (z)))); }
int Bitmap::getScreenWidth(){
if (Screen != 0){
return Screen->getWidth();
}
return 0;
}
int Bitmap::getScreenHeight(){
if (Screen != 0){
return Screen->getHeight();
}
return 0;
}
void Bitmap::clear() const {
fill(makeColor(0, 0, 0));
}
void Bitmap::copy(const Bitmap & him){
resize(him.getWidth(), him.getHeight());
him.Blit(*this);
}
void Bitmap::Stretch( const Bitmap & where ) const {
if (getWidth() == where.getWidth() && getHeight() == where.getHeight()){
Blit(where);
} else {
Stretch(where, 0, 0, getWidth(), getHeight(), 0, 0, where.getWidth(), where.getHeight());
}
}
void Bitmap::StretchHqx(const Bitmap & where) const {
if (getWidth() == where.getWidth() && getHeight() == where.getHeight()){
Blit(where);
} else {
StretchHqx(where, 0, 0, getWidth(), getHeight(), 0, 0, where.getWidth(), where.getHeight());
}
}
void Bitmap::StretchXbr(const Bitmap & where) const {
if (getWidth() == where.getWidth() && getHeight() == where.getHeight()){
Blit(where);
} else {
StretchXbr(where, 0, 0, getWidth(), getHeight(), 0, 0, where.getWidth(), where.getHeight());
}
}
Bitmap Bitmap::aspectRatio(int aspectWidth, int aspectHeight) const {
double width = getWidth();
double height = getHeight();
double ratio = (double) aspectWidth / (double) aspectHeight;
width = (double) height * ratio;
if (width > getWidth()){
width = getWidth();
height = width / ratio;
}
int x = (getWidth() - width) / 2;
int y = (getHeight() - height) / 2;
return Bitmap(*this, x, y, (int) width, (int) height);
}
Color darken(Color color, double factor ){
int r = (int)((double)getRed(color) / factor);
int g = (int)((double)getGreen(color) / factor);
int b = (int)((double)getBlue(color) / factor);
return makeColor(r, g, b);
}
LitBitmap::LitBitmap(const Bitmap & b):
Bitmap(b){
int x1, y1, x2, y2;
b.getClipRect(x1, y1, x2, y2);
setClipRect(x1, y1, x2, y2);
}
LitBitmap::LitBitmap():
Bitmap(){
}
LitBitmap::~LitBitmap(){
}
LitBitmap Bitmap::lit() const {
return LitBitmap(*this);
}
TranslucentBitmap Bitmap::translucent() const {
return TranslucentBitmap(*this);
}
TranslucentBitmap Bitmap::translucent(int red, int green, int blue, int alpha) const {
transBlender(red, green, blue, alpha);
return TranslucentBitmap(*this);
}
TranslucentBitmap::TranslucentBitmap(const Bitmap & b):
Bitmap(b){
int x1, y1, x2, y2;
b.getClipRect(x1, y1, x2, y2);
setClipRect(x1, y1, x2, y2);
}
TranslucentBitmap::TranslucentBitmap():
Bitmap(){
}
TranslucentBitmap::~TranslucentBitmap(){
}
void TranslucentBitmap::fill(Color color) const {
Bitmap::applyTrans(color);
}
int StretchedBitmap::getWidth() const {
return width;
}
int StretchedBitmap::getHeight() const {
return height;
}
double StretchedBitmap::getScaleWidth() const {
return (double) where.getWidth() / (double) getWidth();
}
double StretchedBitmap::getScaleHeight() const {
return (double) where.getHeight() / (double) getHeight();
}
BlendPoint::BlendPoint(const Color & color, int length):
color(color),
length(length){
}
void blend_palette(Color * pal, int mp, const Color & startColor, const Color & endColor){
/*
ASSERT(pal);
ASSERT(mp != 0);
*/
int sc_r = Graphics::getRed(startColor);
int sc_g = Graphics::getGreen(startColor);
int sc_b = Graphics::getBlue(startColor);
int ec_r = Graphics::getRed(endColor);
int ec_g = Graphics::getGreen(endColor);
int ec_b = Graphics::getBlue(endColor);
for ( int q = 0; q < mp; q++ ) {
float j = (float)( q + 1 ) / (float)( mp );
int f_r = (int)( 0.5 + (float)( sc_r ) + (float)( ec_r-sc_r ) * j );
int f_g = (int)( 0.5 + (float)( sc_g ) + (float)( ec_g-sc_g ) * j );
int f_b = (int)( 0.5 + (float)( sc_b ) + (float)( ec_b-sc_b ) * j );
pal[q] = Graphics::makeColor( f_r, f_g, f_b );
}
}
std::vector<Color> blend_palette(const std::vector<BlendPoint> & in){
std::vector<Color> out;
int here = 0;
- for (int i = 1; i < in.size(); i++){
+ for (unsigned int i = 1; i < in.size(); i++){
const BlendPoint & start = in[here];
const BlendPoint & end = in[i];
Color * save = new Color[start.length];
blend_palette(save, start.length, start.color, end.color);
for (int use = 0; use < start.length; use++){
out.push_back(save[use]);
}
delete[] save;
here = i;
}
return out;
}
}
#ifdef USE_ALLEGRO
#include "allegro/bitmap.cpp"
#endif
#ifdef USE_SDL
#include "sdl/bitmap.cpp"
#endif
#ifdef USE_ALLEGRO5
#include "graphics/allegro5/bitmap.cpp"
#endif
diff --git a/src/gui/select-list.cpp b/src/gui/select-list.cpp
index d170fd63..da324955 100644
--- a/src/gui/select-list.cpp
+++ b/src/gui/select-list.cpp
@@ -1,1095 +1,1095 @@
#include "r-tech1/gui/select-list.h"
#include "r-tech1/graphics/bitmap.h"
#include "r-tech1/font.h"
#include "r-tech1/debug.h"
#include <math.h>
using namespace Gui;
SelectItem::SelectItem(){
}
SelectItem::~SelectItem(){
}
SelectListInterface::SelectListInterface():
accessEmpty(true),
drawEmpty(false),
allowWrap(true){
}
SelectListInterface::~SelectListInterface(){
}
Cursor::Cursor(unsigned int index, const SelectListInterface::CursorState & state):
index(index),
state(state){
}
Cursor::~Cursor(){
}
Cursor::Cursor(const Cursor & cursor):
index(cursor.index),
state(cursor.state){
}
const Cursor & Cursor::operator=(const Cursor & copy){
this->index = copy.index;
this->state = copy.state;
return *this;
}
void Cursor::setIndex(unsigned int index){
this->index = index;
}
unsigned int Cursor::getIndex() const{
return index;
}
void Cursor::increment(){
index++;
}
void Cursor::decrement(){
if (index > 0){
index--;
}
}
void Cursor::setState(const SelectListInterface::CursorState & state){
this->state = state;
}
const SelectListInterface::CursorState & Cursor::getState() const{
return state;
}
SimpleSelect::SimpleSelect():
layout(Horizontal),
viewable(3),
currentTop(0),
scrollOffset(0),
cellWidth(100),
cellHeight(100),
cellSpacingX(0),
cellSpacingY(0),
cellMarginX(0),
cellMarginY(0),
startOffsetX(0),
startOffsetY(0){
}
SimpleSelect::~SimpleSelect(){
}
void SimpleSelect::act(){
/* Check if viewable is larger than the current items */
if (viewable >= items.size()){
viewable = items.size()-1;
}
}
void SimpleSelect::getDrawLocation(const Util::ReferenceCount<SelectItem> & item, int * x, int * y) const {
/* TODO */
*x = 0;
*y = 0;
}
void SimpleSelect::render(const Graphics::Bitmap & work, const Font & font) const{
int x = startOffsetX + cellMarginX;
int y = startOffsetY + cellMarginY;
const int stop = currentTop + viewable;
int count = currentTop;
for (std::vector<Util::ReferenceCount<SelectItem> >::const_iterator i = items.begin() + currentTop; i != items.end() && count != stop; ++i, ++count){
const Util::ReferenceCount<SelectItem> item = *i;
if (item->isEmpty()){
if (drawEmpty){
item->draw(x, y, cellWidth, cellHeight, work, font);
}
} else {
item->draw(x, y, cellWidth, cellHeight, work, font);
}
x+=cellSpacingX + (layout == Horizontal ? cellWidth + cellMarginX : 0);
y+=cellSpacingY + (layout == Vertical ? cellHeight + cellMarginY : 0);
}
}
void SimpleSelect::addItem(const Util::ReferenceCount<SelectItem> & item){
items.push_back(item);
}
void SimpleSelect::addItems(const std::vector<Util::ReferenceCount<SelectItem> > & itemList){
items.insert(items.begin(), itemList.begin(), itemList.end());
}
const std::vector<Util::ReferenceCount<SelectItem> > & SimpleSelect::getItems() const{
return items;
}
const Util::ReferenceCount<SelectItem> SimpleSelect::getItem(unsigned int index) const{
if (index >= items.size()){
return Util::ReferenceCount<SelectItem>();
}
return items[index];
}
const Util::ReferenceCount<SelectItem> SimpleSelect::getItemByCursor(unsigned int cursor) const{
if (getCurrentIndex(cursor) >= items.size()){
return Util::ReferenceCount<SelectItem>();
}
return items[getCurrentIndex(cursor)];
}
void SimpleSelect::clearItems(){
items.clear();
}
void SimpleSelect::setCellDimensions(int width, int height){
cellWidth = width;
cellHeight = height;
}
void SimpleSelect::setCellSpacing(int x, int y){
cellSpacingX = x;
cellSpacingY = y;
}
void SimpleSelect::setCellMargins(int x, int y){
cellMarginX = x;
cellMarginY = y;
}
void SimpleSelect::setStartingOffset(int x, int y){
startOffsetX = x;
startOffsetY = y;
}
void SimpleSelect::setCursors(int total){
cursors.clear();
for (int i = 0; i < total; ++i){
cursors.push_back(Cursor(i, SelectListInterface::Active));
}
}
int SimpleSelect::totalCursors() const{
return cursors.size();
}
void SimpleSelect::setCurrentIndex(unsigned int cursor, unsigned int location){
if (checkCursor(cursor) && location >= items.size()){
return;
}
cursors[cursor].setIndex(location);
}
unsigned int SimpleSelect::getCurrentIndex(unsigned int cursor) const{
if (checkCursor(cursor)){
return 0;
}
return cursors[cursor].getIndex();
}
void SimpleSelect::setCurrentState(unsigned int cursor, const SelectListInterface::CursorState & state){
if (checkCursor(cursor)){
return;
}
cursors[cursor].setState(state);
}
const SelectListInterface::CursorState SimpleSelect::getCurrentState(unsigned int cursor) const{
if (checkCursor(cursor)){
return Invalid;
}
return cursors[cursor].getState();
}
/* NOTE This doesn't account for other cursors and viewable areas */
bool SimpleSelect::up(unsigned int cursor){
if (checkCursor(cursor)){
return false;
}
if (cursors[cursor].getIndex() > 0){
cursors[cursor].decrement();
calculateLeft(cursor);
return true;
} else if (allowWrap){
cursors[cursor].setIndex(items.size()-1);
currentTop = cursors[cursor].getIndex() - viewable+1;
return true;
}
return false;
}
bool SimpleSelect::down(unsigned int cursor){
if (checkCursor(cursor)){
return false;
}
if (cursors[cursor].getIndex() < items.size()-1){
cursors[cursor].increment();
calculateRight(cursor);
return true;
} else if (allowWrap){
cursors[cursor].setIndex(0);
currentTop = 0;
return true;
}
return false;
}
bool SimpleSelect::left(unsigned int cursor){
if (checkCursor(cursor)){
return false;
}
if (cursors[cursor].getIndex() > 0){
cursors[cursor].decrement();
calculateLeft(cursor);
return true;
} else if (allowWrap){
cursors[cursor].setIndex(items.size()-1);
currentTop = cursors[cursor].getIndex() - viewable + 1;
return true;
}
return false;
}
bool SimpleSelect::right(unsigned int cursor){
if (checkCursor(cursor)){
return false;
}
if (cursors[cursor].getIndex() < items.size()-1){
cursors[cursor].increment();
calculateRight(cursor);
return true;
} else if (allowWrap){
cursors[cursor].setIndex(0);
currentTop = 0;
return true;
}
return false;
}
bool SimpleSelect::hasMoreLow() const{
return (currentTop > 0);
}
bool SimpleSelect::hasMoreHigh() const{
return ((currentTop + viewable) < items.size());
}
int SimpleSelect::getWidth(){
int x = startOffsetX + cellMarginX;
const int stop = currentTop + viewable;
int count = currentTop;
for (std::vector<Util::ReferenceCount<SelectItem> >::const_iterator i = items.begin() + currentTop; i != items.end() && count != stop; ++i, ++count){
x+=cellSpacingX + (layout == Horizontal ? cellWidth + cellMarginX : 0);
}
x+=cellSpacingX + (layout == Horizontal ? cellWidth + cellMarginX : 0);
return x;
}
int SimpleSelect::getHeight(){
int y = startOffsetY + cellMarginY;
const int stop = currentTop + viewable;
int count = currentTop;
for (std::vector<Util::ReferenceCount<SelectItem> >::const_iterator i = items.begin() + currentTop; i != items.end() && count != stop; ++i, ++count){
y+=cellSpacingY + (layout == Vertical ? cellHeight + cellMarginY : 0);
}
y+=cellSpacingY + (layout == Vertical ? cellHeight + cellMarginY : 0);
return y;
}
bool SimpleSelect::checkCursor(unsigned int cursor) const {
return ((unsigned int)cursor >= cursors.size());
}
void SimpleSelect::calculateLeft(unsigned int cursor){
if (currentTop == 0){
//currentTop = cursors[cursor];
} else if (cursors[cursor].getIndex() < currentTop + scrollOffset){
currentTop = cursors[cursor].getIndex() - scrollOffset;
}
}
void SimpleSelect::calculateRight(unsigned int cursor){
const unsigned int view = viewable-1;
if ((currentTop + view) == items.size()-1){
//currentTop = right;
} else if (cursors[cursor].getIndex() >= (currentTop + view - scrollOffset)){
currentTop = cursors[cursor].getIndex() - view + scrollOffset;
}
}
GridSelect::GridSelect():
layout(Static),
gridX(0),
gridY(0),
cellWidth(100),
cellHeight(100),
cellSpacingX(0),
cellSpacingY(0),
cellMarginX(0),
cellMarginY(0),
startOffsetX(0),
startOffsetY(0),
offset(0){
}
GridSelect::~GridSelect(){
}
void GridSelect::act(){
}
void GridSelect::getDrawLocation(const Util::ReferenceCount<SelectItem> & find, int * findX, int * findY) const {
/* FIXME: this code is a direct copy/paste of render. share some code */
std::vector<Util::ReferenceCount<SelectItem> >::const_iterator item_iterator = items.begin();
switch (layout){
case Static:{
int x = startOffsetX + (cellSpacingX * gridY < 0 ? abs(cellSpacingX * gridY) : 0);
int y = startOffsetY + (cellSpacingY * gridX < 0 ? abs(cellSpacingY * gridX) : 0);
for (int row = 0; row < gridY; ++row){
int x_spacing_mod = x;
int y_spacing_mod = y;
for (int column = 0; column < gridX; ++column){
if (item_iterator != items.end()){
Util::ReferenceCount<SelectItem> item = *item_iterator;
if (item == find){
*findX = x_spacing_mod;
*findY = y_spacing_mod;
return;
}
item_iterator++;
}
x_spacing_mod+= cellSpacingX + cellWidth + cellMarginX;
y_spacing_mod+= cellSpacingY;
}
x+= cellSpacingX;
y+= cellHeight + cellMarginY;
}
break;
}
case InfiniteHorizontal:{
int x = startOffsetX + (cellSpacingX * gridY < 0 ? abs(cellSpacingX * gridY) : 0);
int y = startOffsetY + (cellSpacingY * gridX < 0 ? abs(cellSpacingY * gridX) : 0);
// Start off on offset
item_iterator += offset * gridY;
for (int column = 0; column < gridX; ++column){
int x_spacing_mod = x;
int y_spacing_mod = y;
for (int row = 0; row < gridY; ++row){
if (item_iterator != items.end()){
Util::ReferenceCount<SelectItem> item = *item_iterator;
if (item == find){
*findX = x_spacing_mod;
*findY = y_spacing_mod;
return;
}
item_iterator++;
}
x_spacing_mod+= cellSpacingX;
y_spacing_mod+= cellSpacingY + cellHeight + cellMarginY;
}
x+= cellWidth + cellMarginX;
y+= cellSpacingY;
}
break;
}
case InfiniteVertical:{
int x = startOffsetX + (cellSpacingX * gridY < 0 ? abs(cellSpacingX * gridY) : 0);
int y = startOffsetY + (cellSpacingY * gridX < 0 ? abs(cellSpacingY * gridX) : 0);
// Start off on offset
item_iterator += offset * gridX;
for (int row = 0; row < gridY; ++row){
int x_spacing_mod = x;
int y_spacing_mod = y;
for (int column = 0; column < gridX; ++column){
if (item_iterator != items.end()){
Util::ReferenceCount<SelectItem> item = *item_iterator;
if (item == find){
*findX = x_spacing_mod;
*findY = y_spacing_mod;
return;
}
item_iterator++;
}
x_spacing_mod+=cellSpacingX + cellWidth + cellMarginX;
y_spacing_mod+=cellSpacingY;
}
x+= cellSpacingX;
y+= cellHeight + cellMarginY;
}
break;
}
default:
break;
}
}
void GridSelect::render(const Graphics::Bitmap & where, const Font & font) const {
std::vector<Util::ReferenceCount<SelectItem> >::const_iterator item_iterator = items.begin();
switch (layout){
case Static:{
int x = startOffsetX + (cellSpacingX * gridY < 0 ? abs(cellSpacingX * gridY) : 0);
int y = startOffsetY + (cellSpacingY * gridX < 0 ? abs(cellSpacingY * gridX) : 0);
for (int row = 0; row < gridY; ++row){
int x_spacing_mod = x;
int y_spacing_mod = y;
for (int column = 0; column < gridX; ++column){
if (item_iterator != items.end()){
Util::ReferenceCount<SelectItem> item = *item_iterator;
if (item->isEmpty()){
if (drawEmpty){
item->draw(x_spacing_mod, y_spacing_mod, cellWidth, cellHeight, where, font);
}
} else {
item->draw(x_spacing_mod, y_spacing_mod, cellWidth, cellHeight, where, font);
}
item_iterator++;
}
x_spacing_mod+= cellSpacingX + cellWidth + cellMarginX;
y_spacing_mod+= cellSpacingY;
}
x+= cellSpacingX;
y+= cellHeight + cellMarginY;
}
break;
}
case InfiniteHorizontal:{
int x = startOffsetX + (cellSpacingX * gridY < 0 ? abs(cellSpacingX * gridY) : 0);
int y = startOffsetY + (cellSpacingY * gridX < 0 ? abs(cellSpacingY * gridX) : 0);
// Start off on offset
item_iterator += offset * gridY;
for (int column = 0; column < gridX; ++column){
int x_spacing_mod = x;
int y_spacing_mod = y;
for (int row = 0; row < gridY; ++row){
if (item_iterator != items.end()){
Util::ReferenceCount<SelectItem> item = *item_iterator;
if (item->isEmpty()){
if (drawEmpty){
item->draw(x_spacing_mod, y_spacing_mod, cellWidth, cellHeight, where, font);
}
} else {
item->draw(x_spacing_mod, y_spacing_mod, cellWidth, cellHeight, where, font);
}
item_iterator++;
}
x_spacing_mod+= cellSpacingX;
y_spacing_mod+= cellSpacingY + cellHeight + cellMarginY;
}
x+= cellWidth + cellMarginX;
y+= cellSpacingY;
}
break;
}
case InfiniteVertical:{
int x = startOffsetX + (cellSpacingX * gridY < 0 ? abs(cellSpacingX * gridY) : 0);
int y = startOffsetY + (cellSpacingY * gridX < 0 ? abs(cellSpacingY * gridX) : 0);
// Start off on offset
item_iterator += offset * gridX;
for (int row = 0; row < gridY; ++row){
int x_spacing_mod = x;
int y_spacing_mod = y;
for (int column = 0; column < gridX; ++column){
if (item_iterator != items.end()){
Util::ReferenceCount<SelectItem> item = *item_iterator;
if (item->isEmpty()){
if (drawEmpty){
item->draw(x_spacing_mod, y_spacing_mod, cellWidth, cellHeight, where, font);
}
} else {
item->draw(x_spacing_mod, y_spacing_mod, cellWidth, cellHeight, where, font);
}
item_iterator++;
}
x_spacing_mod += cellSpacingX + cellWidth + cellMarginX;
y_spacing_mod += cellSpacingY;
}
x += cellSpacingX;
y += cellHeight + cellMarginY;
}
break;
}
default:
break;
}
}
void GridSelect::addItem(const Util::ReferenceCount<SelectItem> & item){
items.push_back(item);
}
void GridSelect::addItems(const std::vector<Util::ReferenceCount<SelectItem> > & itemList){
items.insert(items.begin(), itemList.begin(), itemList.end());
}
const std::vector<Util::ReferenceCount<SelectItem> > & GridSelect::getItems() const{
return items;
}
const Util::ReferenceCount<SelectItem> GridSelect::getItem(unsigned int index) const{
if (index >= items.size()){
return Util::ReferenceCount<SelectItem>();
}
return items[index];
}
const Util::ReferenceCount<SelectItem> GridSelect::getItemByCursor(unsigned int cursor) const{
if (getCurrentIndex(cursor) >= items.size()){
return Util::ReferenceCount<SelectItem>();
}
return items[getCurrentIndex(cursor)];
}
void GridSelect::clearItems(){
items.clear();
}
void GridSelect::setCellDimensions(int width, int height){
cellWidth = width;
cellHeight = height;
}
void GridSelect::setCellSpacing(int x, int y){
cellSpacingX = x;
cellSpacingY = y;
}
void GridSelect::setCellMargins(int x, int y){
cellMarginX = x;
cellMarginY = y;
}
void GridSelect::setStartingOffset(int x, int y){
startOffsetX = x;
startOffsetY = y;
}
void GridSelect::setCursors(int total){
cursors.clear();
for (int i = 0; i < total; ++i){
cursors.push_back(Cursor(i, SelectListInterface::Active));
}
}
int GridSelect::totalCursors() const{
return cursors.size();
}
void GridSelect::setCurrentIndex(unsigned int cursor, unsigned int location){
if (checkCursor(cursor) && location >= items.size()){
return;
}
cursors[cursor].setIndex(location);
}
unsigned int GridSelect::getCurrentIndex(unsigned int cursor) const{
if (checkCursor(cursor)){
return 0;
}
return cursors[cursor].getIndex();
}
void GridSelect::setCurrentState(unsigned int cursor, const SelectListInterface::CursorState & state){
if (checkCursor(cursor)){
return;
}
cursors[cursor].setState(state);
}
const SelectListInterface::CursorState GridSelect::getCurrentState(unsigned int cursor) const{
if (checkCursor(cursor)){
return Invalid;
}
return cursors[cursor].getState();
}
static bool inRange(int check, int start, int end){
return (check >= start && check <= end);
}
static bool endPoint(int check, int start, int end, int increment){
for (int i = start; i <= end; i+=increment){
if (check == i){
return true;
}
}
return false;
}
static int computeOffset(int location, int width, int height){
int large = 0;
int small = 0;
if (width == height){
large = small = width;
} else if (width > height){
small = width;
large = height;
} else if (width < height){
small = height;
large = width;
}
int offset = (location/large - small) + 1;
if (offset < 0){
return 0;
}
return offset;
}
bool GridSelect::moveUp(unsigned int cursor){
switch (layout){
case Static:{
if (inRange(cursors[cursor].getIndex(), 0, gridX-1)){
if (allowWrap){
unsigned int location = (gridX * (gridY-1)) + cursors[cursor].getIndex();
if (location >= items.size()){
location = items.size()-1;
}
cursors[cursor].setIndex(location);
/*
if (!items[location]->isEmpty() || (items[location]->isEmpty() && accessEmpty)){
cursors[cursor].setIndex(location);
return true;
} else {
return false;
}
*/
} else {
return false;
}
} else {
unsigned int location = cursors[cursor].getIndex() - gridX;
cursors[cursor].setIndex(location);
/*
if (!items[location]->isEmpty() || (items[location]->isEmpty() && accessEmpty)){
cursors[cursor].setIndex(location);
return true;
} else {
return false;
}
*/
}
break;
}
case InfiniteHorizontal:{
int location = cursors[cursor].getIndex();
location--;
if (location < 0){
if (allowWrap){
location = items.size()-1;
offset = computeOffset(location, gridX, gridY);
} else {
location = 0;
}
} else {
if ((unsigned int)location < offset * gridY){
offset--;
}
}
cursors[cursor].setIndex(location);
break;
}
case InfiniteVertical:{
int location = cursors[cursor].getIndex();
location-=gridX;
if (location < 0){
if (allowWrap){
location = items.size()-1;
offset = computeOffset(location, gridX, gridY);
} else {
location = cursors[cursor].getIndex();
}
} else {
if ((unsigned int)location < offset * gridX){
offset--;
}
}
cursors[cursor].setIndex(location);
break;
}
default:
break;
}
return true;
}
bool GridSelect::up(unsigned int cursor){
if (checkCursor(cursor)){
return false;
}
- int place = cursors[cursor].getIndex();
+ unsigned int place = cursors[cursor].getIndex();
bool ok = moveUp(cursor);
if (!ok){
return ok;
}
if (!accessEmpty && getItemByCursor(cursor)->isEmpty()){
while (ok && getItemByCursor(cursor)->isEmpty() && place != cursors[cursor].getIndex()){
ok = moveUp(cursor);
}
}
/* We tried to move but failed and wrapping doesn't work, so the only alternative
* is to revert to our original place.
*/
if (!ok && !allowWrap){
cursors[cursor].setIndex(place);
}
return ok;
}
bool GridSelect::moveDown(unsigned int cursor){
switch (layout){
case Static:{
if (inRange(cursors[cursor].getIndex(), gridX * (gridY-1), gridX * gridY)){
if (allowWrap){
unsigned int location = cursors[cursor].getIndex() - (gridX * (gridY-1));
cursors[cursor].setIndex(location);
/*
if (!items[location]->isEmpty() || (items[location]->isEmpty() && accessEmpty)){
cursors[cursor].setIndex(location);
return true;
} else {
return false;
}
*/
} else {
return false;
}
} else {
unsigned int location = cursors[cursor].getIndex() + gridX;
if (location >= items.size()){
location = items.size()-1;
}
cursors[cursor].setIndex(location);
/*
if (!items[location]->isEmpty() || (items[location]->isEmpty() && accessEmpty)){
cursors[cursor].setIndex(location);
return true;
} else {
return false;
}
*/
}
break;
}
case InfiniteHorizontal:{
int location = cursors[cursor].getIndex();
location++;
if ((unsigned int)location >= items.size()){
if (allowWrap){
location = offset = 0;
}
} else {
if ((unsigned int)location > ((offset+gridX) * gridY)-1){
offset++;
}
}
cursors[cursor].setIndex(location);
break;
}
case InfiniteVertical:{
int location = cursors[cursor].getIndex();
location+=gridX;
if ((unsigned int)location >= items.size()){
if (cursors[cursor].getIndex() < items.size()-1){
location = items.size()-1;
offset = computeOffset(location, gridX, gridY);
} else if (allowWrap){
location = offset = 0;
}
} else {
if ((unsigned int)location > ((offset+gridY) * gridX)-1){
offset++;
}
}
cursors[cursor].setIndex(location);
break;
}
default:
break;
}
return true;
}
bool GridSelect::down(unsigned int cursor){
if (checkCursor(cursor)){
return false;
}
- int place = cursors[cursor].getIndex();
+ unsigned int place = cursors[cursor].getIndex();
bool ok = moveDown(cursor);
if (!ok){
return ok;
}
if (!accessEmpty && getItemByCursor(cursor)->isEmpty()){
while (ok && getItemByCursor(cursor)->isEmpty() && place != cursors[cursor].getIndex()){
ok = moveDown(cursor);
}
}
if (!ok && !allowWrap){
cursors[cursor].setIndex(place);
}
return ok;
}
bool GridSelect::moveLeft(unsigned int cursor){
switch (layout){
case Static:{
if (endPoint(cursors[cursor].getIndex(), 0, gridX * gridY, gridX)){
if (allowWrap){
unsigned int location = cursors[cursor].getIndex() + gridX-1;
if (location >= items.size()){
location = items.size()-1;
}
cursors[cursor].setIndex(location);
/*
if (!items[location]->isEmpty() || (items[location]->isEmpty() && accessEmpty)){
cursors[cursor].setIndex(location);
return true;
} else {
return false;
}
*/
} else {
return false;
}
} else {
/* FIXME: I think this is wrong.. */
cursors[cursor].decrement();
/*
if (items[cursors[cursor].getIndex()]->isEmpty() && !accessEmpty){
cursors[cursor].increment();
return false;
}
return true;
*/
}
break;
}
case InfiniteHorizontal:{
int location = cursors[cursor].getIndex();
location-=gridY;
if (location < 0){
if (allowWrap){
location = items.size()-1;
offset = computeOffset(location, gridX, gridY);
} else {
location = cursors[cursor].getIndex();
}
} else {
if ((unsigned int)location < offset * gridY){
offset--;
}
}
cursors[cursor].setIndex(location);
break;
}
case InfiniteVertical:{
int location = cursors[cursor].getIndex();
location--;
if (location < 0){
if (allowWrap){
location = items.size()-1;
offset = computeOffset(location, gridX, gridY);
} else {
location = 0;
}
} else {
if ((unsigned int)location < offset * gridX){
offset--;
}
}
cursors[cursor].setIndex(location);
break;
}
default:
break;
}
return true;
}
bool GridSelect::left(unsigned int cursor){
if (checkCursor(cursor)){
return false;
}
- int place = cursors[cursor].getIndex();
+ unsigned int place = cursors[cursor].getIndex();
bool ok = moveLeft(cursor);
if (!ok){
return ok;
}
if (!accessEmpty && getItemByCursor(cursor)->isEmpty()){
while (ok && getItemByCursor(cursor)->isEmpty() && place != cursors[cursor].getIndex()){
ok = moveLeft(cursor);
}
}
if (!ok && !allowWrap){
cursors[cursor].setIndex(place);
}
return ok;
}
bool GridSelect::moveRight(unsigned int cursor){
switch (layout){
case Static:{
if (endPoint(cursors[cursor].getIndex(), gridX-1, gridX * gridY, gridX)){
if (allowWrap){
int location = cursors[cursor].getIndex() - gridX+1;
if (location < 0){
location = 0;
}
cursors[cursor].setIndex(location);
/*
if (!items[location]->isEmpty() || (items[location]->isEmpty() && accessEmpty)){
cursors[cursor].setIndex(location);
return true;
} else {
return false;
}
*/
} else {
return false;
}
} else {
unsigned int location = cursors[cursor].getIndex()+1;
if (location >= items.size()){
if (allowWrap){
location = (gridX * gridY) - gridX;
} else {
location = items.size()-1;
}
}
cursors[cursor].setIndex(location);
/*
if (!items[location]->isEmpty() || (items[location]->isEmpty() && accessEmpty)){
cursors[cursor].setIndex(location);
return true;
} else {
return false;
}
*/
}
break;
}
case InfiniteHorizontal:{
int location = cursors[cursor].getIndex();
location+=gridY;
if ((unsigned int)location >= items.size()){
if (cursors[cursor].getIndex() < items.size()-1){
location = items.size()-1;
offset = computeOffset(location, gridX, gridY);
} else if (allowWrap){
location = offset = 0;
}
} else {
if ((unsigned int)location > ((offset+gridX) * gridY)-1){
offset++;
}
}
cursors[cursor].setIndex(location);
break;
}
case InfiniteVertical:{
int location = cursors[cursor].getIndex();
location++;
if ((unsigned int)location >= items.size()){
if (allowWrap){
location = offset = 0;
}
} else {
if ((unsigned int)location > ((offset+gridY) * gridX)-1){
offset++;
}
}
cursors[cursor].setIndex(location);
break;
}
default:
break;
}
return true;
}
bool GridSelect::right(unsigned int cursor){
if (checkCursor(cursor)){
return false;
}
- int place = cursors[cursor].getIndex();
+ unsigned int place = cursors[cursor].getIndex();
bool ok = moveRight(cursor);
if (!ok){
return ok;
}
if (!accessEmpty && getItemByCursor(cursor)->isEmpty()){
while (ok && getItemByCursor(cursor)->isEmpty() && place != cursors[cursor].getIndex()){
ok = moveRight(cursor);
}
}
if (!ok && !allowWrap){
cursors[cursor].setIndex(place);
}
return ok;
}
bool GridSelect::hasMoreLow() const{
return (offset > 0);
}
bool GridSelect::hasMoreHigh() const{
if (!cursors.empty()){
switch (layout){
case InfiniteHorizontal:{
const unsigned int location = (offset * gridY) + (gridX * gridY);
if (location < items.size() && location > ((offset+gridX) * gridY)-1){
return true;
}
break;
}
case InfiniteVertical:{
const unsigned int location = (offset * gridX) + (gridX * gridY);
if (location < items.size() && location > ((offset+gridY) * gridX)-1){
return true;
}
break;
}
case Static:
default:
break;
}
}
return false;
}
int GridSelect::getWidth(){
return (cellWidth+cellMarginX+cellSpacingX) * gridX;
}
int GridSelect::getHeight(){
return (cellHeight+cellMarginY+cellSpacingY) * gridY;
}
bool GridSelect::checkCursor(unsigned int cursor) const {
return ((unsigned int)cursor >= cursors.size());
}
diff --git a/src/input/input-manager.cpp b/src/input/input-manager.cpp
index d7852976..05f194c0 100644
--- a/src/input/input-manager.cpp
+++ b/src/input/input-manager.cpp
@@ -1,167 +1,171 @@
#include "r-tech1/input/input-manager.h"
#include "r-tech1/configuration.h"
#include "r-tech1/input/joystick.h"
#include "r-tech1/events.h"
#include "r-tech1/debug.h"
#include <stdlib.h>
#include <vector>
using namespace std;
InputManager * InputManager::manager = 0;
InputManager::InputManager():
capture(0){
manager = this;
if (Configuration::isJoystickEnabled()){
installJoysticks();
}
touch = DeviceInput::getTouchDevice();
}
const Util::ReferenceCount<DeviceInput::Touch> & InputManager::getTouch(){
if (manager != NULL){
return manager->touch;
}
+
+ throw 1;
}
void InputManager::installJoysticks(){
joysticks.clear();
for (int i = 0; i < Joystick::numberOfJoysticks(); i++){
joysticks[i] = Joystick::create(i);
}
}
const std::map<int, Util::ReferenceCount<Joystick> > & InputManager::getJoysticks(){
if (manager != NULL){
return manager->joysticks;
}
Global::debug(0) << "*BUG* Input manager not set up" << endl;
exit(0);
}
#ifdef PS3
#include <io/pad.h>
extern "C" int SDL_JoystickInit();
extern "C" int SDL_JoystickQuit();
#endif
static bool needJoystickUpdate(){
#ifdef PS3
padInfo pad;
if (ioPadGetInfo(&pad) == 0){
/* re-initialize the joystick stuff if we have a different
* number of joysticks according to lv2
*/
if (pad.connected != (unsigned) SDL_NumJoysticks()){
SDL_JoystickQuit();
SDL_JoystickInit();
return true;
}
}
#endif
return false;
}
void InputManager::checkJoysticks(){
if (needJoystickUpdate()){
installJoysticks();
}
}
InputManager::~InputManager(){
}
void InputManager::deferResizeEvents(bool defer){
if (manager != NULL){
manager->eventManager.deferResizeEvents(defer);
}
}
bool InputManager::anyInput(){
if (manager == 0){
Global::debug(0) << "*BUG* Input manager not set up" << endl;
exit(0);
}
return manager->_anyInput();
}
bool InputManager::_anyInput(){
if (keyboard.keypressed()){
return true;
}
for (map<int, Util::ReferenceCount<Joystick> >::iterator it = joysticks.begin(); it != joysticks.end(); it++){
Util::ReferenceCount<Joystick> joystick = it->second;
if (joystick != NULL){
return joystick->pressed();
}
}
return false;
}
void InputManager::waitForClear(){
manager->keyboard.clear();
while (anyInput()){
poll();
Util::rest(1);
}
}
void InputManager::waitForKeys(int key1, int key2, const InputSource & source){
InputMap<int> wait;
wait.set(key1, 0, false, 1);
wait.set(key2, 0, false, 1);
InputManager::waitForRelease(wait, source, 1);
InputManager::waitForPress(wait, source, 1);
InputManager::waitForRelease(wait, source, 1);
}
void InputManager::waitForKeys(int key, const InputSource & source){
InputMap<int> wait;
wait.set(key, 0, false, 1);
InputManager::waitForRelease(wait, source, 1);
InputManager::waitForPress(wait, source, 1);
InputManager::waitForRelease(wait, source, 1);
}
void InputManager::poll(){
if (manager == 0){
Global::debug(0) << "*BUG* Input manager not set up" << endl;
exit(0);
}
return manager->_poll();
}
int InputManager::readKey(){
return manager->_readKey();
}
int InputManager::_readKey(){
std::vector<int> keys;
do{
keyboard.readKeys(keys);
if (keys.size() == 0){
Util::rest(1);
poll();
}
} while (keys.size() == 0);
return keys.front();
}
std::vector<DeviceInput::Touch::Event> InputManager::getTouchEvents(){
if (touch != NULL){
return touch->getEvents();
}
+
+ throw 1;
}
void InputManager::_poll(){
#ifdef PS3
checkJoysticks();
#endif
touch->poll();
eventManager.run(keyboard, joysticks);
}
diff --git a/src/libs/gme/Ay_Cpu.cpp b/src/libs/gme/Ay_Cpu.cpp
index 0f67db1b..c11dd437 100644
--- a/src/libs/gme/Ay_Cpu.cpp
+++ b/src/libs/gme/Ay_Cpu.cpp
@@ -1,1665 +1,1666 @@
// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
/*
Last validated with zexall 2006.11.21 5:26 PM
* Doesn't implement the R register or immediate interrupt after EI.
* Address wrap-around isn't completely correct, but is prevented from crashing emulator.
*/
#include "Ay_Cpu.h"
#include "blargg_endian.h"
#include <string.h>
//#include "z80_cpu_log.h"
/* Copyright (C) 2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#define SYNC_TIME() (void) (s.time = s_time)
#define RELOAD_TIME() (void) (s_time = s.time)
// Callbacks to emulator
#define CPU_OUT( cpu, addr, data, TIME )\
ay_cpu_out( cpu, TIME, addr, data )
#define CPU_IN( cpu, addr, TIME )\
ay_cpu_in( cpu, addr )
#include "blargg_source.h"
// flags, named with hex value for clarity
int const S80 = 0x80;
int const Z40 = 0x40;
int const F20 = 0x20;
int const H10 = 0x10;
int const F08 = 0x08;
int const V04 = 0x04;
int const P04 = 0x04;
int const N02 = 0x02;
int const C01 = 0x01;
#define SZ28P( n ) szpc [n]
#define SZ28PC( n ) szpc [n]
#define SZ28C( n ) (szpc [n] & ~P04)
#define SZ28( n ) SZ28C( n )
#define SET_R( n ) (void) (r.r = n)
#define GET_R() (r.r)
Ay_Cpu::Ay_Cpu()
{
state = &state_;
for ( int i = 0x100; --i >= 0; )
{
int even = 1;
for ( int p = i; p; p >>= 1 )
even ^= p;
int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04);
szpc [i] = n;
szpc [i + 0x100] = n | C01;
}
szpc [0x000] |= Z40;
szpc [0x100] |= Z40;
}
void Ay_Cpu::reset( void* m )
{
mem = (uint8_t*) m;
check( state == &state_ );
state = &state_;
state_.time = 0;
state_.base = 0;
end_time_ = 0;
memset( &r, 0, sizeof r );
}
#define TIME (s_time + s.base)
#define READ_PROG( addr ) (mem [addr])
#define INSTR( offset ) READ_PROG( pc + (offset) )
#define GET_ADDR() GET_LE16( &READ_PROG( pc ) )
#define READ( addr ) READ_PROG( addr )
#define WRITE( addr, data ) (void) (READ_PROG( addr ) = data)
#define READ_WORD( addr ) GET_LE16( &READ_PROG( addr ) )
#define WRITE_WORD( addr, data ) SET_LE16( &READ_PROG( addr ), data )
#define IN( addr ) CPU_IN( this, addr, TIME )
#define OUT( addr, data ) CPU_OUT( this, addr, data, TIME )
#if BLARGG_BIG_ENDIAN
#define R8( n, offset ) ((r8_ - offset) [n])
#elif BLARGG_LITTLE_ENDIAN
#define R8( n, offset ) ((r8_ - offset) [(n) ^ 1])
#else
#error "Byte order of CPU must be known"
#endif
//#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)])
// help compiler see that it can just adjust stack offset, saving an extra instruction
#define R16( n, shift, offset )\
(*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1))))
#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e
#define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f
#define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g
#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h
// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8
static byte const ed_dd_timing [0x100] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00,
0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,
0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,
0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,
0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,
0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,
0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
};
// even on x86, using short and unsigned char was slower
typedef int fint16;
typedef unsigned fuint16;
typedef unsigned fuint8;
bool Ay_Cpu::run( cpu_time_t end_time )
{
set_end_time( end_time );
state_t s = this->state_;
this->state = &s;
bool warning = false;
typedef BOOST::int8_t int8_t;
union {
regs_t rg;
pairs_t rp;
uint8_t r8_ [8]; // indexed
uint16_t r16_ [4];
};
rg = this->r.b;
cpu_time_t s_time = s.time;
uint8_t* const mem = this->mem; // cache
fuint16 pc = r.pc;
fuint16 sp = r.sp;
fuint16 ix = r.ix; // TODO: keep in memory for direct access?
fuint16 iy = r.iy;
int flags = r.b.flags;
goto loop;
jr_not_taken:
s_time -= 5;
goto loop;
call_not_taken:
s_time -= 7;
jp_not_taken:
pc += 2;
loop:
check( (unsigned long) pc < 0x10000 );
check( (unsigned long) sp < 0x10000 );
check( (unsigned) flags < 0x100 );
check( (unsigned) ix < 0x10000 );
check( (unsigned) iy < 0x10000 );
fuint8 opcode;
opcode = READ_PROG( pc );
pc++;
static byte const base_timing [0x100] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0
13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1
12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2
12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6
7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B
11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C
11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D
11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E
11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F
};
fuint16 data;
data = base_timing [opcode];
if ( (s_time += data) >= 0 )
goto possibly_out_of_time;
almost_out_of_time:
data = READ_PROG( pc );
#ifdef Z80_CPU_LOG_H
//log_opcode( opcode, READ_PROG( pc ) );
z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy );
z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ),
READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) );
#endif
switch ( opcode )
{
possibly_out_of_time:
if ( s_time < (int) data )
goto almost_out_of_time;
s_time -= data;
goto out_of_time;
// Common
case 0x00: // NOP
CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc.
goto loop;
case 0x08:{// EX AF,AF'
int temp = r.alt.b.a;
r.alt.b.a = rg.a;
rg.a = temp;
temp = r.alt.b.flags;
r.alt.b.flags = flags;
flags = temp;
goto loop;
}
case 0xD3: // OUT (imm),A
pc++;
OUT( data + rg.a * 0x100, rg.a );
goto loop;
case 0x2E: // LD L,imm
pc++;
rg.l = data;
goto loop;
case 0x3E: // LD A,imm
pc++;
rg.a = data;
goto loop;
case 0x3A:{// LD A,(addr)
fuint16 addr = GET_ADDR();
pc += 2;
rg.a = READ( addr );
goto loop;
}
// Conditional
#define ZERO (flags & Z40)
#define CARRY (flags & C01)
#define EVEN (flags & P04)
#define MINUS (flags & S80)
// JR
#define JR( cond ) {\
int disp = (BOOST::int8_t) data;\
pc++;\
if ( !(cond) )\
goto jr_not_taken;\
pc += disp;\
goto loop;\
}
case 0x20: JR( !ZERO ) // JR NZ,disp
case 0x28: JR( ZERO ) // JR Z,disp
case 0x30: JR( !CARRY ) // JR NC,disp
case 0x38: JR( CARRY ) // JR C,disp
case 0x18: JR( true ) // JR disp
case 0x10:{// DJNZ disp
int temp = rg.b - 1;
rg.b = temp;
JR( temp )
}
// JP
#define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop;
case 0xC2: JP( !ZERO ) // JP NZ,addr
case 0xCA: JP( ZERO ) // JP Z,addr
case 0xD2: JP( !CARRY ) // JP NC,addr
case 0xDA: JP( CARRY ) // JP C,addr
case 0xE2: JP( !EVEN ) // JP PO,addr
case 0xEA: JP( EVEN ) // JP PE,addr
case 0xF2: JP( !MINUS ) // JP P,addr
case 0xFA: JP( MINUS ) // JP M,addr
case 0xC3: // JP addr
pc = GET_ADDR();
goto loop;
case 0xE9: // JP HL
pc = rp.hl;
goto loop;
// RET
#define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop;
case 0xC0: RET( !ZERO ) // RET NZ
case 0xC8: RET( ZERO ) // RET Z
case 0xD0: RET( !CARRY ) // RET NC
case 0xD8: RET( CARRY ) // RET C
case 0xE0: RET( !EVEN ) // RET PO
case 0xE8: RET( EVEN ) // RET PE
case 0xF0: RET( !MINUS ) // RET P
case 0xF8: RET( MINUS ) // RET M
case 0xC9: // RET
ret_taken:
pc = READ_WORD( sp );
sp = uint16_t (sp + 2);
goto loop;
// CALL
#define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken;
case 0xC4: CALL( !ZERO ) // CALL NZ,addr
case 0xCC: CALL( ZERO ) // CALL Z,addr
case 0xD4: CALL( !CARRY ) // CALL NC,addr
case 0xDC: CALL( CARRY ) // CALL C,addr
case 0xE4: CALL( !EVEN ) // CALL PO,addr
case 0xEC: CALL( EVEN ) // CALL PE,addr
case 0xF4: CALL( !MINUS ) // CALL P,addr
case 0xFC: CALL( MINUS ) // CALL M,addr
case 0xCD:{// CALL addr
call_taken:
fuint16 addr = pc + 2;
pc = GET_ADDR();
sp = uint16_t (sp - 2);
WRITE_WORD( sp, addr );
goto loop;
}
case 0xFF: // RST
if ( (pc - 1) > 0xFFFF )
{
pc = uint16_t (pc - 1);
s_time -= 11;
goto loop;
}
CASE7( C7, CF, D7, DF, E7, EF, F7 ):
data = pc;
pc = opcode & 0x38;
goto push_data;
// PUSH/POP
case 0xF5: // PUSH AF
data = rg.a * 0x100u + flags;
goto push_data;
case 0xC5: // PUSH BC
case 0xD5: // PUSH DE
case 0xE5: // PUSH HL
data = R16( opcode, 4, 0xC5 );
push_data:
sp = uint16_t (sp - 2);
WRITE_WORD( sp, data );
goto loop;
case 0xF1: // POP AF
flags = READ( sp );
rg.a = READ( sp + 1 );
sp = uint16_t (sp + 2);
goto loop;
case 0xC1: // POP BC
case 0xD1: // POP DE
case 0xE1: // POP HL
R16( opcode, 4, 0xC1 ) = READ_WORD( sp );
sp = uint16_t (sp + 2);
goto loop;
// ADC/ADD/SBC/SUB
case 0x96: // SUB (HL)
case 0x86: // ADD (HL)
flags &= ~C01;
case 0x9E: // SBC (HL)
case 0x8E: // ADC (HL)
data = READ( rp.hl );
goto adc_data;
case 0xD6: // SUB A,imm
case 0xC6: // ADD imm
flags &= ~C01;
case 0xDE: // SBC A,imm
case 0xCE: // ADC imm
pc++;
goto adc_data;
CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r
CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r
flags &= ~C01;
CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r
CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r
data = R8( opcode & 7, 0 );
adc_data: {
int result = data + (flags & C01);
data ^= rg.a;
flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes
if ( flags )
result = -result;
result += rg.a;
data ^= result;
flags |=(data & H10) |
((data - -0x80) >> 6 & V04) |
SZ28C( result & 0x1FF );
rg.a = result;
goto loop;
}
// CP
case 0xBE: // CP (HL)
data = READ( rp.hl );
goto cp_data;
case 0xFE: // CP imm
pc++;
goto cp_data;
CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r
data = R8( opcode, 0xB8 );
cp_data: {
int result = rg.a - data;
flags = N02 | (data & (F20 | F08)) | (result >> 8 & C01);
data ^= rg.a;
flags |=(((result ^ rg.a) & data) >> 5 & V04) |
(((data & H10) ^ result) & (S80 | H10));
if ( (uint8_t) result )
goto loop;
flags |= Z40;
goto loop;
}
// ADD HL,rp
case 0x39: // ADD HL,SP
data = sp;
goto add_hl_data;
case 0x09: // ADD HL,BC
case 0x19: // ADD HL,DE
case 0x29: // ADD HL,HL
data = R16( opcode, 4, 0x09 );
add_hl_data: {
blargg_ulong sum = rp.hl + data;
data ^= rp.hl;
rp.hl = sum;
flags = (flags & (S80 | Z40 | V04)) |
(sum >> 16) |
(sum >> 8 & (F20 | F08)) |
((data ^ sum) >> 8 & H10);
goto loop;
}
case 0x27:{// DAA
int a = rg.a;
if ( a > 0x99 )
flags |= C01;
int adjust = 0x60 & -(flags & C01);
if ( flags & H10 || (a & 0x0F) > 9 )
adjust |= 0x06;
if ( flags & N02 )
adjust = -adjust;
a += adjust;
flags = (flags & (C01 | N02)) |
((rg.a ^ a) & H10) |
SZ28P( (uint8_t) a );
rg.a = a;
goto loop;
}
/*
case 0x27:{// DAA
// more optimized, but probably not worth the obscurity
int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags
int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0
if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9
adjust |= 0x06;
if ( f & N02 )
adjust = -adjust;
int a = rg.a + adjust;
flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a );
rg.a = a;
goto loop;
}
*/
// INC/DEC
case 0x34: // INC (HL)
data = READ( rp.hl ) + 1;
WRITE( rp.hl, data );
goto inc_set_flags;
CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r
data = ++R8( opcode >> 3, 0 );
inc_set_flags:
flags = (flags & C01) |
(((data & 0x0F) - 1) & H10) |
SZ28( (uint8_t) data );
if ( data != 0x80 )
goto loop;
flags |= V04;
goto loop;
case 0x35: // DEC (HL)
data = READ( rp.hl ) - 1;
WRITE( rp.hl, data );
goto dec_set_flags;
CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r
data = --R8( opcode >> 3, 0 );
dec_set_flags:
flags = (flags & C01) | N02 |
(((data & 0x0F) + 1) & H10) |
SZ28( (uint8_t) data );
if ( data != 0x7F )
goto loop;
flags |= V04;
goto loop;
case 0x03: // INC BC
case 0x13: // INC DE
case 0x23: // INC HL
R16( opcode, 4, 0x03 )++;
goto loop;
case 0x33: // INC SP
sp = uint16_t (sp + 1);
goto loop;
case 0x0B: // DEC BC
case 0x1B: // DEC DE
case 0x2B: // DEC HL
R16( opcode, 4, 0x0B )--;
goto loop;
case 0x3B: // DEC SP
sp = uint16_t (sp - 1);
goto loop;
// AND
case 0xA6: // AND (HL)
data = READ( rp.hl );
goto and_data;
case 0xE6: // AND imm
pc++;
goto and_data;
CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r
data = R8( opcode, 0xA0 );
and_data:
rg.a &= data;
flags = SZ28P( rg.a ) | H10;
goto loop;
// OR
case 0xB6: // OR (HL)
data = READ( rp.hl );
goto or_data;
case 0xF6: // OR imm
pc++;
goto or_data;
CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r
data = R8( opcode, 0xB0 );
or_data:
rg.a |= data;
flags = SZ28P( rg.a );
goto loop;
// XOR
case 0xAE: // XOR (HL)
data = READ( rp.hl );
goto xor_data;
case 0xEE: // XOR imm
pc++;
goto xor_data;
CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r
data = R8( opcode, 0xA8 );
xor_data:
rg.a ^= data;
flags = SZ28P( rg.a );
goto loop;
// LD
CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r
WRITE( rp.hl, R8( opcode, 0x70 ) );
goto loop;
CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r
CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r
CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r
CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r
CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r
CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r
CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r
R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 );
goto loop;
CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm
R8( opcode >> 3, 0 ) = data;
pc++;
goto loop;
case 0x36: // LD (HL),imm
pc++;
WRITE( rp.hl, data );
goto loop;
CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL)
R8( opcode >> 3, 8 ) = READ( rp.hl );
goto loop;
case 0x01: // LD rp,imm
case 0x11:
case 0x21:
R16( opcode, 4, 0x01 ) = GET_ADDR();
pc += 2;
goto loop;
case 0x31: // LD sp,imm
sp = GET_ADDR();
pc += 2;
goto loop;
case 0x2A:{// LD HL,(addr)
fuint16 addr = GET_ADDR();
pc += 2;
rp.hl = READ_WORD( addr );
goto loop;
}
case 0x32:{// LD (addr),A
fuint16 addr = GET_ADDR();
pc += 2;
WRITE( addr, rg.a );
goto loop;
}
case 0x22:{// LD (addr),HL
fuint16 addr = GET_ADDR();
pc += 2;
WRITE_WORD( addr, rp.hl );
goto loop;
}
case 0x02: // LD (BC),A
case 0x12: // LD (DE),A
WRITE( R16( opcode, 4, 0x02 ), rg.a );
goto loop;
case 0x0A: // LD A,(BC)
case 0x1A: // LD A,(DE)
rg.a = READ( R16( opcode, 4, 0x0A ) );
goto loop;
case 0xF9: // LD SP,HL
sp = rp.hl;
goto loop;
// Rotate
case 0x07:{// RLCA
fuint16 temp = rg.a;
temp = (temp << 1) | (temp >> 7);
flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08 | C01));
rg.a = temp;
goto loop;
}
case 0x0F:{// RRCA
fuint16 temp = rg.a;
flags = (flags & (S80 | Z40 | P04)) |
(temp & C01);
temp = (temp << 7) | (temp >> 1);
flags |= temp & (F20 | F08);
rg.a = temp;
goto loop;
}
case 0x17:{// RLA
blargg_ulong temp = (rg.a << 1) | (flags & C01);
flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08)) |
(temp >> 8);
rg.a = temp;
goto loop;
}
case 0x1F:{// RRA
fuint16 temp = (flags << 7) | (rg.a >> 1);
flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08)) |
(rg.a & C01);
rg.a = temp;
goto loop;
}
// Misc
case 0x2F:{// CPL
fuint16 temp = ~rg.a;
flags = (flags & (S80 | Z40 | P04 | C01)) |
(temp & (F20 | F08)) |
(H10 | N02);
rg.a = temp;
goto loop;
}
case 0x3F:{// CCF
flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) |
(flags << 4 & H10) |
(rg.a & (F20 | F08));
goto loop;
}
case 0x37: // SCF
flags = (flags & (S80 | Z40 | P04)) | C01 |
(rg.a & (F20 | F08));
goto loop;
case 0xDB: // IN A,(imm)
pc++;
rg.a = IN( data + rg.a * 0x100 );
goto loop;
case 0xE3:{// EX (SP),HL
fuint16 temp = READ_WORD( sp );
WRITE_WORD( sp, rp.hl );
rp.hl = temp;
goto loop;
}
case 0xEB:{// EX DE,HL
fuint16 temp = rp.hl;
rp.hl = rp.de;
rp.de = temp;
goto loop;
}
case 0xD9:{// EXX DE,HL
fuint16 temp = r.alt.w.bc;
r.alt.w.bc = rp.bc;
rp.bc = temp;
temp = r.alt.w.de;
r.alt.w.de = rp.de;
rp.de = temp;
temp = r.alt.w.hl;
r.alt.w.hl = rp.hl;
rp.hl = temp;
goto loop;
}
case 0xF3: // DI
r.iff1 = 0;
r.iff2 = 0;
goto loop;
case 0xFB: // EI
r.iff1 = 1;
r.iff2 = 1;
// TODO: delayed effect
goto loop;
case 0x76: // HALT
goto halt;
//////////////////////////////////////// CB prefix
{
case 0xCB:
unsigned data2;
data2 = INSTR( 1 );
+ data2 = data2;
pc++;
switch ( data )
{
// Rotate left
#define RLC( read, write ) {\
fuint8 result = read;\
result = uint8_t (result << 1) | (result >> 7);\
flags = SZ28P( result ) | (result & C01);\
write;\
goto loop;\
}
case 0x06: // RLC (HL)
s_time += 7;
data = rp.hl;
rlc_data_addr:
RLC( READ( data ), WRITE( data, result ) )
CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r
uint8_t& reg = R8( data, 0 );
RLC( reg, reg = result )
}
#define RL( read, write ) {\
fuint16 result = (read << 1) | (flags & C01);\
flags = SZ28PC( result );\
write;\
goto loop;\
}
case 0x16: // RL (HL)
s_time += 7;
data = rp.hl;
rl_data_addr:
RL( READ( data ), WRITE( data, result ) )
CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r
uint8_t& reg = R8( data, 0x10 );
RL( reg, reg = result )
}
#define SLA( read, add, write ) {\
fuint16 result = (read << 1) | add;\
flags = SZ28PC( result );\
write;\
goto loop;\
}
case 0x26: // SLA (HL)
s_time += 7;
data = rp.hl;
sla_data_addr:
SLA( READ( data ), 0, WRITE( data, result ) )
CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r
uint8_t& reg = R8( data, 0x20 );
SLA( reg, 0, reg = result )
}
case 0x36: // SLL (HL)
s_time += 7;
data = rp.hl;
sll_data_addr:
SLA( READ( data ), 1, WRITE( data, result ) )
CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r
uint8_t& reg = R8( data, 0x30 );
SLA( reg, 1, reg = result )
}
// Rotate right
#define RRC( read, write ) {\
fuint8 result = read;\
flags = result & C01;\
result = uint8_t (result << 7) | (result >> 1);\
flags |= SZ28P( result );\
write;\
goto loop;\
}
case 0x0E: // RRC (HL)
s_time += 7;
data = rp.hl;
rrc_data_addr:
RRC( READ( data ), WRITE( data, result ) )
CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r
uint8_t& reg = R8( data, 0x08 );
RRC( reg, reg = result )
}
#define RR( read, write ) {\
fuint8 result = read;\
fuint8 temp = result & C01;\
result = uint8_t (flags << 7) | (result >> 1);\
flags = SZ28P( result ) | temp;\
write;\
goto loop;\
}
case 0x1E: // RR (HL)
s_time += 7;
data = rp.hl;
rr_data_addr:
RR( READ( data ), WRITE( data, result ) )
CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r
uint8_t& reg = R8( data, 0x18 );
RR( reg, reg = result )
}
#define SRA( read, write ) {\
fuint8 result = read;\
flags = result & C01;\
result = (result & 0x80) | (result >> 1);\
flags |= SZ28P( result );\
write;\
goto loop;\
}
case 0x2E: // SRA (HL)
data = rp.hl;
s_time += 7;
sra_data_addr:
SRA( READ( data ), WRITE( data, result ) )
CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r
uint8_t& reg = R8( data, 0x28 );
SRA( reg, reg = result )
}
#define SRL( read, write ) {\
fuint8 result = read;\
flags = result & C01;\
result >>= 1;\
flags |= SZ28P( result );\
write;\
goto loop;\
}
case 0x3E: // SRL (HL)
s_time += 7;
data = rp.hl;
srl_data_addr:
SRL( READ( data ), WRITE( data, result ) )
CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r
uint8_t& reg = R8( data, 0x38 );
SRL( reg, reg = result )
}
// BIT
{
unsigned temp;
CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL)
s_time += 4;
temp = READ( rp.hl );
flags &= C01;
goto bit_temp;
CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r
CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r
CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r
CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r
CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r
CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r
CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r
CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r
temp = R8( data & 7, 0 );
flags = (flags & C01) | (temp & (F20 | F08));
bit_temp:
int masked = temp & 1 << (data >> 3 & 7);
flags |=(masked & S80) | H10 |
((masked - 1) >> 8 & (Z40 | P04));
goto loop;
}
// SET/RES
CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL)
CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL)
s_time += 7;
int temp = READ( rp.hl );
int bit = 1 << (data >> 3 & 7);
temp |= bit; // SET
if ( !(data & 0x40) )
temp ^= bit; // RES
WRITE( rp.hl, temp );
goto loop;
}
CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r
CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r
CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r
CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r
CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r
CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r
CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r
CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r
R8( data & 7, 0 ) |= 1 << (data >> 3 & 7);
goto loop;
CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r
CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r
CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r
CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r
CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r
CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r
CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r
CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r
R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7));
goto loop;
}
assert( false );
}
//////////////////////////////////////// ED prefix
{
case 0xED:
pc++;
s_time += ed_dd_timing [data] >> 4;
switch ( data )
{
{
blargg_ulong temp;
case 0x72: // SBC HL,SP
case 0x7A: // ADC HL,SP
temp = sp;
if ( 0 )
case 0x42: // SBC HL,BC
case 0x52: // SBC HL,DE
case 0x62: // SBC HL,HL
case 0x4A: // ADC HL,BC
case 0x5A: // ADC HL,DE
case 0x6A: // ADC HL,HL
temp = R16( data >> 3 & 6, 1, 0 );
blargg_ulong sum = temp + (flags & C01);
flags = ~data >> 2 & N02;
if ( flags )
sum = -sum;
sum += rp.hl;
temp ^= rp.hl;
temp ^= sum;
flags |=(sum >> 16 & C01) |
(temp >> 8 & H10) |
(sum >> 8 & (S80 | F20 | F08)) |
((temp - -0x8000) >> 14 & V04);
rp.hl = sum;
if ( (uint16_t) sum )
goto loop;
flags |= Z40;
goto loop;
}
CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C)
int temp = IN( rp.bc );
R8( data >> 3, 8 ) = temp;
flags = (flags & C01) | SZ28P( temp );
goto loop;
}
case 0x71: // OUT (C),0
rg.flags = 0;
CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r
OUT( rp.bc, R8( data >> 3, 8 ) );
goto loop;
{
unsigned temp;
case 0x73: // LD (ADDR),SP
temp = sp;
if ( 0 )
case 0x43: // LD (ADDR),BC
case 0x53: // LD (ADDR),DE
temp = R16( data, 4, 0x43 );
fuint16 addr = GET_ADDR();
pc += 2;
WRITE_WORD( addr, temp );
goto loop;
}
case 0x4B: // LD BC,(ADDR)
case 0x5B:{// LD DE,(ADDR)
fuint16 addr = GET_ADDR();
pc += 2;
R16( data, 4, 0x4B ) = READ_WORD( addr );
goto loop;
}
case 0x7B:{// LD SP,(ADDR)
fuint16 addr = GET_ADDR();
pc += 2;
sp = READ_WORD( addr );
goto loop;
}
case 0x67:{// RRD
fuint8 temp = READ( rp.hl );
WRITE( rp.hl, (rg.a << 4) | (temp >> 4) );
temp = (rg.a & 0xF0) | (temp & 0x0F);
flags = (flags & C01) | SZ28P( temp );
rg.a = temp;
goto loop;
}
case 0x6F:{// RLD
fuint8 temp = READ( rp.hl );
WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) );
temp = (rg.a & 0xF0) | (temp >> 4);
flags = (flags & C01) | SZ28P( temp );
rg.a = temp;
goto loop;
}
CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG
opcode = 0x10; // flag to do SBC instead of ADC
flags &= ~C01;
data = rg.a;
rg.a = 0;
goto adc_data;
{
int inc;
case 0xA9: // CPD
case 0xB9: // CPDR
inc = -1;
if ( 0 )
case 0xA1: // CPI
case 0xB1: // CPIR
inc = +1;
fuint16 addr = rp.hl;
rp.hl = addr + inc;
int temp = READ( addr );
int result = rg.a - temp;
flags = (flags & C01) | N02 |
((((temp ^ rg.a) & H10) ^ result) & (S80 | H10));
if ( !(uint8_t) result ) flags |= Z40;
result -= (flags & H10) >> 4;
flags |= result & F08;
flags |= result << 4 & F20;
if ( !--rp.bc )
goto loop;
flags |= V04;
if ( flags & Z40 || data < 0xB0 )
goto loop;
pc -= 2;
s_time += 5;
goto loop;
}
{
int inc;
case 0xA8: // LDD
case 0xB8: // LDDR
inc = -1;
if ( 0 )
case 0xA0: // LDI
case 0xB0: // LDIR
inc = +1;
fuint16 addr = rp.hl;
rp.hl = addr + inc;
int temp = READ( addr );
addr = rp.de;
rp.de = addr + inc;
WRITE( addr, temp );
temp += rg.a;
flags = (flags & (S80 | Z40 | C01)) |
(temp & F08) | (temp << 4 & F20);
if ( !--rp.bc )
goto loop;
flags |= V04;
if ( data < 0xB0 )
goto loop;
pc -= 2;
s_time += 5;
goto loop;
}
{
int inc;
case 0xAB: // OUTD
case 0xBB: // OTDR
inc = -1;
if ( 0 )
case 0xA3: // OUTI
case 0xB3: // OTIR
inc = +1;
fuint16 addr = rp.hl;
rp.hl = addr + inc;
int temp = READ( addr );
int b = --rg.b;
flags = (temp >> 6 & N02) | SZ28( b );
if ( b && data >= 0xB0 )
{
pc -= 2;
s_time += 5;
}
OUT( rp.bc, temp );
goto loop;
}
{
int inc;
case 0xAA: // IND
case 0xBA: // INDR
inc = -1;
if ( 0 )
case 0xA2: // INI
case 0xB2: // INIR
inc = +1;
fuint16 addr = rp.hl;
rp.hl = addr + inc;
int temp = IN( rp.bc );
int b = --rg.b;
flags = (temp >> 6 & N02) | SZ28( b );
if ( b && data >= 0xB0 )
{
pc -= 2;
s_time += 5;
}
WRITE( addr, temp );
goto loop;
}
case 0x47: // LD I,A
r.i = rg.a;
goto loop;
case 0x4F: // LD R,A
SET_R( rg.a );
debug_printf( "LD R,A not supported\n" );
warning = true;
goto loop;
case 0x57: // LD A,I
rg.a = r.i;
goto ld_ai_common;
case 0x5F: // LD A,R
rg.a = GET_R();
debug_printf( "LD A,R not supported\n" );
warning = true;
ld_ai_common:
flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04);
goto loop;
CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN
r.iff1 = r.iff2;
goto ret_taken;
case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0
r.im = 0;
goto loop;
case 0x56: case 0x76: // IM 1
r.im = 1;
goto loop;
case 0x5E: case 0x7E: // IM 2
r.im = 2;
goto loop;
default:
debug_printf( "Opcode $ED $%02X not supported\n", data );
warning = true;
goto loop;
}
assert( false );
}
//////////////////////////////////////// DD/FD prefix
{
fuint16 ixy;
case 0xDD:
ixy = ix;
goto ix_prefix;
case 0xFD:
ixy = iy;
ix_prefix:
pc++;
unsigned data2 = READ_PROG( pc );
s_time += ed_dd_timing [data] & 0x0F;
switch ( data )
{
// TODO: more efficient way of avoid negative address
#define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp))
#define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in;
// ADD/ADC/SUB/SBC
case 0x96: // SUB (IXY+disp)
case 0x86: // ADD (IXY+disp)
flags &= ~C01;
case 0x9E: // SBC (IXY+disp)
case 0x8E: // ADC (IXY+disp)
pc++;
opcode = data;
data = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto adc_data;
case 0x94: // SUB HXY
case 0x84: // ADD HXY
flags &= ~C01;
case 0x9C: // SBC HXY
case 0x8C: // ADC HXY
opcode = data;
data = ixy >> 8;
goto adc_data;
case 0x95: // SUB LXY
case 0x85: // ADD LXY
flags &= ~C01;
case 0x9D: // SBC LXY
case 0x8D: // ADC LXY
opcode = data;
data = (uint8_t) ixy;
goto adc_data;
{
unsigned temp;
case 0x39: // ADD IXY,SP
temp = sp;
goto add_ixy_data;
case 0x29: // ADD IXY,HL
temp = ixy;
goto add_ixy_data;
case 0x09: // ADD IXY,BC
case 0x19: // ADD IXY,DE
temp = R16( data, 4, 0x09 );
add_ixy_data: {
blargg_ulong sum = ixy + temp;
temp ^= ixy;
ixy = (uint16_t) sum;
flags = (flags & (S80 | Z40 | V04)) |
(sum >> 16) |
(sum >> 8 & (F20 | F08)) |
((temp ^ sum) >> 8 & H10);
goto set_ixy;
}
}
// AND
case 0xA6: // AND (IXY+disp)
pc++;
data = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto and_data;
case 0xA4: // AND HXY
data = ixy >> 8;
goto and_data;
case 0xA5: // AND LXY
data = (uint8_t) ixy;
goto and_data;
// OR
case 0xB6: // OR (IXY+disp)
pc++;
data = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto or_data;
case 0xB4: // OR HXY
data = ixy >> 8;
goto or_data;
case 0xB5: // OR LXY
data = (uint8_t) ixy;
goto or_data;
// XOR
case 0xAE: // XOR (IXY+disp)
pc++;
data = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto xor_data;
case 0xAC: // XOR HXY
data = ixy >> 8;
goto xor_data;
case 0xAD: // XOR LXY
data = (uint8_t) ixy;
goto xor_data;
// CP
case 0xBE: // CP (IXY+disp)
pc++;
data = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto cp_data;
case 0xBC: // CP HXY
data = ixy >> 8;
goto cp_data;
case 0xBD: // CP LXY
data = (uint8_t) ixy;
goto cp_data;
// LD
CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r
data = R8( data, 0x70 );
if ( 0 )
case 0x36: // LD (IXY+disp),imm
pc++, data = READ_PROG( pc );
pc++;
WRITE( IXY_DISP( ixy, (int8_t) data2 ), data );
goto loop;
CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY
R8( data >> 3, 8 ) = ixy >> 8;
goto loop;
case 0x64: // LD HXY,HXY
case 0x6D: // LD LXY,LXY
goto loop;
CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY
R8( data >> 3, 8 ) = ixy;
goto loop;
CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp)
pc++;
R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto loop;
case 0x26: // LD HXY,imm
pc++;
goto ld_hxy_data;
case 0x65: // LD HXY,LXY
data2 = (uint8_t) ixy;
goto ld_hxy_data;
CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r
data2 = R8( data, 0x60 );
ld_hxy_data:
ixy = (uint8_t) ixy | (data2 << 8);
goto set_ixy;
case 0x2E: // LD LXY,imm
pc++;
goto ld_lxy_data;
case 0x6C: // LD LXY,HXY
data2 = ixy >> 8;
goto ld_lxy_data;
CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r
data2 = R8( data, 0x68 );
ld_lxy_data:
ixy = (ixy & 0xFF00) | data2;
set_ixy:
if ( opcode == 0xDD )
{
ix = ixy;
goto loop;
}
iy = ixy;
goto loop;
case 0xF9: // LD SP,IXY
sp = ixy;
goto loop;
case 0x22:{// LD (ADDR),IXY
fuint16 addr = GET_ADDR();
pc += 2;
WRITE_WORD( addr, ixy );
goto loop;
}
case 0x21: // LD IXY,imm
ixy = GET_ADDR();
pc += 2;
goto set_ixy;
case 0x2A:{// LD IXY,(addr)
fuint16 addr = GET_ADDR();
ixy = READ_WORD( addr );
pc += 2;
goto set_ixy;
}
// DD/FD CB prefix
case 0xCB: {
data = IXY_DISP( ixy, (int8_t) data2 );
pc++;
data2 = READ_PROG( pc );
pc++;
switch ( data2 )
{
case 0x06: goto rlc_data_addr; // RLC (IXY)
case 0x16: goto rl_data_addr; // RL (IXY)
case 0x26: goto sla_data_addr; // SLA (IXY)
case 0x36: goto sll_data_addr; // SLL (IXY)
case 0x0E: goto rrc_data_addr; // RRC (IXY)
case 0x1E: goto rr_data_addr; // RR (IXY)
case 0x2E: goto sra_data_addr; // SRA (IXY)
case 0x3E: goto srl_data_addr; // SRL (IXY)
CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp)
fuint8 temp = READ( data );
int masked = temp & 1 << (data2 >> 3 & 7);
flags = (flags & C01) | H10 |
(masked & S80) |
((masked - 1) >> 8 & (Z40 | P04));
goto loop;
}
CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp)
CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp)
int temp = READ( data );
int bit = 1 << (data2 >> 3 & 7);
temp |= bit; // SET
if ( !(data2 & 0x40) )
temp ^= bit; // RES
WRITE( data, temp );
goto loop;
}
default:
debug_printf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 );
warning = true;
goto loop;
}
assert( false );
}
// INC/DEC
case 0x23: // INC IXY
ixy = uint16_t (ixy + 1);
goto set_ixy;
case 0x2B: // DEC IXY
ixy = uint16_t (ixy - 1);
goto set_ixy;
case 0x34: // INC (IXY+disp)
ixy = IXY_DISP( ixy, (int8_t) data2 );
pc++;
data = READ( ixy ) + 1;
WRITE( ixy, data );
goto inc_set_flags;
case 0x35: // DEC (IXY+disp)
ixy = IXY_DISP( ixy, (int8_t) data2 );
pc++;
data = READ( ixy ) - 1;
WRITE( ixy, data );
goto dec_set_flags;
case 0x24: // INC HXY
ixy = uint16_t (ixy + 0x100);
data = ixy >> 8;
goto inc_xy_common;
case 0x2C: // INC LXY
data = uint8_t (ixy + 1);
ixy = (ixy & 0xFF00) | data;
inc_xy_common:
if ( opcode == 0xDD )
{
ix = ixy;
goto inc_set_flags;
}
iy = ixy;
goto inc_set_flags;
case 0x25: // DEC HXY
ixy = uint16_t (ixy - 0x100);
data = ixy >> 8;
goto dec_xy_common;
case 0x2D: // DEC LXY
data = uint8_t (ixy - 1);
ixy = (ixy & 0xFF00) | data;
dec_xy_common:
if ( opcode == 0xDD )
{
ix = ixy;
goto dec_set_flags;
}
iy = ixy;
goto dec_set_flags;
// PUSH/POP
case 0xE5: // PUSH IXY
data = ixy;
goto push_data;
case 0xE1:{// POP IXY
ixy = READ_WORD( sp );
sp = uint16_t (sp + 2);
goto set_ixy;
}
// Misc
case 0xE9: // JP (IXY)
pc = ixy;
goto loop;
case 0xE3:{// EX (SP),IXY
fuint16 temp = READ_WORD( sp );
WRITE_WORD( sp, ixy );
ixy = temp;
goto set_ixy;
}
default:
debug_printf( "Unnecessary DD/FD prefix encountered\n" );
warning = true;
pc--;
goto loop;
}
assert( false );
}
}
debug_printf( "Unhandled main opcode: $%02X\n", opcode );
assert( false );
halt:
s_time &= 3; // increment by multiple of 4
out_of_time:
pc--;
s.time = s_time;
rg.flags = flags;
r.ix = ix;
r.iy = iy;
r.sp = sp;
r.pc = pc;
this->r.b = rg;
this->state_ = s;
this->state = &this->state_;
return warning;
}
diff --git a/src/libs/gme/Kss_Cpu.cpp b/src/libs/gme/Kss_Cpu.cpp
index dac483c1..3654da2f 100644
--- a/src/libs/gme/Kss_Cpu.cpp
+++ b/src/libs/gme/Kss_Cpu.cpp
@@ -1,1706 +1,1707 @@
// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
/*
Last validated with zexall 2006.11.14 2:19 PM
* Doesn't implement the R register or immediate interrupt after EI.
* Address wrap-around isn't completely correct, but is prevented from crashing emulator.
*/
#include "Kss_Cpu.h"
#include "blargg_endian.h"
#include <string.h>
//#include "z80_cpu_log.h"
/* Copyright (C) 2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#define SYNC_TIME() (void) (s.time = s_time)
#define RELOAD_TIME() (void) (s_time = s.time)
// Callbacks to emulator
#define CPU_OUT( cpu, addr, data, time )\
kss_cpu_out( this, time, addr, data )
#define CPU_IN( cpu, addr, time )\
kss_cpu_in( this, time, addr )
#define CPU_WRITE( cpu, addr, data, time )\
(SYNC_TIME(), kss_cpu_write( this, addr, data ))
#include "blargg_source.h"
// flags, named with hex value for clarity
int const S80 = 0x80;
int const Z40 = 0x40;
int const F20 = 0x20;
int const H10 = 0x10;
int const F08 = 0x08;
int const V04 = 0x04;
int const P04 = 0x04;
int const N02 = 0x02;
int const C01 = 0x01;
#define SZ28P( n ) szpc [n]
#define SZ28PC( n ) szpc [n]
#define SZ28C( n ) (szpc [n] & ~P04)
#define SZ28( n ) SZ28C( n )
#define SET_R( n ) (void) (r.r = n)
#define GET_R() (r.r)
Kss_Cpu::Kss_Cpu()
{
state = &state_;
for ( int i = 0x100; --i >= 0; )
{
int even = 1;
for ( int p = i; p; p >>= 1 )
even ^= p;
int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04);
szpc [i] = n;
szpc [i + 0x100] = n | C01;
}
szpc [0x000] |= Z40;
szpc [0x100] |= Z40;
}
inline void Kss_Cpu::set_page( int i, void* write, void const* read )
{
blargg_long offset = KSS_CPU_PAGE_OFFSET( i * (blargg_long) page_size );
state->write [i] = (byte *) write - offset;
state->read [i] = (byte const*) read - offset;
}
void Kss_Cpu::reset( void* unmapped_write, void const* unmapped_read )
{
check( state == &state_ );
state = &state_;
state_.time = 0;
state_.base = 0;
end_time_ = 0;
for ( int i = 0; i < page_count + 1; i++ )
set_page( i, unmapped_write, unmapped_read );
memset( &r, 0, sizeof r );
}
void Kss_Cpu::map_mem( unsigned addr, blargg_ulong size, void* write, void const* read )
{
// address range must begin and end on page boundaries
require( addr % page_size == 0 );
require( size % page_size == 0 );
unsigned first_page = addr / page_size;
for ( unsigned i = size / page_size; i--; )
{
blargg_long offset = i * (blargg_long) page_size;
set_page( first_page + i, (byte*) write + offset, (byte const*) read + offset );
}
}
#define TIME (s_time + s.base)
#define RW_MEM( addr, rw ) (s.rw [(addr) >> page_shift] [KSS_CPU_PAGE_OFFSET( addr )])
#define READ_PROG( addr ) RW_MEM( addr, read )
#define READ( addr ) READ_PROG( addr )
//#define WRITE( addr, data ) (void) (RW_MEM( addr, write ) = data)
#define WRITE( addr, data ) CPU_WRITE( this, addr, data, TIME )
#define READ_WORD( addr ) GET_LE16( &READ( addr ) )
#define WRITE_WORD( addr, data ) SET_LE16( &RW_MEM( addr, write ), data )
#define IN( addr ) CPU_IN( this, addr, TIME )
#define OUT( addr, data ) CPU_OUT( this, addr, data, TIME )
#if BLARGG_BIG_ENDIAN
#define R8( n, offset ) ((r8_ - offset) [n])
#elif BLARGG_LITTLE_ENDIAN
#define R8( n, offset ) ((r8_ - offset) [(n) ^ 1])
#else
#error "Byte order of CPU must be known"
#endif
//#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)])
// help compiler see that it can just adjust stack offset, saving an extra instruction
#define R16( n, shift, offset )\
(*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1))))
#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e
#define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f
#define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g
#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h
// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8
static byte const ed_dd_timing [0x100] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00,
0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,
0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,
0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,
0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,
0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,
0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
};
// even on x86, using short and unsigned char was slower
typedef int fint16;
typedef unsigned fuint16;
typedef unsigned fuint8;
bool Kss_Cpu::run( cpu_time_t end_time )
{
set_end_time( end_time );
state_t s = this->state_;
this->state = &s;
bool warning = false;
typedef BOOST::int8_t int8_t;
union {
regs_t rg;
pairs_t rp;
uint8_t r8_ [8]; // indexed
uint16_t r16_ [4];
};
rg = this->r.b;
cpu_time_t s_time = s.time;
fuint16 pc = r.pc;
fuint16 sp = r.sp;
fuint16 ix = r.ix; // TODO: keep in memory for direct access?
fuint16 iy = r.iy;
int flags = r.b.flags;
goto loop;
jr_not_taken:
s_time -= 5;
goto loop;
call_not_taken:
s_time -= 7;
jp_not_taken:
pc += 2;
loop:
check( (unsigned long) pc < 0x10000 );
check( (unsigned long) sp < 0x10000 );
check( (unsigned) flags < 0x100 );
check( (unsigned) ix < 0x10000 );
check( (unsigned) iy < 0x10000 );
uint8_t const* instr = s.read [pc >> page_shift];
#define GET_ADDR() GET_LE16( instr )
fuint8 opcode;
// TODO: eliminate this special case
#if BLARGG_NONPORTABLE
opcode = instr [pc];
pc++;
instr += pc;
#else
instr += KSS_CPU_PAGE_OFFSET( pc );
opcode = *instr++;
pc++;
#endif
static byte const base_timing [0x100] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0
13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1
12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2
12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6
7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B
11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C
11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D
11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E
11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F
};
fuint16 data;
data = base_timing [opcode];
if ( (s_time += data) >= 0 )
goto possibly_out_of_time;
almost_out_of_time:
data = READ_PROG( pc );
#ifdef Z80_CPU_LOG_H
//log_opcode( opcode, READ_PROG( pc ) );
z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy );
z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ),
READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) );
#endif
switch ( opcode )
{
possibly_out_of_time:
if ( s_time < (int) data )
goto almost_out_of_time;
s_time -= data;
goto out_of_time;
// Common
case 0x00: // NOP
CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc.
goto loop;
case 0x08:{// EX AF,AF'
int temp = r.alt.b.a;
r.alt.b.a = rg.a;
rg.a = temp;
temp = r.alt.b.flags;
r.alt.b.flags = flags;
flags = temp;
goto loop;
}
case 0xD3: // OUT (imm),A
pc++;
OUT( data + rg.a * 0x100, rg.a );
goto loop;
case 0x2E: // LD L,imm
pc++;
rg.l = data;
goto loop;
case 0x3E: // LD A,imm
pc++;
rg.a = data;
goto loop;
case 0x3A:{// LD A,(addr)
fuint16 addr = GET_ADDR();
pc += 2;
rg.a = READ( addr );
goto loop;
}
// Conditional
#define ZERO (flags & Z40)
#define CARRY (flags & C01)
#define EVEN (flags & P04)
#define MINUS (flags & S80)
// JR
// TODO: more efficient way to handle negative branch that wraps PC around
#define JR( cond ) {\
int offset = (BOOST::int8_t) data;\
pc++;\
if ( !(cond) )\
goto jr_not_taken;\
pc = uint16_t (pc + offset);\
goto loop;\
}
case 0x20: JR( !ZERO ) // JR NZ,disp
case 0x28: JR( ZERO ) // JR Z,disp
case 0x30: JR( !CARRY ) // JR NC,disp
case 0x38: JR( CARRY ) // JR C,disp
case 0x18: JR( true ) // JR disp
case 0x10:{// DJNZ disp
int temp = rg.b - 1;
rg.b = temp;
JR( temp )
}
// JP
#define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop;
case 0xC2: JP( !ZERO ) // JP NZ,addr
case 0xCA: JP( ZERO ) // JP Z,addr
case 0xD2: JP( !CARRY ) // JP NC,addr
case 0xDA: JP( CARRY ) // JP C,addr
case 0xE2: JP( !EVEN ) // JP PO,addr
case 0xEA: JP( EVEN ) // JP PE,addr
case 0xF2: JP( !MINUS ) // JP P,addr
case 0xFA: JP( MINUS ) // JP M,addr
case 0xC3: // JP addr
pc = GET_ADDR();
goto loop;
case 0xE9: // JP HL
pc = rp.hl;
goto loop;
// RET
#define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop;
case 0xC0: RET( !ZERO ) // RET NZ
case 0xC8: RET( ZERO ) // RET Z
case 0xD0: RET( !CARRY ) // RET NC
case 0xD8: RET( CARRY ) // RET C
case 0xE0: RET( !EVEN ) // RET PO
case 0xE8: RET( EVEN ) // RET PE
case 0xF0: RET( !MINUS ) // RET P
case 0xF8: RET( MINUS ) // RET M
case 0xC9: // RET
ret_taken:
pc = READ_WORD( sp );
sp = uint16_t (sp + 2);
goto loop;
// CALL
#define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken;
case 0xC4: CALL( !ZERO ) // CALL NZ,addr
case 0xCC: CALL( ZERO ) // CALL Z,addr
case 0xD4: CALL( !CARRY ) // CALL NC,addr
case 0xDC: CALL( CARRY ) // CALL C,addr
case 0xE4: CALL( !EVEN ) // CALL PO,addr
case 0xEC: CALL( EVEN ) // CALL PE,addr
case 0xF4: CALL( !MINUS ) // CALL P,addr
case 0xFC: CALL( MINUS ) // CALL M,addr
case 0xCD:{// CALL addr
call_taken:
fuint16 addr = pc + 2;
pc = GET_ADDR();
sp = uint16_t (sp - 2);
WRITE_WORD( sp, addr );
goto loop;
}
case 0xFF: // RST
if ( pc > idle_addr )
goto hit_idle_addr;
CASE7( C7, CF, D7, DF, E7, EF, F7 ):
data = pc;
pc = opcode & 0x38;
goto push_data;
// PUSH/POP
case 0xF5: // PUSH AF
data = rg.a * 0x100u + flags;
goto push_data;
case 0xC5: // PUSH BC
case 0xD5: // PUSH DE
case 0xE5: // PUSH HL
data = R16( opcode, 4, 0xC5 );
push_data:
sp = uint16_t (sp - 2);
WRITE_WORD( sp, data );
goto loop;
case 0xF1: // POP AF
flags = READ( sp );
rg.a = READ( sp + 1 );
sp = uint16_t (sp + 2);
goto loop;
case 0xC1: // POP BC
case 0xD1: // POP DE
case 0xE1: // POP HL
R16( opcode, 4, 0xC1 ) = READ_WORD( sp );
sp = uint16_t (sp + 2);
goto loop;
// ADC/ADD/SBC/SUB
case 0x96: // SUB (HL)
case 0x86: // ADD (HL)
flags &= ~C01;
case 0x9E: // SBC (HL)
case 0x8E: // ADC (HL)
data = READ( rp.hl );
goto adc_data;
case 0xD6: // SUB A,imm
case 0xC6: // ADD imm
flags &= ~C01;
case 0xDE: // SBC A,imm
case 0xCE: // ADC imm
pc++;
goto adc_data;
CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r
CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r
flags &= ~C01;
CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r
CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r
data = R8( opcode & 7, 0 );
adc_data: {
int result = data + (flags & C01);
data ^= rg.a;
flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes
if ( flags )
result = -result;
result += rg.a;
data ^= result;
flags |=(data & H10) |
((data - -0x80) >> 6 & V04) |
SZ28C( result & 0x1FF );
rg.a = result;
goto loop;
}
// CP
case 0xBE: // CP (HL)
data = READ( rp.hl );
goto cp_data;
case 0xFE: // CP imm
pc++;
goto cp_data;
CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r
data = R8( opcode, 0xB8 );
cp_data: {
int result = rg.a - data;
flags = N02 | (data & (F20 | F08)) | (result >> 8 & C01);
data ^= rg.a;
flags |=(((result ^ rg.a) & data) >> 5 & V04) |
(((data & H10) ^ result) & (S80 | H10));
if ( (uint8_t) result )
goto loop;
flags |= Z40;
goto loop;
}
// ADD HL,rp
case 0x39: // ADD HL,SP
data = sp;
goto add_hl_data;
case 0x09: // ADD HL,BC
case 0x19: // ADD HL,DE
case 0x29: // ADD HL,HL
data = R16( opcode, 4, 0x09 );
add_hl_data: {
blargg_ulong sum = rp.hl + data;
data ^= rp.hl;
rp.hl = sum;
flags = (flags & (S80 | Z40 | V04)) |
(sum >> 16) |
(sum >> 8 & (F20 | F08)) |
((data ^ sum) >> 8 & H10);
goto loop;
}
case 0x27:{// DAA
int a = rg.a;
if ( a > 0x99 )
flags |= C01;
int adjust = 0x60 & -(flags & C01);
if ( flags & H10 || (a & 0x0F) > 9 )
adjust |= 0x06;
if ( flags & N02 )
adjust = -adjust;
a += adjust;
flags = (flags & (C01 | N02)) |
((rg.a ^ a) & H10) |
SZ28P( (uint8_t) a );
rg.a = a;
goto loop;
}
/*
case 0x27:{// DAA
// more optimized, but probably not worth the obscurity
int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags
int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0
if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9
adjust |= 0x06;
if ( f & N02 )
adjust = -adjust;
int a = rg.a + adjust;
flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a );
rg.a = a;
goto loop;
}
*/
// INC/DEC
case 0x34: // INC (HL)
data = READ( rp.hl ) + 1;
WRITE( rp.hl, data );
goto inc_set_flags;
CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r
data = ++R8( opcode >> 3, 0 );
inc_set_flags:
flags = (flags & C01) |
(((data & 0x0F) - 1) & H10) |
SZ28( (uint8_t) data );
if ( data != 0x80 )
goto loop;
flags |= V04;
goto loop;
case 0x35: // DEC (HL)
data = READ( rp.hl ) - 1;
WRITE( rp.hl, data );
goto dec_set_flags;
CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r
data = --R8( opcode >> 3, 0 );
dec_set_flags:
flags = (flags & C01) | N02 |
(((data & 0x0F) + 1) & H10) |
SZ28( (uint8_t) data );
if ( data != 0x7F )
goto loop;
flags |= V04;
goto loop;
case 0x03: // INC BC
case 0x13: // INC DE
case 0x23: // INC HL
R16( opcode, 4, 0x03 )++;
goto loop;
case 0x33: // INC SP
sp = uint16_t (sp + 1);
goto loop;
case 0x0B: // DEC BC
case 0x1B: // DEC DE
case 0x2B: // DEC HL
R16( opcode, 4, 0x0B )--;
goto loop;
case 0x3B: // DEC SP
sp = uint16_t (sp - 1);
goto loop;
// AND
case 0xA6: // AND (HL)
data = READ( rp.hl );
goto and_data;
case 0xE6: // AND imm
pc++;
goto and_data;
CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r
data = R8( opcode, 0xA0 );
and_data:
rg.a &= data;
flags = SZ28P( rg.a ) | H10;
goto loop;
// OR
case 0xB6: // OR (HL)
data = READ( rp.hl );
goto or_data;
case 0xF6: // OR imm
pc++;
goto or_data;
CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r
data = R8( opcode, 0xB0 );
or_data:
rg.a |= data;
flags = SZ28P( rg.a );
goto loop;
// XOR
case 0xAE: // XOR (HL)
data = READ( rp.hl );
goto xor_data;
case 0xEE: // XOR imm
pc++;
goto xor_data;
CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r
data = R8( opcode, 0xA8 );
xor_data:
rg.a ^= data;
flags = SZ28P( rg.a );
goto loop;
// LD
CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r
WRITE( rp.hl, R8( opcode, 0x70 ) );
goto loop;
CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r
CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r
CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r
CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r
CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r
CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r
CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r
R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 );
goto loop;
CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm
R8( opcode >> 3, 0 ) = data;
pc++;
goto loop;
case 0x36: // LD (HL),imm
pc++;
WRITE( rp.hl, data );
goto loop;
CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL)
R8( opcode >> 3, 8 ) = READ( rp.hl );
goto loop;
case 0x01: // LD rp,imm
case 0x11:
case 0x21:
R16( opcode, 4, 0x01 ) = GET_ADDR();
pc += 2;
goto loop;
case 0x31: // LD sp,imm
sp = GET_ADDR();
pc += 2;
goto loop;
case 0x2A:{// LD HL,(addr)
fuint16 addr = GET_ADDR();
pc += 2;
rp.hl = READ_WORD( addr );
goto loop;
}
case 0x32:{// LD (addr),A
fuint16 addr = GET_ADDR();
pc += 2;
WRITE( addr, rg.a );
goto loop;
}
case 0x22:{// LD (addr),HL
fuint16 addr = GET_ADDR();
pc += 2;
WRITE_WORD( addr, rp.hl );
goto loop;
}
case 0x02: // LD (BC),A
case 0x12: // LD (DE),A
WRITE( R16( opcode, 4, 0x02 ), rg.a );
goto loop;
case 0x0A: // LD A,(BC)
case 0x1A: // LD A,(DE)
rg.a = READ( R16( opcode, 4, 0x0A ) );
goto loop;
case 0xF9: // LD SP,HL
sp = rp.hl;
goto loop;
// Rotate
case 0x07:{// RLCA
fuint16 temp = rg.a;
temp = (temp << 1) | (temp >> 7);
flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08 | C01));
rg.a = temp;
goto loop;
}
case 0x0F:{// RRCA
fuint16 temp = rg.a;
flags = (flags & (S80 | Z40 | P04)) |
(temp & C01);
temp = (temp << 7) | (temp >> 1);
flags |= temp & (F20 | F08);
rg.a = temp;
goto loop;
}
case 0x17:{// RLA
blargg_ulong temp = (rg.a << 1) | (flags & C01);
flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08)) |
(temp >> 8);
rg.a = temp;
goto loop;
}
case 0x1F:{// RRA
fuint16 temp = (flags << 7) | (rg.a >> 1);
flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08)) |
(rg.a & C01);
rg.a = temp;
goto loop;
}
// Misc
case 0x2F:{// CPL
fuint16 temp = ~rg.a;
flags = (flags & (S80 | Z40 | P04 | C01)) |
(temp & (F20 | F08)) |
(H10 | N02);
rg.a = temp;
goto loop;
}
case 0x3F:{// CCF
flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) |
(flags << 4 & H10) |
(rg.a & (F20 | F08));
goto loop;
}
case 0x37: // SCF
flags = (flags & (S80 | Z40 | P04)) | C01 |
(rg.a & (F20 | F08));
goto loop;
case 0xDB: // IN A,(imm)
pc++;
rg.a = IN( data + rg.a * 0x100 );
goto loop;
case 0xE3:{// EX (SP),HL
fuint16 temp = READ_WORD( sp );
WRITE_WORD( sp, rp.hl );
rp.hl = temp;
goto loop;
}
case 0xEB:{// EX DE,HL
fuint16 temp = rp.hl;
rp.hl = rp.de;
rp.de = temp;
goto loop;
}
case 0xD9:{// EXX DE,HL
fuint16 temp = r.alt.w.bc;
r.alt.w.bc = rp.bc;
rp.bc = temp;
temp = r.alt.w.de;
r.alt.w.de = rp.de;
rp.de = temp;
temp = r.alt.w.hl;
r.alt.w.hl = rp.hl;
rp.hl = temp;
goto loop;
}
case 0xF3: // DI
r.iff1 = 0;
r.iff2 = 0;
goto loop;
case 0xFB: // EI
r.iff1 = 1;
r.iff2 = 1;
// TODO: delayed effect
goto loop;
case 0x76: // HALT
goto halt;
//////////////////////////////////////// CB prefix
{
case 0xCB:
unsigned data2;
data2 = instr [1];
+ data2 = data2;
pc++;
switch ( data )
{
// Rotate left
#define RLC( read, write ) {\
fuint8 result = read;\
result = uint8_t (result << 1) | (result >> 7);\
flags = SZ28P( result ) | (result & C01);\
write;\
goto loop;\
}
case 0x06: // RLC (HL)
s_time += 7;
data = rp.hl;
rlc_data_addr:
RLC( READ( data ), WRITE( data, result ) )
CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r
uint8_t& reg = R8( data, 0 );
RLC( reg, reg = result )
}
#define RL( read, write ) {\
fuint16 result = (read << 1) | (flags & C01);\
flags = SZ28PC( result );\
write;\
goto loop;\
}
case 0x16: // RL (HL)
s_time += 7;
data = rp.hl;
rl_data_addr:
RL( READ( data ), WRITE( data, result ) )
CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r
uint8_t& reg = R8( data, 0x10 );
RL( reg, reg = result )
}
#define SLA( read, add, write ) {\
fuint16 result = (read << 1) | add;\
flags = SZ28PC( result );\
write;\
goto loop;\
}
case 0x26: // SLA (HL)
s_time += 7;
data = rp.hl;
sla_data_addr:
SLA( READ( data ), 0, WRITE( data, result ) )
CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r
uint8_t& reg = R8( data, 0x20 );
SLA( reg, 0, reg = result )
}
case 0x36: // SLL (HL)
s_time += 7;
data = rp.hl;
sll_data_addr:
SLA( READ( data ), 1, WRITE( data, result ) )
CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r
uint8_t& reg = R8( data, 0x30 );
SLA( reg, 1, reg = result )
}
// Rotate right
#define RRC( read, write ) {\
fuint8 result = read;\
flags = result & C01;\
result = uint8_t (result << 7) | (result >> 1);\
flags |= SZ28P( result );\
write;\
goto loop;\
}
case 0x0E: // RRC (HL)
s_time += 7;
data = rp.hl;
rrc_data_addr:
RRC( READ( data ), WRITE( data, result ) )
CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r
uint8_t& reg = R8( data, 0x08 );
RRC( reg, reg = result )
}
#define RR( read, write ) {\
fuint8 result = read;\
fuint8 temp = result & C01;\
result = uint8_t (flags << 7) | (result >> 1);\
flags = SZ28P( result ) | temp;\
write;\
goto loop;\
}
case 0x1E: // RR (HL)
s_time += 7;
data = rp.hl;
rr_data_addr:
RR( READ( data ), WRITE( data, result ) )
CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r
uint8_t& reg = R8( data, 0x18 );
RR( reg, reg = result )
}
#define SRA( read, write ) {\
fuint8 result = read;\
flags = result & C01;\
result = (result & 0x80) | (result >> 1);\
flags |= SZ28P( result );\
write;\
goto loop;\
}
case 0x2E: // SRA (HL)
data = rp.hl;
s_time += 7;
sra_data_addr:
SRA( READ( data ), WRITE( data, result ) )
CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r
uint8_t& reg = R8( data, 0x28 );
SRA( reg, reg = result )
}
#define SRL( read, write ) {\
fuint8 result = read;\
flags = result & C01;\
result >>= 1;\
flags |= SZ28P( result );\
write;\
goto loop;\
}
case 0x3E: // SRL (HL)
s_time += 7;
data = rp.hl;
srl_data_addr:
SRL( READ( data ), WRITE( data, result ) )
CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r
uint8_t& reg = R8( data, 0x38 );
SRL( reg, reg = result )
}
// BIT
{
unsigned temp;
CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL)
s_time += 4;
temp = READ( rp.hl );
flags &= C01;
goto bit_temp;
CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r
CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r
CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r
CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r
CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r
CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r
CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r
CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r
temp = R8( data & 7, 0 );
flags = (flags & C01) | (temp & (F20 | F08));
bit_temp:
int masked = temp & 1 << (data >> 3 & 7);
flags |=(masked & S80) | H10 |
((masked - 1) >> 8 & (Z40 | P04));
goto loop;
}
// SET/RES
CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL)
CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL)
s_time += 7;
int temp = READ( rp.hl );
int bit = 1 << (data >> 3 & 7);
temp |= bit; // SET
if ( !(data & 0x40) )
temp ^= bit; // RES
WRITE( rp.hl, temp );
goto loop;
}
CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r
CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r
CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r
CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r
CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r
CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r
CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r
CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r
R8( data & 7, 0 ) |= 1 << (data >> 3 & 7);
goto loop;
CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r
CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r
CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r
CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r
CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r
CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r
CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r
CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r
R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7));
goto loop;
}
assert( false );
}
#undef GET_ADDR
#define GET_ADDR() GET_LE16( instr + 1 )
//////////////////////////////////////// ED prefix
{
case 0xED:
pc++;
s_time += ed_dd_timing [data] >> 4;
switch ( data )
{
{
blargg_ulong temp;
case 0x72: // SBC HL,SP
case 0x7A: // ADC HL,SP
temp = sp;
if ( 0 )
case 0x42: // SBC HL,BC
case 0x52: // SBC HL,DE
case 0x62: // SBC HL,HL
case 0x4A: // ADC HL,BC
case 0x5A: // ADC HL,DE
case 0x6A: // ADC HL,HL
temp = R16( data >> 3 & 6, 1, 0 );
blargg_ulong sum = temp + (flags & C01);
flags = ~data >> 2 & N02;
if ( flags )
sum = -sum;
sum += rp.hl;
temp ^= rp.hl;
temp ^= sum;
flags |=(sum >> 16 & C01) |
(temp >> 8 & H10) |
(sum >> 8 & (S80 | F20 | F08)) |
((temp - -0x8000) >> 14 & V04);
rp.hl = sum;
if ( (uint16_t) sum )
goto loop;
flags |= Z40;
goto loop;
}
CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C)
int temp = IN( rp.bc );
R8( data >> 3, 8 ) = temp;
flags = (flags & C01) | SZ28P( temp );
goto loop;
}
case 0x71: // OUT (C),0
rg.flags = 0;
CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r
OUT( rp.bc, R8( data >> 3, 8 ) );
goto loop;
{
unsigned temp;
case 0x73: // LD (ADDR),SP
temp = sp;
if ( 0 )
case 0x43: // LD (ADDR),BC
case 0x53: // LD (ADDR),DE
temp = R16( data, 4, 0x43 );
fuint16 addr = GET_ADDR();
pc += 2;
WRITE_WORD( addr, temp );
goto loop;
}
case 0x4B: // LD BC,(ADDR)
case 0x5B:{// LD DE,(ADDR)
fuint16 addr = GET_ADDR();
pc += 2;
R16( data, 4, 0x4B ) = READ_WORD( addr );
goto loop;
}
case 0x7B:{// LD SP,(ADDR)
fuint16 addr = GET_ADDR();
pc += 2;
sp = READ_WORD( addr );
goto loop;
}
case 0x67:{// RRD
fuint8 temp = READ( rp.hl );
WRITE( rp.hl, (rg.a << 4) | (temp >> 4) );
temp = (rg.a & 0xF0) | (temp & 0x0F);
flags = (flags & C01) | SZ28P( temp );
rg.a = temp;
goto loop;
}
case 0x6F:{// RLD
fuint8 temp = READ( rp.hl );
WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) );
temp = (rg.a & 0xF0) | (temp >> 4);
flags = (flags & C01) | SZ28P( temp );
rg.a = temp;
goto loop;
}
CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG
opcode = 0x10; // flag to do SBC instead of ADC
flags &= ~C01;
data = rg.a;
rg.a = 0;
goto adc_data;
{
int inc;
case 0xA9: // CPD
case 0xB9: // CPDR
inc = -1;
if ( 0 )
case 0xA1: // CPI
case 0xB1: // CPIR
inc = +1;
fuint16 addr = rp.hl;
rp.hl = addr + inc;
int temp = READ( addr );
int result = rg.a - temp;
flags = (flags & C01) | N02 |
((((temp ^ rg.a) & H10) ^ result) & (S80 | H10));
if ( !(uint8_t) result ) flags |= Z40;
result -= (flags & H10) >> 4;
flags |= result & F08;
flags |= result << 4 & F20;
if ( !--rp.bc )
goto loop;
flags |= V04;
if ( flags & Z40 || data < 0xB0 )
goto loop;
pc -= 2;
s_time += 5;
goto loop;
}
{
int inc;
case 0xA8: // LDD
case 0xB8: // LDDR
inc = -1;
if ( 0 )
case 0xA0: // LDI
case 0xB0: // LDIR
inc = +1;
fuint16 addr = rp.hl;
rp.hl = addr + inc;
int temp = READ( addr );
addr = rp.de;
rp.de = addr + inc;
WRITE( addr, temp );
temp += rg.a;
flags = (flags & (S80 | Z40 | C01)) |
(temp & F08) | (temp << 4 & F20);
if ( !--rp.bc )
goto loop;
flags |= V04;
if ( data < 0xB0 )
goto loop;
pc -= 2;
s_time += 5;
goto loop;
}
{
int inc;
case 0xAB: // OUTD
case 0xBB: // OTDR
inc = -1;
if ( 0 )
case 0xA3: // OUTI
case 0xB3: // OTIR
inc = +1;
fuint16 addr = rp.hl;
rp.hl = addr + inc;
int temp = READ( addr );
int b = --rg.b;
flags = (temp >> 6 & N02) | SZ28( b );
if ( b && data >= 0xB0 )
{
pc -= 2;
s_time += 5;
}
OUT( rp.bc, temp );
goto loop;
}
{
int inc;
case 0xAA: // IND
case 0xBA: // INDR
inc = -1;
if ( 0 )
case 0xA2: // INI
case 0xB2: // INIR
inc = +1;
fuint16 addr = rp.hl;
rp.hl = addr + inc;
int temp = IN( rp.bc );
int b = --rg.b;
flags = (temp >> 6 & N02) | SZ28( b );
if ( b && data >= 0xB0 )
{
pc -= 2;
s_time += 5;
}
WRITE( addr, temp );
goto loop;
}
case 0x47: // LD I,A
r.i = rg.a;
goto loop;
case 0x4F: // LD R,A
SET_R( rg.a );
debug_printf( "LD R,A not supported\n" );
warning = true;
goto loop;
case 0x57: // LD A,I
rg.a = r.i;
goto ld_ai_common;
case 0x5F: // LD A,R
rg.a = GET_R();
debug_printf( "LD A,R not supported\n" );
warning = true;
ld_ai_common:
flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04);
goto loop;
CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN
r.iff1 = r.iff2;
goto ret_taken;
case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0
r.im = 0;
goto loop;
case 0x56: case 0x76: // IM 1
r.im = 1;
goto loop;
case 0x5E: case 0x7E: // IM 2
r.im = 2;
goto loop;
default:
debug_printf( "Opcode $ED $%02X not supported\n", data );
warning = true;
goto loop;
}
assert( false );
}
//////////////////////////////////////// DD/FD prefix
{
fuint16 ixy;
case 0xDD:
ixy = ix;
goto ix_prefix;
case 0xFD:
ixy = iy;
ix_prefix:
pc++;
unsigned data2 = READ_PROG( pc );
s_time += ed_dd_timing [data] & 0x0F;
switch ( data )
{
// TODO: more efficient way of avoid negative address
// TODO: avoid using this as argument to READ() since it is evaluated twice
#define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp))
#define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in;
// ADD/ADC/SUB/SBC
case 0x96: // SUB (IXY+disp)
case 0x86: // ADD (IXY+disp)
flags &= ~C01;
case 0x9E: // SBC (IXY+disp)
case 0x8E: // ADC (IXY+disp)
pc++;
opcode = data;
data = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto adc_data;
case 0x94: // SUB HXY
case 0x84: // ADD HXY
flags &= ~C01;
case 0x9C: // SBC HXY
case 0x8C: // ADC HXY
opcode = data;
data = ixy >> 8;
goto adc_data;
case 0x95: // SUB LXY
case 0x85: // ADD LXY
flags &= ~C01;
case 0x9D: // SBC LXY
case 0x8D: // ADC LXY
opcode = data;
data = (uint8_t) ixy;
goto adc_data;
{
unsigned temp;
case 0x39: // ADD IXY,SP
temp = sp;
goto add_ixy_data;
case 0x29: // ADD IXY,HL
temp = ixy;
goto add_ixy_data;
case 0x09: // ADD IXY,BC
case 0x19: // ADD IXY,DE
temp = R16( data, 4, 0x09 );
add_ixy_data: {
blargg_ulong sum = ixy + temp;
temp ^= ixy;
ixy = (uint16_t) sum;
flags = (flags & (S80 | Z40 | V04)) |
(sum >> 16) |
(sum >> 8 & (F20 | F08)) |
((temp ^ sum) >> 8 & H10);
goto set_ixy;
}
}
// AND
case 0xA6: // AND (IXY+disp)
pc++;
data = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto and_data;
case 0xA4: // AND HXY
data = ixy >> 8;
goto and_data;
case 0xA5: // AND LXY
data = (uint8_t) ixy;
goto and_data;
// OR
case 0xB6: // OR (IXY+disp)
pc++;
data = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto or_data;
case 0xB4: // OR HXY
data = ixy >> 8;
goto or_data;
case 0xB5: // OR LXY
data = (uint8_t) ixy;
goto or_data;
// XOR
case 0xAE: // XOR (IXY+disp)
pc++;
data = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto xor_data;
case 0xAC: // XOR HXY
data = ixy >> 8;
goto xor_data;
case 0xAD: // XOR LXY
data = (uint8_t) ixy;
goto xor_data;
// CP
case 0xBE: // CP (IXY+disp)
pc++;
data = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto cp_data;
case 0xBC: // CP HXY
data = ixy >> 8;
goto cp_data;
case 0xBD: // CP LXY
data = (uint8_t) ixy;
goto cp_data;
// LD
CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r
data = R8( data, 0x70 );
if ( 0 )
case 0x36: // LD (IXY+disp),imm
pc++, data = READ_PROG( pc );
pc++;
WRITE( IXY_DISP( ixy, (int8_t) data2 ), data );
goto loop;
CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY
R8( data >> 3, 8 ) = ixy >> 8;
goto loop;
case 0x64: // LD HXY,HXY
case 0x6D: // LD LXY,LXY
goto loop;
CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY
R8( data >> 3, 8 ) = ixy;
goto loop;
CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp)
pc++;
R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) );
goto loop;
case 0x26: // LD HXY,imm
pc++;
goto ld_hxy_data;
case 0x65: // LD HXY,LXY
data2 = (uint8_t) ixy;
goto ld_hxy_data;
CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r
data2 = R8( data, 0x60 );
ld_hxy_data:
ixy = (uint8_t) ixy | (data2 << 8);
goto set_ixy;
case 0x2E: // LD LXY,imm
pc++;
goto ld_lxy_data;
case 0x6C: // LD LXY,HXY
data2 = ixy >> 8;
goto ld_lxy_data;
CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r
data2 = R8( data, 0x68 );
ld_lxy_data:
ixy = (ixy & 0xFF00) | data2;
set_ixy:
if ( opcode == 0xDD )
{
ix = ixy;
goto loop;
}
iy = ixy;
goto loop;
case 0xF9: // LD SP,IXY
sp = ixy;
goto loop;
case 0x22:{// LD (ADDR),IXY
fuint16 addr = GET_ADDR();
pc += 2;
WRITE_WORD( addr, ixy );
goto loop;
}
case 0x21: // LD IXY,imm
ixy = GET_ADDR();
pc += 2;
goto set_ixy;
case 0x2A:{// LD IXY,(addr)
fuint16 addr = GET_ADDR();
ixy = READ_WORD( addr );
pc += 2;
goto set_ixy;
}
// DD/FD CB prefix
case 0xCB: {
data = IXY_DISP( ixy, (int8_t) data2 );
pc++;
data2 = READ_PROG( pc );
pc++;
switch ( data2 )
{
case 0x06: goto rlc_data_addr; // RLC (IXY)
case 0x16: goto rl_data_addr; // RL (IXY)
case 0x26: goto sla_data_addr; // SLA (IXY)
case 0x36: goto sll_data_addr; // SLL (IXY)
case 0x0E: goto rrc_data_addr; // RRC (IXY)
case 0x1E: goto rr_data_addr; // RR (IXY)
case 0x2E: goto sra_data_addr; // SRA (IXY)
case 0x3E: goto srl_data_addr; // SRL (IXY)
CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp)
fuint8 temp = READ( data );
int masked = temp & 1 << (data2 >> 3 & 7);
flags = (flags & C01) | H10 |
(masked & S80) |
((masked - 1) >> 8 & (Z40 | P04));
goto loop;
}
CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp)
CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp)
int temp = READ( data );
int bit = 1 << (data2 >> 3 & 7);
temp |= bit; // SET
if ( !(data2 & 0x40) )
temp ^= bit; // RES
WRITE( data, temp );
goto loop;
}
default:
debug_printf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 );
warning = true;
goto loop;
}
assert( false );
}
// INC/DEC
case 0x23: // INC IXY
ixy = uint16_t (ixy + 1);
goto set_ixy;
case 0x2B: // DEC IXY
ixy = uint16_t (ixy - 1);
goto set_ixy;
case 0x34: // INC (IXY+disp)
ixy = IXY_DISP( ixy, (int8_t) data2 );
pc++;
data = READ( ixy ) + 1;
WRITE( ixy, data );
goto inc_set_flags;
case 0x35: // DEC (IXY+disp)
ixy = IXY_DISP( ixy, (int8_t) data2 );
pc++;
data = READ( ixy ) - 1;
WRITE( ixy, data );
goto dec_set_flags;
case 0x24: // INC HXY
ixy = uint16_t (ixy + 0x100);
data = ixy >> 8;
goto inc_xy_common;
case 0x2C: // INC LXY
data = uint8_t (ixy + 1);
ixy = (ixy & 0xFF00) | data;
inc_xy_common:
if ( opcode == 0xDD )
{
ix = ixy;
goto inc_set_flags;
}
iy = ixy;
goto inc_set_flags;
case 0x25: // DEC HXY
ixy = uint16_t (ixy - 0x100);
data = ixy >> 8;
goto dec_xy_common;
case 0x2D: // DEC LXY
data = uint8_t (ixy - 1);
ixy = (ixy & 0xFF00) | data;
dec_xy_common:
if ( opcode == 0xDD )
{
ix = ixy;
goto dec_set_flags;
}
iy = ixy;
goto dec_set_flags;
// PUSH/POP
case 0xE5: // PUSH IXY
data = ixy;
goto push_data;
case 0xE1:{// POP IXY
ixy = READ_WORD( sp );
sp = uint16_t (sp + 2);
goto set_ixy;
}
// Misc
case 0xE9: // JP (IXY)
pc = ixy;
goto loop;
case 0xE3:{// EX (SP),IXY
fuint16 temp = READ_WORD( sp );
WRITE_WORD( sp, ixy );
ixy = temp;
goto set_ixy;
}
default:
debug_printf( "Unnecessary DD/FD prefix encountered\n" );
warning = true;
pc--;
goto loop;
}
assert( false );
}
}
debug_printf( "Unhandled main opcode: $%02X\n", opcode );
assert( false );
hit_idle_addr:
s_time -= 11;
goto out_of_time;
halt:
s_time &= 3; // increment by multiple of 4
out_of_time:
pc--;
s.time = s_time;
rg.flags = flags;
r.ix = ix;
r.iy = iy;
r.sp = sp;
r.pc = pc;
this->r.b = rg;
this->state_ = s;
this->state = &this->state_;
return warning;
}
diff --git a/src/libs/zip/zip.c b/src/libs/zip/zip.c
index ae30763c..56110dd8 100644
--- a/src/libs/zip/zip.c
+++ b/src/libs/zip/zip.c
@@ -1,2004 +1,2004 @@
/* zip.c -- IO on .zip files using zlib
Version 1.1, February 14h, 2010
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
Changes
Oct-2009 - Mathias Svensson - Remove old C style function prototypes
Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives
Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions.
Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data
It is used when recreting zip archive with RAW when deleting items from a zip.
ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed.
Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required)
Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <zlib.h>
#include "zip.h"
#ifdef STDC
# include <stddef.h>
# include <string.h>
# include <stdlib.h>
#endif
#ifdef NO_ERRNO_H
extern int errno;
#else
# include <errno.h>
#endif
#ifndef local
# define local static
#endif
/* compile with -Dlocal if your debugger can't find static symbols */
#ifndef VERSIONMADEBY
# define VERSIONMADEBY (0x0) /* platform depedent */
#endif
#ifndef Z_BUFSIZE
#define Z_BUFSIZE (64*1024) //(16384)
#endif
#ifndef Z_MAXFILENAMEINZIP
#define Z_MAXFILENAMEINZIP (256)
#endif
#ifndef ALLOC
# define ALLOC(size) (malloc(size))
#endif
#ifndef TRYFREE
# define TRYFREE(p) {if (p) free(p);}
#endif
/*
#define SIZECENTRALDIRITEM (0x2e)
#define SIZEZIPLOCALHEADER (0x1e)
*/
/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
// NOT sure that this work on ALL platform
#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32))
#ifndef SEEK_CUR
#define SEEK_CUR 1
#endif
#ifndef SEEK_END
#define SEEK_END 2
#endif
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
#ifndef DEF_MEM_LEVEL
#if MAX_MEM_LEVEL >= 8
# define DEF_MEM_LEVEL 8
#else
# define DEF_MEM_LEVEL MAX_MEM_LEVEL
#endif
#endif
const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
#define SIZEDATA_INDATABLOCK (4096-(4*4))
#define LOCALHEADERMAGIC (0x04034b50)
#define CENTRALHEADERMAGIC (0x02014b50)
#define ENDHEADERMAGIC (0x06054b50)
#define ZIP64ENDHEADERMAGIC (0x6064b50)
#define ZIP64ENDLOCHEADERMAGIC (0x7064b50)
#define FLAG_LOCALHEADER_OFFSET (0x06)
#define CRC_LOCALHEADER_OFFSET (0x0e)
#define SIZECENTRALHEADER (0x2e) /* 46 */
typedef struct linkedlist_datablock_internal_s
{
struct linkedlist_datablock_internal_s* next_datablock;
uLong avail_in_this_block;
uLong filled_in_this_block;
uLong unused; /* for future use and alignement */
unsigned char data[SIZEDATA_INDATABLOCK];
} linkedlist_datablock_internal;
typedef struct linkedlist_data_s
{
linkedlist_datablock_internal* first_block;
linkedlist_datablock_internal* last_block;
} linkedlist_data;
typedef struct
{
z_stream stream; /* zLib stream structure for inflate */
#ifdef HAVE_BZIP2
bz_stream bstream; /* bzLib stream structure for bziped */
#endif
int stream_initialised; /* 1 is stream is initialised */
uInt pos_in_buffered_data; /* last written byte in buffered_data */
ZPOS64_T pos_local_header; /* offset of the local header of the file
currenty writing */
char* central_header; /* central header data for the current file */
uLong size_centralExtra;
uLong size_centralheader; /* size of the central header for cur file */
uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */
uLong flag; /* flag of the file currently writing */
int method; /* compression method of file currenty wr.*/
int raw; /* 1 for directly writing raw data */
Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/
uLong dosDate;
uLong crc32;
int encrypt;
int zip64; /* Add ZIP64 extened information in the extra field */
ZPOS64_T pos_zip64extrainfo;
ZPOS64_T totalCompressedData;
ZPOS64_T totalUncompressedData;
#ifndef NOCRYPT
unsigned long keys[3]; /* keys defining the pseudo-random sequence */
const unsigned long* pcrc_32_tab;
int crypt_header_size;
#endif
} curfile64_info;
typedef struct
{
zlib_filefunc64_32_def z_filefunc;
voidpf filestream; /* io structore of the zipfile */
linkedlist_data central_dir;/* datablock with central dir in construction*/
int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/
curfile64_info ci; /* info on the file curretly writing */
ZPOS64_T begin_pos; /* position of the beginning of the zipfile */
ZPOS64_T add_position_when_writting_offset;
ZPOS64_T number_entry;
#ifndef NO_ADDFILEINEXISTINGZIP
char *globalcomment;
#endif
} zip64_internal;
#ifndef NOCRYPT
#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED
#include "crypt.h"
#endif
local linkedlist_datablock_internal* allocate_new_datablock()
{
linkedlist_datablock_internal* ldi;
ldi = (linkedlist_datablock_internal*)
ALLOC(sizeof(linkedlist_datablock_internal));
if (ldi!=NULL)
{
ldi->next_datablock = NULL ;
ldi->filled_in_this_block = 0 ;
ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ;
}
return ldi;
}
local void free_datablock(linkedlist_datablock_internal* ldi)
{
while (ldi!=NULL)
{
linkedlist_datablock_internal* ldinext = ldi->next_datablock;
TRYFREE(ldi);
ldi = ldinext;
}
}
local void init_linkedlist(linkedlist_data* ll)
{
ll->first_block = ll->last_block = NULL;
}
local void free_linkedlist(linkedlist_data* ll)
{
free_datablock(ll->first_block);
ll->first_block = ll->last_block = NULL;
}
local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len)
{
linkedlist_datablock_internal* ldi;
const unsigned char* from_copy;
if (ll==NULL)
return ZIP_INTERNALERROR;
if (ll->last_block == NULL)
{
ll->first_block = ll->last_block = allocate_new_datablock();
if (ll->first_block == NULL)
return ZIP_INTERNALERROR;
}
ldi = ll->last_block;
from_copy = (unsigned char*)buf;
while (len>0)
{
uInt copy_this;
uInt i;
unsigned char* to_copy;
if (ldi->avail_in_this_block==0)
{
ldi->next_datablock = allocate_new_datablock();
if (ldi->next_datablock == NULL)
return ZIP_INTERNALERROR;
ldi = ldi->next_datablock ;
ll->last_block = ldi;
}
if (ldi->avail_in_this_block < len)
copy_this = (uInt)ldi->avail_in_this_block;
else
copy_this = (uInt)len;
to_copy = &(ldi->data[ldi->filled_in_this_block]);
for (i=0;i<copy_this;i++)
*(to_copy+i)=*(from_copy+i);
ldi->filled_in_this_block += copy_this;
ldi->avail_in_this_block -= copy_this;
from_copy += copy_this ;
len -= copy_this;
}
return ZIP_OK;
}
/****************************************************************************/
#ifndef NO_ADDFILEINEXISTINGZIP
/* ===========================================================================
Inputs a long in LSB order to the given file
nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T)
*/
local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte));
local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)
{
unsigned char buf[8];
int n;
for (n = 0; n < nbByte; n++)
{
buf[n] = (unsigned char)(x & 0xff);
x >>= 8;
}
if (x != 0)
{ /* data overflow - hack for ZIP64 (X Roche) */
for (n = 0; n < nbByte; n++)
{
buf[n] = 0xff;
}
}
if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte)
return ZIP_ERRNO;
else
return ZIP_OK;
}
local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte));
local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte)
{
unsigned char* buf=(unsigned char*)dest;
int n;
for (n = 0; n < nbByte; n++) {
buf[n] = (unsigned char)(x & 0xff);
x >>= 8;
}
if (x != 0)
{ /* data overflow - hack for ZIP64 */
for (n = 0; n < nbByte; n++)
{
buf[n] = 0xff;
}
}
}
/****************************************************************************/
local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm)
{
uLong year = (uLong)ptm->tm_year;
if (year>=1980)
year-=1980;
else if (year>=80)
year-=80;
return
(uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) |
((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour));
}
/****************************************************************************/
local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi));
local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi)
{
unsigned char c;
int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1);
if (err==1)
{
*pi = (int)c;
return ZIP_OK;
}
else
{
if (ZERROR64(*pzlib_filefunc_def,filestream))
return ZIP_ERRNO;
else
return ZIP_EOF;
}
}
/* ===========================================================================
Reads a long in LSB order from the given gz_stream. Sets
*/
local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX));
local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX)
{
uLong x ;
int i = 0;
int err;
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x = (uLong)i;
if (err==ZIP_OK)
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x += ((uLong)i)<<8;
if (err==ZIP_OK)
*pX = x;
else
*pX = 0;
return err;
}
local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX));
local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX)
{
uLong x ;
int i = 0;
int err;
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x = (uLong)i;
if (err==ZIP_OK)
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x += ((uLong)i)<<8;
if (err==ZIP_OK)
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x += ((uLong)i)<<16;
if (err==ZIP_OK)
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x += ((uLong)i)<<24;
if (err==ZIP_OK)
*pX = x;
else
*pX = 0;
return err;
}
local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX));
local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)
{
ZPOS64_T x;
int i = 0;
int err;
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x = (ZPOS64_T)i;
if (err==ZIP_OK)
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x += ((ZPOS64_T)i)<<8;
if (err==ZIP_OK)
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x += ((ZPOS64_T)i)<<16;
if (err==ZIP_OK)
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x += ((ZPOS64_T)i)<<24;
if (err==ZIP_OK)
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x += ((ZPOS64_T)i)<<32;
if (err==ZIP_OK)
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x += ((ZPOS64_T)i)<<40;
if (err==ZIP_OK)
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x += ((ZPOS64_T)i)<<48;
if (err==ZIP_OK)
err = zip64local_getByte(pzlib_filefunc_def,filestream,&i);
x += ((ZPOS64_T)i)<<56;
if (err==ZIP_OK)
*pX = x;
else
*pX = 0;
return err;
}
#ifndef BUFREADCOMMENT
#define BUFREADCOMMENT (0x400)
#endif
/*
Locate the Central directory of a zipfile (at the end, just before
the global comment)
*/
local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream));
local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)
{
unsigned char* buf;
ZPOS64_T uSizeFile;
ZPOS64_T uBackRead;
ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */
ZPOS64_T uPosFound=0;
if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
return 0;
uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
if (uMaxBack>uSizeFile)
uMaxBack = uSizeFile;
buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
if (buf==NULL)
return 0;
uBackRead = 4;
while (uBackRead<uMaxBack)
{
uLong uReadSize;
ZPOS64_T uReadPos ;
int i;
if (uBackRead+BUFREADCOMMENT>uMaxBack)
uBackRead = uMaxBack;
else
uBackRead+=BUFREADCOMMENT;
uReadPos = uSizeFile-uBackRead ;
uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
(BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos);
if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
break;
if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
break;
- for (i=(int)uReadSize-3; (i--)>0;)
+ for (i=(int)uReadSize-3; (i--)>0;){
if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
- ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
- {
+ ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)){
uPosFound = uReadPos+i;
break;
}
+ }
- if (uPosFound!=0)
- break;
+ if (uPosFound!=0)
+ break;
}
TRYFREE(buf);
return uPosFound;
}
/*
Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before
the global comment)
*/
local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream));
local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)
{
unsigned char* buf;
ZPOS64_T uSizeFile;
ZPOS64_T uBackRead;
ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */
ZPOS64_T uPosFound=0;
uLong uL;
ZPOS64_T relativeOffset;
if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
return 0;
uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
if (uMaxBack>uSizeFile)
uMaxBack = uSizeFile;
buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
if (buf==NULL)
return 0;
uBackRead = 4;
while (uBackRead<uMaxBack)
{
uLong uReadSize;
ZPOS64_T uReadPos;
int i;
if (uBackRead+BUFREADCOMMENT>uMaxBack)
uBackRead = uMaxBack;
else
uBackRead+=BUFREADCOMMENT;
uReadPos = uSizeFile-uBackRead ;
uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
(BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos);
if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
break;
if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
break;
for (i=(int)uReadSize-3; (i--)>0;)
{
// Signature "0x07064b50" Zip64 end of central directory locater
if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07))
{
uPosFound = uReadPos+i;
break;
}
}
if (uPosFound!=0)
break;
}
TRYFREE(buf);
if (uPosFound == 0)
return 0;
/* Zip64 end of central directory locator */
if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0)
return 0;
/* the signature, already checked */
if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK)
return 0;
/* number of the disk with the start of the zip64 end of central directory */
if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK)
return 0;
if (uL != 0)
return 0;
/* relative offset of the zip64 end of central directory record */
if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK)
return 0;
/* total number of disks */
if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK)
return 0;
if (uL != 1)
return 0;
/* Goto Zip64 end of central directory record */
if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0)
return 0;
/* the signature */
if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK)
return 0;
if (uL != 0x06064b50) // signature of 'Zip64 end of central directory'
return 0;
return relativeOffset;
}
int LoadCentralDirectoryRecord(zip64_internal* pziinit)
{
int err=ZIP_OK;
ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
ZPOS64_T size_central_dir; /* size of the central directory */
ZPOS64_T offset_central_dir; /* offset of start of central directory */
ZPOS64_T central_pos;
uLong uL;
uLong number_disk; /* number of the current dist, used for
spaning ZIP, unsupported, always 0*/
uLong number_disk_with_CD; /* number the the disk with central dir, used
for spaning ZIP, unsupported, always 0*/
ZPOS64_T number_entry;
ZPOS64_T number_entry_CD; /* total number of entries in
the central dir
(same than number_entry on nospan) */
uLong VersionMadeBy;
uLong VersionNeeded;
uLong size_comment;
int hasZIP64Record = 0;
// check first if we find a ZIP64 record
central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream);
if(central_pos > 0)
{
hasZIP64Record = 1;
}
else if(central_pos == 0)
{
central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream);
}
/* disable to allow appending to empty ZIP archive
if (central_pos==0)
err=ZIP_ERRNO;
*/
if(hasZIP64Record)
{
ZPOS64_T sizeEndOfCentralDirectory;
if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0)
err=ZIP_ERRNO;
/* the signature, already checked */
if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK)
err=ZIP_ERRNO;
/* size of zip64 end of central directory record */
if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK)
err=ZIP_ERRNO;
/* version made by */
if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK)
err=ZIP_ERRNO;
/* version needed to extract */
if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK)
err=ZIP_ERRNO;
/* number of this disk */
if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK)
err=ZIP_ERRNO;
/* number of the disk with the start of the central directory */
if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK)
err=ZIP_ERRNO;
/* total number of entries in the central directory on this disk */
if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK)
err=ZIP_ERRNO;
/* total number of entries in the central directory */
if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK)
err=ZIP_ERRNO;
if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0))
err=ZIP_BADZIPFILE;
/* size of the central directory */
if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK)
err=ZIP_ERRNO;
/* offset of start of central directory with respect to the
starting disk number */
if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK)
err=ZIP_ERRNO;
// TODO..
// read the comment from the standard central header.
size_comment = 0;
}
else
{
// Read End of central Directory info
if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
err=ZIP_ERRNO;
/* the signature, already checked */
if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK)
err=ZIP_ERRNO;
/* number of this disk */
if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK)
err=ZIP_ERRNO;
/* number of the disk with the start of the central directory */
if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK)
err=ZIP_ERRNO;
/* total number of entries in the central dir on this disk */
number_entry = 0;
if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK)
err=ZIP_ERRNO;
else
number_entry = uL;
/* total number of entries in the central dir */
number_entry_CD = 0;
if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK)
err=ZIP_ERRNO;
else
number_entry_CD = uL;
if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0))
err=ZIP_BADZIPFILE;
/* size of the central directory */
size_central_dir = 0;
if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK)
err=ZIP_ERRNO;
else
size_central_dir = uL;
/* offset of start of central directory with respect to the starting disk number */
offset_central_dir = 0;
if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK)
err=ZIP_ERRNO;
else
offset_central_dir = uL;
/* zipfile global comment length */
if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK)
err=ZIP_ERRNO;
}
if ((central_pos<offset_central_dir+size_central_dir) &&
(err==ZIP_OK))
err=ZIP_BADZIPFILE;
if (err!=ZIP_OK)
{
ZCLOSE64(pziinit->z_filefunc, pziinit->filestream);
return ZIP_ERRNO;
}
if (size_comment>0)
{
pziinit->globalcomment = (char*)ALLOC(size_comment+1);
if (pziinit->globalcomment)
{
size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment);
pziinit->globalcomment[size_comment]=0;
}
}
byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir);
pziinit->add_position_when_writting_offset = byte_before_the_zipfile;
{
ZPOS64_T size_central_dir_to_read = size_central_dir;
size_t buf_size = SIZEDATA_INDATABLOCK;
void* buf_read = (void*)ALLOC(buf_size);
if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0)
err=ZIP_ERRNO;
while ((size_central_dir_to_read>0) && (err==ZIP_OK))
{
ZPOS64_T read_this = SIZEDATA_INDATABLOCK;
if (read_this > size_central_dir_to_read)
read_this = size_central_dir_to_read;
if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this)
err=ZIP_ERRNO;
if (err==ZIP_OK)
err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this);
size_central_dir_to_read-=read_this;
}
TRYFREE(buf_read);
}
pziinit->begin_pos = byte_before_the_zipfile;
pziinit->number_entry = number_entry_CD;
if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0)
err=ZIP_ERRNO;
return err;
}
#endif /* !NO_ADDFILEINEXISTINGZIP*/
/************************************************************/
extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def)
{
zip64_internal ziinit;
zip64_internal* zi;
int err=ZIP_OK;
ziinit.z_filefunc.zseek32_file = NULL;
ziinit.z_filefunc.ztell32_file = NULL;
if (pzlib_filefunc64_32_def==NULL)
fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64);
else
ziinit.z_filefunc = *pzlib_filefunc64_32_def;
ziinit.filestream = ZOPEN64(ziinit.z_filefunc,
pathname,
(append == APPEND_STATUS_CREATE) ?
(ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) :
(ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING));
if (ziinit.filestream == NULL)
return NULL;
if (append == APPEND_STATUS_CREATEAFTER)
ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END);
ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream);
ziinit.in_opened_file_inzip = 0;
ziinit.ci.stream_initialised = 0;
ziinit.number_entry = 0;
ziinit.add_position_when_writting_offset = 0;
init_linkedlist(&(ziinit.central_dir));
zi = (zip64_internal*)ALLOC(sizeof(zip64_internal));
if (zi==NULL)
{
ZCLOSE64(ziinit.z_filefunc,ziinit.filestream);
return NULL;
}
/* now we add file in a zipfile */
# ifndef NO_ADDFILEINEXISTINGZIP
ziinit.globalcomment = NULL;
if (append == APPEND_STATUS_ADDINZIP)
{
// Read and Cache Central Directory Records
err = LoadCentralDirectoryRecord(&ziinit);
}
if (globalcomment)
{
*globalcomment = ziinit.globalcomment;
}
# endif /* !NO_ADDFILEINEXISTINGZIP*/
if (err != ZIP_OK)
{
# ifndef NO_ADDFILEINEXISTINGZIP
TRYFREE(ziinit.globalcomment);
# endif /* !NO_ADDFILEINEXISTINGZIP*/
TRYFREE(zi);
return NULL;
}
else
{
*zi = ziinit;
return (zipFile)zi;
}
}
extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def)
{
if (pzlib_filefunc32_def != NULL)
{
zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def);
return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill);
}
else
return zipOpen3(pathname, append, globalcomment, NULL);
}
extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def)
{
if (pzlib_filefunc_def != NULL)
{
zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def;
zlib_filefunc64_32_def_fill.ztell32_file = NULL;
zlib_filefunc64_32_def_fill.zseek32_file = NULL;
return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill);
}
else
return zipOpen3(pathname, append, globalcomment, NULL);
}
extern zipFile ZEXPORT zipOpen (const char* pathname, int append)
{
return zipOpen3((const void*)pathname,append,NULL,NULL);
}
extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append)
{
return zipOpen3(pathname,append,NULL,NULL);
}
int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local)
{
/* write the local header */
int err;
uInt size_filename = (uInt)strlen(filename);
uInt size_extrafield = size_extrafield_local;
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4);
if (err==ZIP_OK)
{
if(zi->ci.zip64)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */
else
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */
}
if (err==ZIP_OK)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2);
if (err==ZIP_OK)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2);
if (err==ZIP_OK)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4);
// CRC / Compressed size / Uncompressed size will be filled in later and rewritten later
if (err==ZIP_OK)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */
if (err==ZIP_OK)
{
if(zi->ci.zip64)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */
else
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */
}
if (err==ZIP_OK)
{
if(zi->ci.zip64)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */
else
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */
}
if (err==ZIP_OK)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2);
if(zi->ci.zip64)
{
size_extrafield += 20;
}
if (err==ZIP_OK)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2);
if ((err==ZIP_OK) && (size_filename > 0))
{
if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename)
err = ZIP_ERRNO;
}
if ((err==ZIP_OK) && (size_extrafield_local > 0))
{
if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local)
err = ZIP_ERRNO;
}
if ((err==ZIP_OK) && (zi->ci.zip64))
{
// write the Zip64 extended info
short HeaderID = 1;
short DataSize = 16;
ZPOS64_T CompressedSize = 0;
ZPOS64_T UncompressedSize = 0;
// Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file)
zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream);
err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2);
err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2);
err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8);
err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8);
}
return err;
}
/*
NOTE.
When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped
before calling this function it can be done with zipRemoveExtraInfoBlock
It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize
unnecessary allocations.
*/
extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi,
const void* extrafield_local, uInt size_extrafield_local,
const void* extrafield_global, uInt size_extrafield_global,
const char* comment, int method, int level, int raw,
int windowBits,int memLevel, int strategy,
const char* password, uLong crcForCrypting,
uLong versionMadeBy, uLong flagBase, int zip64)
{
zip64_internal* zi;
uInt size_filename;
uInt size_comment;
uInt i;
int err = ZIP_OK;
# ifdef NOCRYPT
if (password != NULL)
return ZIP_PARAMERROR;
# endif
if (file == NULL)
return ZIP_PARAMERROR;
#ifdef HAVE_BZIP2
if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED))
return ZIP_PARAMERROR;
#else
if ((method!=0) && (method!=Z_DEFLATED))
return ZIP_PARAMERROR;
#endif
zi = (zip64_internal*)file;
if (zi->in_opened_file_inzip == 1)
{
err = zipCloseFileInZip (file);
if (err != ZIP_OK)
return err;
}
if (filename==NULL)
filename="-";
if (comment==NULL)
size_comment = 0;
else
size_comment = (uInt)strlen(comment);
size_filename = (uInt)strlen(filename);
if (zipfi == NULL)
zi->ci.dosDate = 0;
else
{
if (zipfi->dosDate != 0)
zi->ci.dosDate = zipfi->dosDate;
else
zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date);
}
zi->ci.flag = flagBase;
if ((level==8) || (level==9))
zi->ci.flag |= 2;
if (level==2)
zi->ci.flag |= 4;
if (level==1)
zi->ci.flag |= 6;
if (password != NULL)
zi->ci.flag |= 1;
zi->ci.crc32 = 0;
zi->ci.method = method;
zi->ci.encrypt = 0;
zi->ci.stream_initialised = 0;
zi->ci.pos_in_buffered_data = 0;
zi->ci.raw = raw;
zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream);
zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment;
zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data
zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree);
zi->ci.size_centralExtra = size_extrafield_global;
zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4);
/* version info */
zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2);
zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2);
zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2);
zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2);
zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4);
zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/
zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/
zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/
zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2);
zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2);
zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2);
zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/
if (zipfi==NULL)
zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2);
else
zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2);
if (zipfi==NULL)
zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4);
else
zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4);
if(zi->ci.pos_local_header >= 0xffffffff)
zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4);
else
zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4);
for (i=0;i<size_filename;i++)
*(zi->ci.central_header+SIZECENTRALHEADER+i) = *(filename+i);
for (i=0;i<size_extrafield_global;i++)
*(zi->ci.central_header+SIZECENTRALHEADER+size_filename+i) =
*(((const char*)extrafield_global)+i);
for (i=0;i<size_comment;i++)
*(zi->ci.central_header+SIZECENTRALHEADER+size_filename+
size_extrafield_global+i) = *(comment+i);
if (zi->ci.central_header == NULL)
return ZIP_INTERNALERROR;
zi->ci.zip64 = zip64;
zi->ci.totalCompressedData = 0;
zi->ci.totalUncompressedData = 0;
zi->ci.pos_zip64extrainfo = 0;
err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local);
#ifdef HAVE_BZIP2
zi->ci.bstream.avail_in = (uInt)0;
zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE;
zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
zi->ci.bstream.total_in_hi32 = 0;
zi->ci.bstream.total_in_lo32 = 0;
zi->ci.bstream.total_out_hi32 = 0;
zi->ci.bstream.total_out_lo32 = 0;
#endif
zi->ci.stream.avail_in = (uInt)0;
zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
zi->ci.stream.next_out = zi->ci.buffered_data;
zi->ci.stream.total_in = 0;
zi->ci.stream.total_out = 0;
zi->ci.stream.data_type = Z_BINARY;
#ifdef HAVE_BZIP2
if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
#else
if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
#endif
{
if(zi->ci.method == Z_DEFLATED)
{
zi->ci.stream.zalloc = (alloc_func)0;
zi->ci.stream.zfree = (free_func)0;
zi->ci.stream.opaque = (voidpf)0;
if (windowBits>0)
windowBits = -windowBits;
err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy);
if (err==Z_OK)
zi->ci.stream_initialised = Z_DEFLATED;
}
else if(zi->ci.method == Z_BZIP2ED)
{
#ifdef HAVE_BZIP2
// Init BZip stuff here
zi->ci.bstream.bzalloc = 0;
zi->ci.bstream.bzfree = 0;
zi->ci.bstream.opaque = (voidpf)0;
err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35);
if(err == BZ_OK)
zi->ci.stream_initialised = Z_BZIP2ED;
#endif
}
}
# ifndef NOCRYPT
zi->ci.crypt_header_size = 0;
if ((err==Z_OK) && (password != NULL))
{
unsigned char bufHead[RAND_HEAD_LEN];
unsigned int sizeHead;
zi->ci.encrypt = 1;
zi->ci.pcrc_32_tab = (const unsigned long*) get_crc_table();
/*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/
sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting);
zi->ci.crypt_header_size = sizeHead;
if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead)
err = ZIP_ERRNO;
}
# endif
if (err==Z_OK)
zi->in_opened_file_inzip = 1;
return err;
}
extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi,
const void* extrafield_local, uInt size_extrafield_local,
const void* extrafield_global, uInt size_extrafield_global,
const char* comment, int method, int level, int raw,
int windowBits,int memLevel, int strategy,
const char* password, uLong crcForCrypting,
uLong versionMadeBy, uLong flagBase)
{
return zipOpenNewFileInZip4_64 (file, filename, zipfi,
extrafield_local, size_extrafield_local,
extrafield_global, size_extrafield_global,
comment, method, level, raw,
windowBits, memLevel, strategy,
password, crcForCrypting, versionMadeBy, flagBase, 0);
}
extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi,
const void* extrafield_local, uInt size_extrafield_local,
const void* extrafield_global, uInt size_extrafield_global,
const char* comment, int method, int level, int raw,
int windowBits,int memLevel, int strategy,
const char* password, uLong crcForCrypting)
{
return zipOpenNewFileInZip4_64 (file, filename, zipfi,
extrafield_local, size_extrafield_local,
extrafield_global, size_extrafield_global,
comment, method, level, raw,
windowBits, memLevel, strategy,
password, crcForCrypting, VERSIONMADEBY, 0, 0);
}
extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi,
const void* extrafield_local, uInt size_extrafield_local,
const void* extrafield_global, uInt size_extrafield_global,
const char* comment, int method, int level, int raw,
int windowBits,int memLevel, int strategy,
const char* password, uLong crcForCrypting, int zip64)
{
return zipOpenNewFileInZip4_64 (file, filename, zipfi,
extrafield_local, size_extrafield_local,
extrafield_global, size_extrafield_global,
comment, method, level, raw,
windowBits, memLevel, strategy,
password, crcForCrypting, VERSIONMADEBY, 0, zip64);
}
extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi,
const void* extrafield_local, uInt size_extrafield_local,
const void* extrafield_global, uInt size_extrafield_global,
const char* comment, int method, int level, int raw)
{
return zipOpenNewFileInZip4_64 (file, filename, zipfi,
extrafield_local, size_extrafield_local,
extrafield_global, size_extrafield_global,
comment, method, level, raw,
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
NULL, 0, VERSIONMADEBY, 0, 0);
}
extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi,
const void* extrafield_local, uInt size_extrafield_local,
const void* extrafield_global, uInt size_extrafield_global,
const char* comment, int method, int level, int raw, int zip64)
{
return zipOpenNewFileInZip4_64 (file, filename, zipfi,
extrafield_local, size_extrafield_local,
extrafield_global, size_extrafield_global,
comment, method, level, raw,
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
NULL, 0, VERSIONMADEBY, 0, zip64);
}
extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi,
const void* extrafield_local, uInt size_extrafield_local,
const void*extrafield_global, uInt size_extrafield_global,
const char* comment, int method, int level, int zip64)
{
return zipOpenNewFileInZip4_64 (file, filename, zipfi,
extrafield_local, size_extrafield_local,
extrafield_global, size_extrafield_global,
comment, method, level, 0,
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
NULL, 0, VERSIONMADEBY, 0, zip64);
}
extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi,
const void* extrafield_local, uInt size_extrafield_local,
const void*extrafield_global, uInt size_extrafield_global,
const char* comment, int method, int level)
{
return zipOpenNewFileInZip4_64 (file, filename, zipfi,
extrafield_local, size_extrafield_local,
extrafield_global, size_extrafield_global,
comment, method, level, 0,
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
NULL, 0, VERSIONMADEBY, 0, 0);
}
local int zip64FlushWriteBuffer(zip64_internal* zi)
{
int err=ZIP_OK;
if (zi->ci.encrypt != 0)
{
#ifndef NOCRYPT
uInt i;
int t;
for (i=0;i<zi->ci.pos_in_buffered_data;i++)
zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t);
#endif
}
if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data)
err = ZIP_ERRNO;
zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data;
#ifdef HAVE_BZIP2
if(zi->ci.method == Z_BZIP2ED)
{
zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32;
zi->ci.bstream.total_in_lo32 = 0;
zi->ci.bstream.total_in_hi32 = 0;
}
else
#endif
{
zi->ci.totalUncompressedData += zi->ci.stream.total_in;
zi->ci.stream.total_in = 0;
}
zi->ci.pos_in_buffered_data = 0;
return err;
}
extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len)
{
zip64_internal* zi;
int err=ZIP_OK;
if (file == NULL)
return ZIP_PARAMERROR;
zi = (zip64_internal*)file;
if (zi->in_opened_file_inzip == 0)
return ZIP_PARAMERROR;
zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len);
#ifdef HAVE_BZIP2
if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw))
{
zi->ci.bstream.next_in = (void*)buf;
zi->ci.bstream.avail_in = len;
err = BZ_RUN_OK;
while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0))
{
if (zi->ci.bstream.avail_out == 0)
{
if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
err = ZIP_ERRNO;
zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE;
zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
}
if(err != BZ_RUN_OK)
break;
if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
{
uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32;
// uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32;
err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN);
zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ;
}
}
if(err == BZ_RUN_OK)
err = ZIP_OK;
}
else
#endif
{
zi->ci.stream.next_in = (Bytef*)buf;
zi->ci.stream.avail_in = len;
while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0))
{
if (zi->ci.stream.avail_out == 0)
{
if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
err = ZIP_ERRNO;
zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
zi->ci.stream.next_out = zi->ci.buffered_data;
}
if(err != ZIP_OK)
break;
if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
{
uLong uTotalOutBefore = zi->ci.stream.total_out;
err=deflate(&zi->ci.stream, Z_NO_FLUSH);
if(uTotalOutBefore > zi->ci.stream.total_out)
{
int bBreak = 0;
bBreak++;
}
zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
}
else
{
uInt copy_this,i;
if (zi->ci.stream.avail_in < zi->ci.stream.avail_out)
copy_this = zi->ci.stream.avail_in;
else
copy_this = zi->ci.stream.avail_out;
for (i = 0; i < copy_this; i++)
*(((char*)zi->ci.stream.next_out)+i) =
*(((const char*)zi->ci.stream.next_in)+i);
{
zi->ci.stream.avail_in -= copy_this;
zi->ci.stream.avail_out-= copy_this;
zi->ci.stream.next_in+= copy_this;
zi->ci.stream.next_out+= copy_this;
zi->ci.stream.total_in+= copy_this;
zi->ci.stream.total_out+= copy_this;
zi->ci.pos_in_buffered_data += copy_this;
}
}
}// while(...)
}
return err;
}
extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32)
{
return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32);
}
extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32)
{
zip64_internal* zi;
ZPOS64_T compressed_size;
uLong invalidValue = 0xffffffff;
short datasize = 0;
int err=ZIP_OK;
if (file == NULL)
return ZIP_PARAMERROR;
zi = (zip64_internal*)file;
if (zi->in_opened_file_inzip == 0)
return ZIP_PARAMERROR;
zi->ci.stream.avail_in = 0;
if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
{
while (err==ZIP_OK)
{
uLong uTotalOutBefore;
if (zi->ci.stream.avail_out == 0)
{
if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
err = ZIP_ERRNO;
zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
zi->ci.stream.next_out = zi->ci.buffered_data;
}
uTotalOutBefore = zi->ci.stream.total_out;
err=deflate(&zi->ci.stream, Z_FINISH);
zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
}
}
else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
{
#ifdef HAVE_BZIP2
err = BZ_FINISH_OK;
while (err==BZ_FINISH_OK)
{
uLong uTotalOutBefore;
if (zi->ci.bstream.avail_out == 0)
{
if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
err = ZIP_ERRNO;
zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE;
zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
}
uTotalOutBefore = zi->ci.bstream.total_out_lo32;
err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH);
if(err == BZ_STREAM_END)
err = Z_STREAM_END;
zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore);
}
if(err == BZ_FINISH_OK)
err = ZIP_OK;
#endif
}
if (err==Z_STREAM_END)
err=ZIP_OK; /* this is normal */
if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK))
{
if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO)
err = ZIP_ERRNO;
}
if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
{
int tmp_err = deflateEnd(&zi->ci.stream);
if (err == ZIP_OK)
err = tmp_err;
zi->ci.stream_initialised = 0;
}
#ifdef HAVE_BZIP2
else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
{
int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream);
if (err==ZIP_OK)
err = tmperr;
zi->ci.stream_initialised = 0;
}
#endif
if (!zi->ci.raw)
{
crc32 = (uLong)zi->ci.crc32;
uncompressed_size = zi->ci.totalUncompressedData;
}
compressed_size = zi->ci.totalCompressedData;
# ifndef NOCRYPT
compressed_size += zi->ci.crypt_header_size;
# endif
// update Current Item crc and sizes,
if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff)
{
/*version Made by*/
zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2);
/*version needed*/
zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2);
}
zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/
if(compressed_size >= 0xffffffff)
zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/
else
zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/
/// set internal file attributes field
if (zi->ci.stream.data_type == Z_ASCII)
zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2);
if(uncompressed_size >= 0xffffffff)
zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/
else
zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/
// Add ZIP64 extra info field for uncompressed size
if(uncompressed_size >= 0xffffffff)
datasize += 8;
// Add ZIP64 extra info field for compressed size
if(compressed_size >= 0xffffffff)
datasize += 8;
// Add ZIP64 extra info field for relative offset to local file header of current file
if(zi->ci.pos_local_header >= 0xffffffff)
datasize += 8;
if(datasize > 0)
{
char* p = NULL;
if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree)
{
// we can not write more data to the buffer that we have room for.
return ZIP_BADZIPFILE;
}
p = zi->ci.central_header + zi->ci.size_centralheader;
// Add Extra Information Header for 'ZIP64 information'
zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID
p += 2;
zip64local_putValue_inmemory(p, datasize, 2); // DataSize
p += 2;
if(uncompressed_size >= 0xffffffff)
{
zip64local_putValue_inmemory(p, uncompressed_size, 8);
p += 8;
}
if(compressed_size >= 0xffffffff)
{
zip64local_putValue_inmemory(p, compressed_size, 8);
p += 8;
}
if(zi->ci.pos_local_header >= 0xffffffff)
{
zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8);
p += 8;
}
// Update how much extra free space we got in the memory buffer
// and increase the centralheader size so the new ZIP64 fields are included
// ( 4 below is the size of HeaderID and DataSize field )
zi->ci.size_centralExtraFree -= datasize + 4;
zi->ci.size_centralheader += datasize + 4;
// Update the extra info size field
zi->ci.size_centralExtra += datasize + 4;
zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2);
}
if (err==ZIP_OK)
err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader);
free(zi->ci.central_header);
if (err==ZIP_OK)
{
// Update the LocalFileHeader with the new values.
ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream);
if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0)
err = ZIP_ERRNO;
if (err==ZIP_OK)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */
if(uncompressed_size >= 0xffffffff)
{
if(zi->ci.pos_zip64extrainfo > 0)
{
// Update the size in the ZIP64 extended field.
if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0)
err = ZIP_ERRNO;
if (err==ZIP_OK) /* compressed size, unknown */
err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8);
if (err==ZIP_OK) /* uncompressed size, unknown */
err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8);
}
}
else
{
if (err==ZIP_OK) /* compressed size, unknown */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4);
if (err==ZIP_OK) /* uncompressed size, unknown */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4);
}
if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0)
err = ZIP_ERRNO;
}
zi->number_entry ++;
zi->in_opened_file_inzip = 0;
return err;
}
extern int ZEXPORT zipCloseFileInZip (zipFile file)
{
return zipCloseFileInZipRaw (file,0,0);
}
int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip)
{
int err = ZIP_OK;
ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset;
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4);
/*num disks*/
if (err==ZIP_OK) /* number of the disk with the start of the central directory */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4);
/*relative offset*/
if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8);
/*total disks*/ /* Do not support spawning of disk so always say 1 here*/
if (err==ZIP_OK) /* number of the disk with the start of the central directory */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4);
return err;
}
int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip)
{
int err = ZIP_OK;
uLong Zip64DataSize = 44;
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4);
if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ?
if (err==ZIP_OK) /* version made by */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);
if (err==ZIP_OK) /* version needed */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);
if (err==ZIP_OK) /* number of this disk */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4);
if (err==ZIP_OK) /* number of the disk with the start of the central directory */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4);
if (err==ZIP_OK) /* total number of entries in the central dir on this disk */
err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8);
if (err==ZIP_OK) /* total number of entries in the central dir */
err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8);
if (err==ZIP_OK) /* size of the central directory */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8);
if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */
{
ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8);
}
return err;
}
int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip)
{
int err = ZIP_OK;
/*signature*/
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4);
if (err==ZIP_OK) /* number of this disk */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2);
if (err==ZIP_OK) /* number of the disk with the start of the central directory */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2);
if (err==ZIP_OK) /* total number of entries in the central dir on this disk */
{
{
if(zi->number_entry >= 0xFFFF)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record
else
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2);
}
}
if (err==ZIP_OK) /* total number of entries in the central dir */
{
if(zi->number_entry >= 0xFFFF)
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record
else
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2);
}
if (err==ZIP_OK) /* size of the central directory */
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4);
if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */
{
ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
if(pos >= 0xffffffff)
{
err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4);
}
else
err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4);
}
return err;
}
int Write_GlobalComment(zip64_internal* zi, const char* global_comment)
{
int err = ZIP_OK;
uInt size_global_comment = 0;
if(global_comment != NULL)
size_global_comment = (uInt)strlen(global_comment);
err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2);
if (err == ZIP_OK && size_global_comment > 0)
{
if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment)
err = ZIP_ERRNO;
}
return err;
}
extern int ZEXPORT zipClose (zipFile file, const char* global_comment)
{
zip64_internal* zi;
int err = 0;
uLong size_centraldir = 0;
ZPOS64_T centraldir_pos_inzip;
ZPOS64_T pos;
if (file == NULL)
return ZIP_PARAMERROR;
zi = (zip64_internal*)file;
if (zi->in_opened_file_inzip == 1)
{
err = zipCloseFileInZip (file);
}
#ifndef NO_ADDFILEINEXISTINGZIP
if (global_comment==NULL)
global_comment = zi->globalcomment;
#endif
centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream);
if (err==ZIP_OK)
{
linkedlist_datablock_internal* ldi = zi->central_dir.first_block;
while (ldi!=NULL)
{
if ((err==ZIP_OK) && (ldi->filled_in_this_block>0))
{
if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block)
err = ZIP_ERRNO;
}
size_centraldir += ldi->filled_in_this_block;
ldi = ldi->next_datablock;
}
}
free_linkedlist(&(zi->central_dir));
pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
if(pos >= 0xffffffff)
{
ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream);
Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip);
Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos);
}
if (err==ZIP_OK)
err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip);
if(err == ZIP_OK)
err = Write_GlobalComment(zi, global_comment);
if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0)
if (err == ZIP_OK)
err = ZIP_ERRNO;
#ifndef NO_ADDFILEINEXISTINGZIP
TRYFREE(zi->globalcomment);
#endif
TRYFREE(zi);
return err;
}
extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader)
{
char* p = pData;
int size = 0;
char* pNewHeader;
char* pTmp;
short header;
short dataSize;
int retVal = ZIP_OK;
if(pData == NULL || *dataLen < 4)
return ZIP_PARAMERROR;
pNewHeader = (char*)ALLOC(*dataLen);
pTmp = pNewHeader;
while(p < (pData + *dataLen))
{
header = *(short*)p;
dataSize = *(((short*)p)+1);
if( header == sHeader ) // Header found.
{
p += dataSize + 4; // skip it. do not copy to temp buffer
}
else
{
// Extra Info block should not be removed, So copy it to the temp buffer.
memcpy(pTmp, p, dataSize + 4);
p += dataSize + 4;
size += dataSize + 4;
}
}
if(size < *dataLen)
{
// clean old extra info block.
memset(pData,0, *dataLen);
// copy the new extra info block over the old
if(size > 0)
memcpy(pData, pNewHeader, size);
// set the new extra info size
*dataLen = size;
retVal = ZIP_OK;
}
else
retVal = ZIP_ERRNO;
TRYFREE(pNewHeader);
return retVal;
}
diff --git a/src/menu/options.cpp b/src/menu/options.cpp
index f61356d2..68ce52f2 100644
--- a/src/menu/options.cpp
+++ b/src/menu/options.cpp
@@ -1,3129 +1,3129 @@
#include "r-tech1/graphics/bitmap.h"
#include "r-tech1/menu/options.h"
#include "r-tech1/token.h"
#include "r-tech1/input/input-source.h"
#include "r-tech1/parameter.h"
#include "r-tech1/tokenreader.h"
#include "r-tech1/menu/menu.h"
#include "r-tech1/configuration.h"
#include "r-tech1/exceptions/load_exception.h"
#include "r-tech1/menu/menu-exception.h"
#include "r-tech1/init.h"
#include "r-tech1/events.h"
#include "r-tech1/version.h"
#include "r-tech1/menu/optionfactory.h"
#include "r-tech1/sound/music.h"
#include "r-tech1/input/keyboard.h"
#include "r-tech1/funcs.h"
#include "r-tech1/file-system.h"
#include "r-tech1/system.h"
#include "r-tech1/font_factory.h"
#include "r-tech1/exceptions/shutdown_exception.h"
#include "r-tech1/exceptions/exception.h"
#include "r-tech1/font.h"
#include "r-tech1/gui/box.h"
#include "r-tech1/thread.h"
#include "r-tech1/loading.h"
#include "r-tech1/input/input-map.h"
#include "r-tech1/input/input-manager.h"
#include <sstream>
#include <algorithm>
#include <time.h>
#include <math.h>
using namespace std;
using namespace Gui;
/* true if the arguments passed in match todays date.
* pass 0 for any argument that you don't care about (it will match any date)
*/
static bool todaysDate(int month, int day, int year){
time_t result = time(NULL);
struct tm * local = localtime(&result);
return (month == 0 || month == (local->tm_mon + 1)) &&
(day == 0 || day == local->tm_mday) &&
(year == 0 || year == local->tm_year + 1900);
}
static bool jonBirthday(){
return todaysDate(3, 25, 0);
}
static bool miguelBirthday(){
return todaysDate(8, 11, 0);
}
OptionCredits::Block::Block(const std::string & title):
title(title),
titleColorOverride(false),
titleColor(Graphics::makeColor(0,255,255)),
colorOverride(false),
color(Graphics::makeColor(255,255,255)),
spacing(0){
}
OptionCredits::Block::Block(const Token * token):
titleColorOverride(false),
titleColor(Graphics::makeColor(0,255,255)),
colorOverride(false),
color(Graphics::makeColor(255,255,255)),
topWidth(0),
topHeight(0),
bottomWidth(0),
bottomHeight(0),
spacing(0){
if ( *token != "block" ){
throw LoadException(__FILE__, __LINE__, "Not a credit block");
}
TokenView view = token->view();
while (view.hasMore()){
std::string match;
try{
const Token * tok;
view >> tok;
if ( *tok == "title" ) {
tok->view() >> title;
} else if (*tok == "credit"){
std::string credit;
tok->view() >> credit;
credits.push_back(credit);
} else if ( *tok == "titlecolor" ) {
try{
int r,b,g;
tok->view() >> r >> g >> b;
titleColor = Graphics::makeColor( r, g, b );
titleColorOverride = true;
} catch (const TokenException & ex){
}
} else if ( *tok == "color" ) {
try{
int r,b,g;
tok->view() >> r >> g >> b;
color = Graphics::makeColor( r, g, b );
colorOverride = true;
} catch (const TokenException & ex){
}
} else if ( *tok == "animation" ) {
TokenView animView = tok->view();
while (animView.hasMore()){
const Token * animTok;
animView >> animTok;
if (*animTok == "top"){
tok->match("_/width", topWidth);
tok->match("_/height", topHeight);
topAnimation = Util::ReferenceCount<Gui::Animation>(new Animation(tok));
} else if (*animTok == "bottom"){
tok->match("_/width", bottomWidth);
tok->match("_/height", bottomHeight);
bottomAnimation = Util::ReferenceCount<Gui::Animation>(new Animation(tok));
}
}
} else if (*tok == "spacing"){
tok->view() >> spacing;
} else {
Global::debug( 3 ) <<"Unhandled Credit Block attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Credit Block parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
}
OptionCredits::Block::Block(const OptionCredits::Block & copy):
title(copy.title),
credits(copy.credits),
titleColorOverride(copy.titleColorOverride),
titleColor(copy.titleColor),
colorOverride(copy.colorOverride),
color(copy.color),
topAnimation(copy.topAnimation),
topWidth(copy.topWidth),
topHeight(copy.topHeight),
bottomAnimation(copy.bottomAnimation),
bottomWidth(copy.bottomWidth),
bottomHeight(copy.bottomHeight),
spacing(copy.spacing){
}
OptionCredits::Block::~Block(){
}
const OptionCredits::Block & OptionCredits::Block::operator=(const OptionCredits::Block & copy){
title = copy.title;
credits = copy.credits;
titleColor = copy.titleColor;
titleColorOverride = copy.titleColorOverride;
color = copy.color;
colorOverride = copy.colorOverride;
topAnimation = copy.topAnimation;
topWidth =copy.topWidth;
topHeight = copy.topHeight;
bottomAnimation = copy.bottomAnimation;
bottomWidth = copy.bottomWidth;
bottomHeight = copy.bottomHeight;
spacing = copy.spacing;
return *this;
}
void OptionCredits::Block::addCredit(const std::string & credit){
credits.push_back(credit);
}
void OptionCredits::Block::act(){
// Top animation
if (topAnimation != NULL){
topAnimation->act();
}
// Bottom animation
if (bottomAnimation != NULL){
bottomAnimation->act();
}
}
int OptionCredits::Block::print(int x, int y, Graphics::Color defaultTitleColor, Graphics::Color defaultColor, const Font & font, const Graphics::Bitmap & work, const Justification & justification) const {
int currentY = y;
// Top animation
if (topAnimation != NULL){
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = topWidth/2;
break;
case Right:
xmod = topWidth;
break;
}
// FIXME temporary solution
const Graphics::Bitmap temp(topWidth, topHeight);
//topAnimation->draw(x - xmod, y, topWidth, topHeight, work);
topAnimation->draw(0, 0, topWidth, topHeight, temp);
temp.translucent().draw(x-xmod, y, work);
currentY += topHeight;
}
if (!title.empty()){
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = font.textLength(title.c_str())/2;
break;
case Right:
xmod = font.textLength(title.c_str());
break;
}
font.printf(x - xmod, currentY, (titleColorOverride ? titleColor : defaultTitleColor), work, title, 0);
currentY += font.getHeight();
}
for (std::vector<std::string>::const_iterator i = credits.begin(); i != credits.end(); ++i){
const std::string & credit = *i;
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = font.textLength(credit.c_str())/2;
break;
case Right:
xmod = font.textLength(credit.c_str());
break;
}
font.printf(x - xmod, currentY, (colorOverride ? color : defaultColor), work, credit, 0);
currentY += font.getHeight();
}
// Bottom animation
if (bottomAnimation != NULL){
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = bottomWidth/2;
break;
case Right:
xmod = bottomWidth;
break;
}
// FIXME temporary solution
const Graphics::Bitmap temp(topWidth, topHeight);
//bottomAnimation->draw(x - xmod, y, bottomWidth, bottomHeight, work);
bottomAnimation->draw(0, 0, bottomWidth, bottomHeight, temp);
temp.translucent().draw(x-xmod, y, work);
currentY += bottomHeight;
}
currentY += font.getHeight() + spacing;
return currentY;
}
const int OptionCredits::Block::size(const Font & font) const{
// Counts title and space in between
int total = 0;
if (topAnimation != NULL){
total += topHeight;
}
if (!title.empty()){
total+= font.getHeight();
}
total += credits.size() * font.getHeight();
if (bottomAnimation != NULL){
total += bottomHeight;
}
total += font.getHeight();
total += spacing;
return total;
}
OptionCredits::Sequence::Sequence(const Token * token):
type(Primary),
x(0),
y(0),
startx(0),
endx(0),
starty(0),
endy(0),
ticks(0),
duration(250),
speed(0),
alpha(0),
alphaMultiplier(0),
justification(Block::Center),
current(0),
done(false),
creditLength(0){
if ( *token != "sequence" ){
throw LoadException(__FILE__, __LINE__, "Not a credit sequence");
}
TokenView view = token->view();
while (view.hasMore()){
std::string match;
try{
const Token * tok;
view >> tok;
if (*tok == "type"){
std::string sequenceType;
tok->view() >> sequenceType;
if (sequenceType == "roll"){
type = Roll;
} else if (sequenceType == "primary"){
type = Primary;
}
} else if (*tok == "start-x"){
tok->view() >> startx;
} else if (*tok == "end-x"){
tok->view() >> endx;
} else if (*tok == "start-y"){
tok->view() >> starty;
} else if (*tok == "end-y"){
tok->view() >> endy;
} else if (*tok == "duration"){
tok->view() >> duration;
} else if (*tok == "speed"){
tok->view() >> speed;
} else if (*tok == "alpha-multiplier"){
tok->view() >> alphaMultiplier;
} else if ( *tok == "justification" ) {
std::string justify;
tok->view() >> justify;
if (justify == "left"){
justification = Block::Left;
} else if (justify == "center"){
justification = Block::Center;
} else if (justify == "right"){
justification = Block::Right;
}
} else if (*tok == "block"){
credits.push_back(Block(tok));
} else {
Global::debug( 3 ) <<"Unhandled Credit Sequence attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Credit Sequence parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
// Initial
reset();
for (std::vector<OptionCredits::Block>::const_iterator i = credits.begin(); i != credits.end(); ++i){
const OptionCredits::Block & block = *i;
creditLength += block.size(Menu::menuFontParameter.current()->get());
}
}
OptionCredits::Sequence::Sequence(const Sequence & copy):
type(copy.type),
x(copy.x),
y(copy.y),
startx(copy.startx),
endx(copy.endx),
starty(copy.starty),
endy(copy.endy),
ticks(copy.ticks),
duration(copy.duration),
speed(copy.speed),
alpha(copy.alpha),
alphaMultiplier(copy.alphaMultiplier),
justification(copy.justification),
credits(copy.credits),
current(copy.current),
done(false),
creditLength(copy.creditLength){
}
OptionCredits::Sequence::~Sequence(){
}
const OptionCredits::Sequence & OptionCredits::Sequence::operator=(const OptionCredits::Sequence & copy){
type = copy.type;
x = copy.x;
y = copy.y;
startx = copy.startx;
endx = copy.endx;
starty = copy.starty;
endy = copy.endy;
ticks = copy.ticks;
duration = copy.duration;
speed = copy.speed;
alpha = copy.alpha;
alphaMultiplier = copy.alphaMultiplier;
justification = copy.justification;
credits = copy.credits;
current = copy.current;
done = false;
creditLength = copy.creditLength;
return *this;
}
static int alphaClamp(int x, double multiplier){
int clamp = x * multiplier;
if (clamp < 0){
clamp = 0;
} else if (clamp > 255){
clamp = 255;
}
return clamp;
}
void OptionCredits::Sequence::act(){
if (!done && !credits.empty()){
if (type == Roll){
y += speed;
if (starty > endy){
if ((y + (creditLength * 1.1)) < endy){
done = true;
}
} else if (starty < endy){
if ((y * 1.1) > endy){
done = true;
}
}
} else if (type == Primary){
credits[current].act();
if (startx != endx){
x += speed;
if (startx > endx){
const double midpoint = (startx+endx)/2;
const int mid = x > midpoint ? startx -x : x - endx;
alpha = alphaClamp(mid, alphaMultiplier);
if (x < endx){
next();
}
} else if (startx < endx){
const double midpoint = (startx+endx)/2;
const int mid = x < midpoint ? x - startx : endx - x;
alpha = alphaClamp(mid, alphaMultiplier);
//Global::debug(0) << "alpha: " << alpha << " midpoint: " << midpoint << " mid: " << mid << std::endl;
if (x > endx){
next();
}
}
} else {
const double midpoint = duration/2;
const int mid = ticks < midpoint ? ticks : duration - ticks;
alpha = alphaClamp(mid, alphaMultiplier);
ticks++;
if (ticks >= duration){
ticks = 0;
next();
}
}
}
}
}
void OptionCredits::Sequence::draw(Graphics::Color title, Graphics::Color color, const Graphics::Bitmap & work){
if (!done && !credits.empty()){
if (type == Roll){
int rollY = (int) y;
for (std::vector<OptionCredits::Block>::const_iterator i = credits.begin(); i != credits.end(); ++i){
const OptionCredits::Block & block = *i;
rollY = block.print(x, rollY, title, color, Menu::menuFontParameter.current()->get(), work, justification);
}
} else if (type == Primary){
Graphics::Bitmap::transBlender(0, 0, 0, alpha);
credits[current].print(x, y, title, color, Menu::menuFontParameter.current()->get(), work.translucent(), justification);
}
}
}
void OptionCredits::Sequence::reset(){
done = false;
current = 0;
ticks = 0;
if (!credits.empty()){
if (type == Roll){
x = startx;
y = starty;
} else if (type == Primary){
x = startx;
y = starty - (credits[current].size(Menu::menuFontParameter.current()->get())/2);
}
}
}
void OptionCredits::Sequence::next(){
if (type == Primary){
if (current < credits.size()){
current++;
if (current == credits.size()){
done = true;
} else {
x = startx;
y = starty - (credits[current].size(Menu::menuFontParameter.current()->get())/2);
}
}
}
}
static std::string defaultPositions(){
const int width = Configuration::getScreenWidth();
const int height = Configuration::getScreenHeight();
std::ostringstream out;
out << "(start-x " << width/2.3 << ") (end-x " << width/1.8 << ") (start-y " << height/2 << ") ";
//out << "(start-x " << width/2 << ") (end-x " << width/2 << ") (start-y " << height/2 << ") (duration 250) ";
return out.str();
}
OptionCredits::OptionCredits(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token),
creditsContext(new Menu::Context()),
music(""),
color(Graphics::makeColor(255,255,255)),
title(Graphics::makeColor(0,255,255)),
clearColor(Graphics::makeColor(0,0,0)){
std::string defaultSequence = "(sequence (type primary) (speed 0.3) (alpha-multiplier 20) (justification center) " + defaultPositions();
/* Always */
if (jonBirthday()){
defaultSequence += "(block (title \"Happy birthday, Jon!\"))";
}
if (miguelBirthday()){
defaultSequence += "(block (title \"Happy birthday, Miguel!\"))";
}
if (Storage::instance().exists(Filesystem::RelativePath("sprites/paintown.png"))){
defaultSequence += "(block (animation (top) (width 350) (height 65) (image 0 \"sprites/paintown.png\") (frame (image 0) (time -1))) (credit \"Version " + Version::getVersionString() + "\"))";
} else {
defaultSequence += "(block (title \"R-Tech1\") (credit \"Version " + Version::getVersionString() + "\"))";
}
defaultSequence += "(block (title \"Programming\") (credit \"Jon Rafkind\") (credit \"Miguel Gavidia\"))";
defaultSequence += "(block (title \"Level design\") (credit \"Jon Rafkind\") (credit \"Miguel Gavidia\"))";
defaultSequence += "(block (title \"Contact\") (credit \"Website: http://paintown.org\") (credit \"Email: jon@rafkind.com\")))";
TokenReader reader;
Sequence sequence(reader.readTokenFromString(defaultSequence));
sequences.push_back(sequence);
//Global::debug(0) << defaultSequence << std::endl;
if ( *token != "credits" ){
throw LoadException(__FILE__, __LINE__, "Not a credit menu");
}
readName(token);
TokenView view = token->view();
// NOTE Use this to handle legacy additional blocks for the time being
Block legacyAdditional("");
bool additionalTitle = true;
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
if ( *tok == "music" ) {
/* Set music for credits */
tok->view() >> music;
} else if ( *tok == "background" ) {
/* Create an image and push it back on to vector */
std::string temp;
tok->view() >> temp;
creditsContext->addBackground(temp);
} else if ( *tok == "anim" || *tok == "animation" ){
creditsContext->addBackground(tok);
} else if ( *tok == "additional" ) {
std::string str;
TokenView additionalView = tok->view();
while (additionalView.hasMore()){
additionalView >> str;
if (additionalTitle){
legacyAdditional = Block(str);
additionalTitle = false;
} else {
legacyAdditional.addCredit(str);
}
}
} else if (*tok == "sequence"){
sequences.push_back(OptionCredits::Sequence(tok));
} else if ( *tok == "titlecolor" ) {
int r,b,g;
tok->view() >> r >> g >> b;
title = Graphics::makeColor( r, g, b );
} else if ( *tok == "color" ) {
int r,b,g;
tok->view() >> r >> g >> b;
color = Graphics::makeColor( r, g, b );
} else if ( *tok == "clear-color" ) {
int r,b,g;
tok->view() >> r >> g >> b;
clearColor = Graphics::makeColor( r, g, b );
} else {
Global::debug( 3 ) <<"Unhandled menu attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
if (!legacyAdditional.empty()){
//creditsRoll.push_back(legacyAdditional);
}
input.set(Keyboard::Key_ESC, 0, true, Exit);
input.set(Joystick::Button2, 0, true, Exit);
input.set(DeviceInput::Touch::Quit, Exit);
}
OptionCredits::~OptionCredits(){
}
void OptionCredits::logic(){
}
class CreditsLogicDraw : public Util::Logic, public Util::Draw {
public:
CreditsLogicDraw(std::vector<OptionCredits::Sequence> & sequences, Graphics::Color clearColor, Graphics::Color title, Graphics::Color color, const Font & font, InputMap<OptionCredits::CreditKey> & input, Menu::Context & context):
sequences(sequences),
clearColor(clearColor),
title(title),
color(color),
font(font),
input(input),
quit(false),
context(context),
current(0){
}
std::vector<OptionCredits::Sequence> & sequences;
Graphics::Color clearColor, title, color;
const Font & font;
InputMap<OptionCredits::CreditKey> & input;
bool quit;
Menu::Context & context;
unsigned int current;
void run(){
vector<InputMap<OptionCredits::CreditKey>::InputEvent> out = InputManager::getEvents(input, InputSource(true));
for (vector<InputMap<OptionCredits::CreditKey>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<OptionCredits::CreditKey>::InputEvent & event = *it;
if (event.enabled){
if (event.out == OptionCredits::Exit){
quit = true;
context.finish();
}
}
}
sequences[current].act();
if (sequences[current].isDone()){
sequences[current].reset();
current++;
if (current >= sequences.size()){
current = 0;
}
}
context.act();
}
bool done(){
return quit;
}
double ticks(double system){
return system * Global::ticksPerSecond(90);
}
void draw(const Graphics::Bitmap & buffer){
/* FIXME: hard coded resolution */
Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
work.fill(clearColor);
work.start();
//background.Blit(work);
context.render(Util::ReferenceCount<Menu::Renderer>(NULL), work);
sequences[current].draw(title, color, work);
work.finish();
// buffer.BlitToScreen();
}
};
void OptionCredits::run(const Menu::Context & context){
Menu::Context localContext(context, *creditsContext);
localContext.initialize();
if (!music.empty()){
if (Music::loadSong(Storage::instance().find(Filesystem::RelativePath(music)).path())){
Music::pause();
Music::play();
}
}
const Font & vFont = Menu::menuFontParameter.current()->get();
CreditsLogicDraw loop(sequences, clearColor, title, color, vFont, input, localContext);
Util::standardLoop(loop, loop);
InputManager::waitForRelease(input, InputSource(true), Exit);
throw Menu::Reload(__FILE__, __LINE__);
}
OptionDummy::OptionDummy(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token){
if ( *token != "dummy" ){
throw LoadException(__FILE__, __LINE__, "Not dummy option");
}
readName(token);
if (getText().empty()){
this->setText("Dummy");
}
setRunnable(false);
}
OptionDummy::OptionDummy(const Gui::ContextBox & parent, const std::string &name):
MenuOption(parent, 0){
if (name.empty()){
throw LoadException(__FILE__, __LINE__, "No name given to dummy");
}
this->setText(name);
setRunnable(false);
}
OptionDummy::~OptionDummy(){
}
void OptionDummy::logic(){
}
void OptionDummy::run(const Menu::Context & context){
}
OptionFullscreen::OptionFullscreen(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if ( *token != "fullscreen" )
throw LoadException(__FILE__, __LINE__, "Not fullscreen option");
readName(token);
}
OptionFullscreen::~OptionFullscreen()
{
// Nothing
}
std::string OptionFullscreen::getText() const {
ostringstream out;
out << MenuOption::getText() << ": " << (Configuration::getFullscreen() ? "Yes" : "No");
return out.str();
}
void OptionFullscreen::logic(){;
}
static void changeScreenMode(){
Configuration::setFullscreen(!Configuration::getFullscreen());
int gfx = (Configuration::getFullscreen() ? Global::FULLSCREEN : Global::WINDOWED);
Graphics::changeGraphicsMode(gfx, Graphics::Bitmap::getScreenWidth(), Graphics::Bitmap::getScreenHeight());
}
void OptionFullscreen::run(const Menu::Context & context){
changeScreenMode();
}
bool OptionFullscreen::leftKey(){
changeScreenMode();
return true;
}
bool OptionFullscreen::rightKey(){
changeScreenMode();
return true;
}
OptionFps::OptionFps(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
readName(token);
setRunnable(false);
}
void OptionFps::logic(){
}
void OptionFps::run(const Menu::Context & context){
}
std::string OptionFps::getText() const {
ostringstream out;
out << "Frames per second: " << Global::TICS_PER_SECOND;
return out.str();
}
bool OptionFps::leftKey(){
Global::setTicksPerSecond(Global::TICS_PER_SECOND - 1);
Configuration::setFps(Global::TICS_PER_SECOND);
return true;
}
bool OptionFps::rightKey(){
Global::setTicksPerSecond(Global::TICS_PER_SECOND + 1);
Configuration::setFps(Global::TICS_PER_SECOND);
return true;
}
OptionFps::~OptionFps(){
}
OptionQualityFilter::OptionQualityFilter(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
readName(token);
setRunnable(false);
}
std::string OptionQualityFilter::getText() const {
ostringstream out;
out << MenuOption::getText() << ": " << Configuration::getQualityFilter();
return out.str();
}
void OptionQualityFilter::logic(){
}
bool OptionQualityFilter::leftKey(){
string quality = Configuration::getQualityFilter();
if (quality == "none"){
quality = "hqx";
} else if (quality == "hqx"){
quality = "xbr";
} else if (quality == "xbr"){
quality = "none";
}
Configuration::setQualityFilter(quality);
return true;
}
bool OptionQualityFilter::rightKey(){
string quality = Configuration::getQualityFilter();
if (quality == "none"){
quality = "xbr";
} else if (quality == "hqx"){
quality = "none";
} else if (quality == "xbr"){
quality = "hqx";
}
Configuration::setQualityFilter(quality);
return true;
}
void OptionQualityFilter::run(const Menu::Context & context){
}
OptionQualityFilter::~OptionQualityFilter(){
}
#if 0
static OptionJoystick::JoystickType convertToKey(const std::string &k){
std::string temp = k;
for(unsigned int i=0;i<temp.length();i++){
temp[i] = tolower(temp[i]);
}
if (temp == "up") return OptionJoystick::Up;
if (temp == "down") return OptionJoystick::Down;
if (temp == "left") return OptionJoystick::Left;
/*
if (temp == "right") return OptionJoystick::Right;
if (temp == "jump") return OptionJoystick::Jump;
if (temp == "attack1") return OptionJoystick::Attack1;
if (temp == "attack2") return OptionJoystick::Attack2;
if (temp == "attack3") return OptionJoystick::Attack3;
if (temp == "attack4") return OptionJoystick::Attack4;
if (temp == "attack5") return OptionJoystick::Attack5;
if (temp == "attack6") return OptionJoystick::Attack6;
return OptionJoystick::Invalidkey;
}
static Configuration::JoystickInput getKey(int player, OptionJoystick::JoystickType k){
switch(k){
case OptionJoystick::Up:
return Joystick::Up;
case OptionJoystick::Down:
return Joystick::Down;
case OptionJoystick::Left:
return Joystick::Left;
case OptionJoystick::Right:
return Joystick::Right;
case OptionJoystick::Jump:
return Joystick::Button4;
case OptionJoystick::Attack1:
return Joystick::Button1;
case OptionJoystick::Attack2:
return Joystick::Button2;
case OptionJoystick::Attack3:
return Joystick::Button3;
case OptionJoystick::Attack4:
return Joystick::Button4;
case OptionJoystick::Attack5:
return Joystick::Button5;
case OptionJoystick::Attack6:
return Joystick::Button6;
default:
break;
}
return Joystick::Up;
}
static void setKey(int player, OptionJoystick::JoystickType k, Configuration::JoystickInput key){
/ *
switch(k){
case OptionJoystick::Up:
Configuration::setJoystickUp(player, key);
break;
case OptionJoystick::Down:
Configuration::setJoystickDown(player, key);
break;
case OptionJoystick::Left:
Configuration::setJoystickLeft(player, key);
break;
case OptionJoystick::Right:
Configuration::setJoystickRight(player, key);
break;
case OptionJoystick::Jump:
Configuration::setJoystickJump(player, key);
break;
case OptionJoystick::Attack1:
Configuration::setJoystickAttack1(player, key);
break;
case OptionJoystick::Attack2:
Configuration::setJoystickAttack2(player, key);
break;
case OptionJoystick::Attack3:
Configuration::setJoystickAttack3(player, key);
break;
case OptionJoystick::Attack4:
Configuration::setJoystickAttack4(player, key);
break;
case OptionJoystick::Attack5:
Configuration::setJoystickAttack5(player, key);
break;
case OptionJoystick::Attack6:
Configuration::setJoystickAttack6(player, key);
break;
default:
break;
}
*/
}
static Configuration::JoystickInput readJoystick(){
vector<Joystick::Key> keys;
keys.push_back(Joystick::Up);
keys.push_back(Joystick::Down);
keys.push_back(Joystick::Left);
keys.push_back(Joystick::Right);
keys.push_back(Joystick::Button1);
keys.push_back(Joystick::Button2);
keys.push_back(Joystick::Button3);
keys.push_back(Joystick::Button4);
keys.push_back(Joystick::Button5);
keys.push_back(Joystick::Button6);
InputMap<Joystick::Key> input;
for (vector<Joystick::Key>::iterator it = keys.begin(); it != keys.end(); it++){
input.set(*it, 0, true, *it);
}
input.set(Keyboard::Key_ESC, 0, true, Joystick::Invalid);
while (true){
InputManager::poll();
vector<InputMap<Joystick::Key>::InputEvent> out = InputManager::getEvents(input, InputSource());
for (vector<InputMap<Joystick::Key>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<Joystick::Key>::InputEvent & event = *it;
if (event.enabled){
Global::debug(1) << "Press: " << event.out << std::endl;
if (event.out == Joystick::Invalid){
InputManager::waitForRelease(input, InputSource(), Joystick::Invalid);
throw Exception::Return(__FILE__, __LINE__);
}
return event.out;
}
}
Util::rest(1);
}
/* control probably shouldn't get here.. */
return Joystick::Up;
}
OptionJoystick::OptionJoystick(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
player(-1),
type(Invalidkey),
keyCode(0){
if (*token != "joystick"){
throw LoadException(__FILE__, __LINE__, "Not joystick option");
}
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
if ( *tok == "name" ){
tok->view() >> name;
} else if ( *tok == "player" ) {
tok->view() >> player;
} else if ( *tok == "type" ) {
std::string temp;
tok->view() >> temp;
type = convertToKey(temp);
} else {
Global::debug( 3 ) <<"Unhandled menu attribute: "<<endl;
tok->print(" ");
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
// delete current;
throw ex;
}
}
if (name.empty()){
throw LoadException(__FILE__, __LINE__, "No name set, this option should have a name!");
}
if (type == Invalidkey){
throw LoadException(__FILE__, __LINE__, "Invalid joystick button, should be up, down, left, right, up, down, jump, attack1-6!");
}
if (player == -1){
throw LoadException(__FILE__, __LINE__, "Player not specified in joystick configuration");
}
ostringstream out;
out << name << ": " << Joystick::keyToName(getKey(player, type));
setText(out.str());
}
OptionJoystick::~OptionJoystick(){
// Nothing
}
void OptionJoystick::logic(){
/*
char temp[255];
sprintf( temp, "%s: %s", name.c_str(), Joystick::keyToName(getKey(player,type)));
setText(std::string(temp));
*/
}
void OptionJoystick::run(const Menu::Context & context){
/*
//int x, y, width, height;
const Font & vFont = Menu::menuFontParameter.current()->get();
const char * message = "Press a joystick button!";
const int width = vFont.textLength(message) + 10;
const int height = vFont.getHeight() + 10;
// const int x = (getParent()->getWork()->getWidth()/2) - (width/2);
// const int y = (getParent()->getWork()->getHeight()/2) - (height/2);
const int x = Menu::Menu::Width / 2 - width/2;
const int y = Menu::Menu::Height / 2 - height/2;
Box dialog;
dialog.location.setPosition(Gui::AbsolutePoint(0,0));
dialog.location.setDimensions(vFont.textLength(message) + 10, vFont.getHeight() + 10);
dialog.transforms.setRadius(0);
dialog.colors.body = Graphics::makeColor(0,0,0);
dialog.colors.bodyAlpha = 200;
dialog.colors.border = Graphics::makeColor(255,255,255);
dialog.colors.borderAlpha = 255;
Graphics::Bitmap temp = Graphics::Bitmap::temporaryBitmap(width,height);
dialog.render(temp, vFont);
vFont.printf( 5, 5, Graphics::makeColor(255,255,255), temp, message, -1);
temp.BlitToScreen(x,y);
setKey(player, type, readJoystick());
ostringstream out;
out << name << ": " << Joystick::keyToName(getKey(player, type));
setText(out.str());
*/
Graphics::Bitmap temp(Menu::Menu::Width, Menu::Menu::Height);
// Menu::Context tempContext = context;
Menu::Context tempContext(context);
tempContext.initialize();
Menu::InfoBox keyDialog;
// keyDialog.setFont(tempContext.getFont());
//keyDialog.location.set(-1,-1,1,1);
const int width = temp.getWidth();
const int height = temp.getHeight();
const Font & font = Menu::menuFontParameter.current()->get();
const int radius = 15;
keyDialog.setText("Press a joystick button!");
keyDialog.initialize(font);
keyDialog.location.setDimensions(font.textLength("Press a joystick button!") + radius, font.getHeight() + radius);
keyDialog.location.setCenterPosition(Gui::RelativePoint(0, 0));
// keyDialog.location.setPosition(Gui::AbsolutePoint((width/2)-(keyDialog.location.getWidth()/2), (height/2)-(keyDialog.location.getHeight()/2)));
// keyDialog.location.setPosition2(Gui::AbsolutePoint((
keyDialog.transforms.setRadius(radius);
keyDialog.colors.body = Graphics::makeColor(0,0,0);
keyDialog.colors.bodyAlpha = 180;
keyDialog.colors.border = Graphics::makeColor(255,255,255);
keyDialog.colors.borderAlpha = 255;
keyDialog.open();
InputManager::waitForClear();
while (!InputManager::anyInput() && keyDialog.isActive()){
InputManager::poll();
keyDialog.act(font);
/*
if (keyDialog.isActive()){
InputManager::poll();
}
*/
tempContext.act();
tempContext.render(0, temp);
keyDialog.render(temp, font);
temp.BlitToScreen();
}
tempContext.finish();
setKey(player, type, readJoystick());
InputManager::waitForClear();
ostringstream out;
out << name << ": " << Joystick::keyToName(getKey(player, type));
setText(out.str());
/*
Keyboard key;
keyCode = readKey(key);
setKey(player,type, keyCode);
*/
}
#endif
static OptionKey::keyType convertToKeyboardKey(const std::string &k){
std::string temp = k;
for(unsigned int i=0;i<temp.length();i++){
temp[i] = tolower(temp[i]);
}
if (temp == "up") return OptionKey::up;
if (temp == "down") return OptionKey::down;
if (temp == "left") return OptionKey::left;
if (temp == "right") return OptionKey::right;
if (temp == "jump") return OptionKey::jump;
if (temp == "attack1") return OptionKey::attack1;
if (temp == "attack2") return OptionKey::attack2;
if (temp == "attack3") return OptionKey::attack3;
if (temp == "attack4") return OptionKey::attack4;
if (temp == "attack5") return OptionKey::attack5;
if (temp == "attack6") return OptionKey::attack6;
return OptionKey::invalidkey;
}
static int getKey(int player, OptionKey::keyType k){
switch(k){
case OptionKey::up:
return Configuration::getUp(player);
break;
case OptionKey::down:
return Configuration::getDown(player);
break;
case OptionKey::left:
return Configuration::getLeft(player);
break;
case OptionKey::right:
return Configuration::getRight(player);
break;
case OptionKey::jump:
return Configuration::getJump(player);
break;
case OptionKey::attack1:
return Configuration::getAttack1(player);
break;
case OptionKey::attack2:
return Configuration::getAttack2(player);
break;
case OptionKey::attack3:
return Configuration::getAttack3(player);
break;
case OptionKey::attack4:
return Configuration::getAttack4(player);
case OptionKey::attack5:
return Configuration::getAttack5(player);
case OptionKey::attack6:
return Configuration::getAttack6(player);
default:
break;
}
return 0;
}
static void setKey(int player, OptionKey::keyType k, int key){
switch(k){
case OptionKey::up:
Configuration::setUp(player, key);
break;
case OptionKey::down:
Configuration::setDown(player, key);
break;
case OptionKey::left:
Configuration::setLeft(player, key);
break;
case OptionKey::right:
Configuration::setRight(player, key);
break;
case OptionKey::jump:
Configuration::setJump(player, key);
break;
case OptionKey::attack1:
Configuration::setAttack1(player, key);
break;
case OptionKey::attack2:
Configuration::setAttack2(player, key);
break;
case OptionKey::attack3:
Configuration::setAttack3(player, key);
break;
case OptionKey::attack4:
Configuration::setAttack4(player, key);
break;
case OptionKey::attack5:
Configuration::setAttack5(player, key);
break;
case OptionKey::attack6:
Configuration::setAttack6(player, key);
break;
default:
break;
}
}
/*
static int readKey( Keyboard & key ){
int k = key.readKey();
key.wait();
return k;
}
*/
OptionKey::OptionKey(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
player(-1),
type(invalidkey),
keyCode(0){
if ( *token != "key" )
throw LoadException(__FILE__, __LINE__, "Not key option");
TokenView view = token->view();
while (view.hasMore()) {
try {
const Token * tok;
view >> tok;
if ( *tok == "name" ) {
tok->view() >> name;
} else if ( *tok == "player" ) {
tok->view() >> player;
} else if ( *tok == "type" ) {
std::string temp;
tok->view() >> temp;
type = convertToKeyboardKey(temp);
} else {
Global::debug( 3 ) <<"Unhandled menu attribute: "<<endl;
tok->print(" ");
}
} catch ( const TokenException & ex ){
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
// delete current;
throw ex;
}
}
if(name.empty())throw LoadException(__FILE__, __LINE__, "No name set, this option should have a name!");
if(type == invalidkey)throw LoadException(__FILE__, __LINE__, "Invalid key, should be up, down, left, right, up, down, jump, attack1-6!");
if(player == -1)throw LoadException(__FILE__, __LINE__, "Player not specified in key configuration");
char temp[255];
sprintf( temp, "%s: %s", name.c_str(), Keyboard::keyToName(getKey(player,type)));
setText(std::string(temp));
}
OptionKey::~OptionKey(){
// Nothing
}
void OptionKey::logic(){
char temp[255];
sprintf( temp, "%s: %s", name.c_str(), Keyboard::keyToName(getKey(player,type)));
setText(std::string(temp));
}
void OptionKey::run(const Menu::Context & context){
// Do dialog
//Box::messageDialog(Menu::Menu::Width, Menu::Menu::Height, "Press a Key!",2);
class WaitForKey: public Util::Logic, public Util::Draw {
public:
WaitForKey(const Menu::Context & context):
font(Menu::menuFontParameter.current()->get()),
tempContext(context),
speed(30),
listener(*this),
quit(false),
out(Keyboard::Key_ESC),
startingTime(System::currentMilliseconds()),
maxTime(5000){
tempContext.initialize();
// keyDialog.setFont(tempContext.getFont());
//keyDialog.location.set(-1,-1,1,1);
const int width = 320;
const int height = 240;
const int radius = 15;
keyDialog.setText("Press a Key!");
keyDialog.initialize(font);
keyDialog.location.setDimensions(font.textLength("Press a Key!") + radius, font.getHeight() + radius);
keyDialog.location.setCenterPosition(Gui::RelativePoint(0, 0));
// keyDialog.location.setPosition(Gui::AbsolutePoint((width/2)-(keyDialog.location.getWidth()/2), (height/2)-(keyDialog.location.getHeight()/2)));
// keyDialog.location.setPosition2(Gui::AbsolutePoint((
keyDialog.transforms.setRadius(radius);
keyDialog.colors.body = Graphics::makeColor(0,0,0);
keyDialog.colors.bodyAlpha = 180;
keyDialog.colors.border = Graphics::makeColor(255,255,255);
keyDialog.colors.borderAlpha = 255;
keyDialog.open();
Keyboard::pushRepeatState(false);
Keyboard::addListener(&listener);
vector<Graphics::BlendPoint> points;
points.push_back(Graphics::BlendPoint(Graphics::makeColor(255, 0, 0), 30));
points.push_back(Graphics::BlendPoint(Graphics::makeColor(0, 255, 0), 25));
points.push_back(Graphics::BlendPoint(Graphics::makeColor(255, 255, 255), 0));
colors = Graphics::blend_palette(points);
}
virtual ~WaitForKey(){
Keyboard::removeListener(&listener);
Keyboard::popRepeatState();
}
class Listener: public KeyboardListener {
public:
Listener(WaitForKey & parent):
parent(parent){
}
WaitForKey & parent;
virtual void press(Keyboard * keyboard, Keyboard::KeyType key, Keyboard::unicode_t code){
parent.press(key);
}
virtual void release(Keyboard * keyboard, Keyboard::KeyType key){
parent.release(key);
}
};
const Font & font;
Menu::Context tempContext;
Menu::InfoBox keyDialog;
const int speed;
Listener listener;
bool quit;
Keyboard::KeyType out;
/* keep track of how much time we are in this loop */
uint64_t startingTime;
/* maximum number of milliseconds to wait */
const uint64_t maxTime;
vector<Graphics::Color> colors;
virtual void press(Keyboard::KeyType key){
quit = true;
if (key != Keyboard::Key_ESC){
this->out = key;
}
}
virtual void release(Keyboard::KeyType key){
}
virtual void run(){
for (int i = 0; i < 90 / speed; i++){
tempContext.act();
keyDialog.act(font);
}
/* exit after 5 seconds */
quit = quit || (System::currentMilliseconds() - startingTime > maxTime);
}
virtual bool done(){
return quit;
}
virtual double ticks(double system){
return system * Global::ticksPerSecond(speed);
}
virtual void showTimeLeft(const Graphics::Bitmap & screen){
int x1 = 1;
int y1 = 1;
int x2 = screen.getWidth() * (1 - (double) (System::currentMilliseconds() - startingTime) / (double) maxTime);
if (x2 < x1){
x2 = x1;
}
int y2 = 10;
int color = (1.0 - (double) (System::currentMilliseconds() - startingTime) / maxTime) * colors.size();
if (color < 0){
color = 0;
}
- if (color >= colors.size()){
+ if (color >= (int) colors.size()){
color = colors.size() - 1;
}
screen.rectangleFill(x1, y1, x2, y2, colors[color]);
}
virtual void draw(const Graphics::Bitmap & screen){
Graphics::StretchedBitmap work(640, 480, screen);
work.start();
tempContext.render(Util::ReferenceCount<Menu::Renderer>(NULL), work);
keyDialog.render(work, font);
showTimeLeft(work);
work.finish();
}
};
WaitForKey run(context);
Util::standardLoop(run, run);
Keyboard::KeyType key = run.out;
if (key != Keyboard::Key_ESC){
setKey(player, type, key);
}
/*
Keyboard key;
key.wait();
*/
/*
Graphics::Bitmap temp(Menu::Menu::Width, Menu::Menu::Height);
// Menu::Context tempContext = context;
Menu::Context tempContext(context);
tempContext.initialize();
Menu::InfoBox keyDialog;
// keyDialog.setFont(tempContext.getFont());
//keyDialog.location.set(-1,-1,1,1);
const int width = temp.getWidth();
const int height = temp.getHeight();
const Font & font = Menu::menuFontParameter.current()->get();
const int radius = 15;
keyDialog.setText("Press a Key!");
keyDialog.initialize(font);
keyDialog.location.setDimensions(font.textLength("Press a Key!") + radius, font.getHeight() + radius);
keyDialog.location.setCenterPosition(Gui::RelativePoint(0, 0));
// keyDialog.location.setPosition(Gui::AbsolutePoint((width/2)-(keyDialog.location.getWidth()/2), (height/2)-(keyDialog.location.getHeight()/2)));
// keyDialog.location.setPosition2(Gui::AbsolutePoint((
keyDialog.transforms.setRadius(radius);
keyDialog.colors.body = Graphics::makeColor(0,0,0);
keyDialog.colors.bodyAlpha = 180;
keyDialog.colors.border = Graphics::makeColor(255,255,255);
keyDialog.colors.borderAlpha = 255;
keyDialog.open();
InputManager::waitForClear();
while (!InputManager::anyInput() && keyDialog.isActive()){
InputManager::poll();
keyDialog.act(font);
/ *
if (keyDialog.isActive()){
InputManager::poll();
}
* /
tempContext.act();
tempContext.render(Util::ReferenceCount<Menu::Renderer>(NULL), temp);
keyDialog.render(temp, font);
temp.BlitToScreen();
}
tempContext.finish();
keyCode = InputManager::readKey();
setKey(player,type, keyCode);
InputManager::waitForClear();
*/
}
OptionLevel::OptionLevel(const Gui::ContextBox & parent, const Token *token, int * set, int value):
MenuOption(parent, token),
set(set),
value(value){
// Nothing
}
OptionLevel::~OptionLevel(){
}
void OptionLevel::logic(){
}
void OptionLevel::run(const Menu::Context & context){
*set = value;
throw Menu::MenuException(__FILE__, __LINE__);
}
OptionMenu::OptionMenu(const Gui::ContextBox & parent, const Token *token, const Menu::OptionFactory & factory):
MenuOption(parent, token),
menu(0){
if (*token != "menu"){
throw LoadException(__FILE__, __LINE__, "Not a menu");
}
if (token->numTokens() == 1){
std::string temp;
token->view() >> temp;
menu = new Menu::Menu(Storage::instance().find(Filesystem::RelativePath(temp)), factory);
} else {
menu = new Menu::Menu(token, factory);
}
this->setText(menu->getName());
this->setInfoText(menu->getInfo());
// Lets check if this menu is going bye bye
//if ( menu->checkRemoval() ) setForRemoval(true);
}
OptionMenu::~OptionMenu(){
// Delete our menu
if (menu){
delete menu;
}
}
void OptionMenu::logic(){
// Nothing
}
void OptionMenu::run(const Menu::Context & context){
// Do our new menu
try{
menu->run(context);
} catch (const Exception::Return ignore){
throw Menu::Reload(__FILE__, __LINE__);
}
}
OptionReturn::OptionReturn(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
if (*token != "return"){
throw LoadException(__FILE__, __LINE__, "Not a return option");
}
readName(token);
}
void OptionReturn::logic(){
}
/* maybe this option is misnamed, but its supposed to quit the current game
* and go back to the main menu
*/
void OptionReturn::run(const Menu::Context & context){
throw Exception::Quit(__FILE__, __LINE__);
}
OptionReturn::~OptionReturn(){
}
OptionContinue::OptionContinue(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
if (*token != "continue"){
throw LoadException(__FILE__, __LINE__, "Not a continue option");
}
readName(token);
}
void OptionContinue::logic(){
}
void OptionContinue::run(const Menu::Context & context){
throw Exception::Return(__FILE__, __LINE__);
}
OptionContinue::~OptionContinue(){
}
OptionQuit::OptionQuit(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token){
if ( *token != "quit" ){
throw LoadException(__FILE__, __LINE__, "Not quit option");
}
readName(token);
}
OptionQuit::OptionQuit(const Gui::ContextBox & parent, const std::string &name):
MenuOption(parent, 0){
if (name.empty()){
throw LoadException(__FILE__, __LINE__, "No name given to quit");
}
this->setText(name);
}
OptionQuit::~OptionQuit(){
}
void OptionQuit::logic(){
}
void OptionQuit::run(const Menu::Context & context){
throw ShutdownException();
}
#if defined(WINDOWS) && defined(doesnt_work_yet)
#include <windows.h>
#include <stdio.h>
/* contributed by Roy Underthump from allegro.cc */
static vector<ScreenSize> getScreenResolutions(){
HWND hwnd;
HDC hdc;
// int iPixelFormat;
int descerr;
int retval;
DEVMODE d;
PIXELFORMATDESCRIPTOR pfd;
hwnd = GetDesktopWindow();
hdc = GetDC(hwnd);
vector<ScreenSize> modes;
for (int i = 0;; i++){
retval = EnumDisplaySettings(0,i,&d);
if (!retval){
break;
}
descerr = DescribePixelFormat(hdc, i+1, sizeof(pfd), &pfd);
if(!descerr){
continue;
}
/*
printf("\n#%d bpp %d width %d height %d colorbits %d fps %d",i,d.dmBitsPerPel,
d.dmPelsWidth, d.dmPelsHeight,pfd.cColorBits,d.dmDisplayFrequency);
if(pfd.dwFlags & PFD_SUPPORT_OPENGL)printf(" OGL OK");
*/
modes.push_back(ScreenSize(d.dmPelsWidth, d.dmPelsHeight));
}
if (modes.empty()){
modes.push_back(ScreenSize(640,480));
}
return modes;
}
#else
static vector<ScreenSize> getScreenResolutions(){
vector<ScreenSize> modes;
modes.push_back(ScreenSize(320, 240));
modes.push_back(ScreenSize(640, 480));
modes.push_back(ScreenSize(800, 600));
modes.push_back(ScreenSize(960, 720));
modes.push_back(ScreenSize(1024, 768));
modes.push_back(ScreenSize(1280, 960));
modes.push_back(ScreenSize(1600, 1200));
return modes;
}
#endif
static bool doSort(const ScreenSize & a, const ScreenSize & b){
return (a.w * a.h) < (b.w * b.h);
}
static vector<ScreenSize> sortResolutions(const vector<ScreenSize> & modes){
vector<ScreenSize> copy(modes);
std::sort(copy.begin(), copy.end(), doSort);
return copy;
}
OptionScreenSize::OptionScreenSize(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
Global::debug(1) << "Get screen resolution" << endl;
modes = sortResolutions(getScreenResolutions());
if (Global::getDebug() >= 1){
for (vector<ScreenSize>::iterator it = modes.begin(); it != modes.end(); it++){
Global::debug(1) << "Screen size: " << it->w << " x " << it->h << endl;
}
}
if ( *token != "screen-size" ){
throw LoadException(__FILE__, __LINE__, "Not a screen-size");
}
readName(token);
}
OptionScreenSize::~OptionScreenSize(){
// Nothing
}
void OptionScreenSize::logic(){
ostringstream temp;
temp << "Screen size: " << Configuration::getScreenWidth() << " x " << Configuration::getScreenHeight();
setText(temp.str());
}
void OptionScreenSize::run(const Menu::Context & context){
}
void OptionScreenSize::setMode(int width, int height){
if (width != Configuration::getScreenWidth() ||
height != Configuration::getScreenHeight()){
Global::debug(1) << "Changing mode to " << width << " x " << height << endl;
int gfx = Configuration::getFullscreen() ? Global::FULLSCREEN : Global::WINDOWED;
int ok = Graphics::changeGraphicsMode(gfx, width, height);
if (ok == 0){
Global::debug(1) << "Success" << endl;
Configuration::setScreenWidth(width);
Configuration::setScreenHeight(height);
} else {
Global::debug(1) << "Fail" << endl;
int ok = Graphics::changeGraphicsMode(gfx, Configuration::getScreenWidth(), Configuration::getScreenHeight());
Global::debug(1) << "Set mode back " << ok << endl;
}
}
}
/*
static int modes[][2] = {{640,480}, {800,600}, {1024,768}, {1280,1024}, {1600,1200}};
// static int max_modes = sizeof(modes) / sizeof(int[]);
static int max_modes = 5;
*/
int OptionScreenSize::findMode(int width, int height){
for (int mode = 0; mode < (int) modes.size(); mode++){
if (modes[mode].w == width && modes[mode].h == height){
return mode;
}
}
return -1;
}
bool OptionScreenSize::leftKey(){
int mode = findMode(Configuration::getScreenWidth(), Configuration::getScreenHeight());
if (mode >= 1 && mode < (int)modes.size()){
mode -= 1;
} else {
mode = 0;
}
setMode(modes[mode].w, modes[mode].h);
lblue = lgreen = 0;
return true;
}
bool OptionScreenSize::rightKey(){
int mode = findMode(Configuration::getScreenWidth(), Configuration::getScreenHeight());
if (mode >= 0 && mode < (int)modes.size() - 1){
mode += 1;
} else {
mode = 0;
}
setMode(modes[mode].w, modes[mode].h);
rblue = rgreen = 0;
return true;
}
static string joinPaths(const vector<Filesystem::AbsolutePath> & strings, const string & middle){
ostringstream out;
for (vector<Filesystem::AbsolutePath>::const_iterator it = strings.begin(); it != strings.end(); it++){
out << (*it).path() << middle;
}
return out.str();
}
static bool sortInfo(const Util::ReferenceCount<Menu::FontInfo> & info1,
const Util::ReferenceCount<Menu::FontInfo> & info2){
string name1 = Util::lowerCaseAll(info1->getName());
string name2 = Util::lowerCaseAll(info2->getName());
return name1 < name2;
}
static bool isWindows(){
#ifdef WINDOWS
return true;
#else
return false;
#endif
}
static bool isOSX(){
#ifdef MACOSX
return true;
#else
return false;
#endif
}
template <class X>
static vector<X> operator+(const vector<X> & v1, const vector<X> & v2){
vector<X> out;
for (typename vector<X>::const_iterator it = v1.begin(); it != v1.end(); it++){
out.push_back(*it);
}
for (typename vector<X>::const_iterator it = v2.begin(); it != v2.end(); it++){
out.push_back(*it);
}
return out;
}
static vector<Filesystem::AbsolutePath> findSystemFonts(){
if (isWindows()){
const char * windows = getenv("windir");
if (windows != NULL){
return Storage::instance().getFilesRecursive(Filesystem::AbsolutePath(string(windows) + "/fonts"), "*.ttf");
}
return vector<Filesystem::AbsolutePath>();
} else if (isOSX()){
return Storage::instance().getFilesRecursive(Filesystem::AbsolutePath("/Library/Fonts"), "*.ttf");
} else {
/* assume unix/linux conventions */
return Storage::instance().getFilesRecursive(Filesystem::AbsolutePath("/usr/share/fonts/truetype"), "*.ttf") +
Storage::instance().getFilesRecursive(Filesystem::AbsolutePath("/usr/local/share/fonts/truetype"), "*.ttf");
}
}
static vector<Util::ReferenceCount<Menu::FontInfo> > findFonts(){
vector<Util::ReferenceCount<Menu::FontInfo> > fonts;
try{
Filesystem::AbsolutePath fontsDirectory = Storage::instance().find(Filesystem::RelativePath("fonts"));
Global::debug(1, "fonts") << "Font directory " << fontsDirectory.path() << endl;
vector<Filesystem::AbsolutePath> ttfFonts = Storage::instance().getFiles(fontsDirectory, "*.ttf");
Global::debug(1, "fonts") << "Found ttf fonts " << joinPaths(ttfFonts, ", ") << endl;
vector<Filesystem::AbsolutePath> otfFonts = Storage::instance().getFiles(fontsDirectory, "*.otf");
Global::debug(1, "fonts") << "Found otf fonts " << joinPaths(otfFonts, ", ") << endl;
for (vector<Filesystem::AbsolutePath>::iterator it = ttfFonts.begin(); it != ttfFonts.end(); it++){
fonts.push_back(Util::ReferenceCount<Menu::FontInfo>(new Menu::RelativeFontInfo(Storage::instance().cleanse(*it), Configuration::getMenuFontWidth(), Configuration::getMenuFontHeight())));
}
for (vector<Filesystem::AbsolutePath>::iterator it = otfFonts.begin(); it != otfFonts.end(); it++){
fonts.push_back(Util::ReferenceCount<Menu::FontInfo>(new Menu::RelativeFontInfo(Storage::instance().cleanse(*it), Configuration::getMenuFontWidth(), Configuration::getMenuFontHeight())));
}
/* linux specific fonts */
vector<Filesystem::AbsolutePath> systemFonts = findSystemFonts();
for (vector<Filesystem::AbsolutePath>::iterator it = systemFonts.begin(); it != systemFonts.end(); it++){
Global::debug(1) << "Adding system font `" << (*it).path() << "'" << endl;
fonts.push_back(Util::ReferenceCount<Menu::FontInfo>(new Menu::AbsoluteFontInfo(*it, Configuration::getMenuFontWidth(), Configuration::getMenuFontHeight())));
}
sort(fonts.begin(), fonts.end(), sortInfo);
// DEFAULT (blank)
// fonts.insert(fonts.begin(), new Menu::DefaultFontInfo());
fonts.insert(fonts.begin(), Util::ReferenceCount<Menu::FontInfo>(NULL));
} catch (const Filesystem::NotFound & e){
throw LoadException(__FILE__, __LINE__, e, "Could not load font");
}
return fonts;
}
OptionSelectFont::OptionSelectFont(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
typeAdjust(fontName),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if ( *token != "font-select" ){
throw LoadException(__FILE__, __LINE__, "Not a font selector");
}
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
if ( *tok == "adjust" ){
std::string temp;
tok->view() >> temp;
if ( temp == "name" ) typeAdjust = fontName;
else if ( temp == "width" ) typeAdjust = fontWidth;
else if ( temp == "height" ) typeAdjust = fontHeight;
else throw LoadException(__FILE__, __LINE__, "Incorrect value \"" + temp + "\" in font-select");
} else {
Global::debug(3) << "Unhandled menu attribute: " << endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
// delete current;
throw ex;
}
}
}
void OptionSelectFont::open(){
// Find and set fonts now
if (typeAdjust == fontName){
fonts = findFonts();
}
}
void OptionSelectFont::close(){
if (typeAdjust == fontName){
/* the user probably loaded a bunch of different fonts that will
* never be used again, so clear the font cache
* TODO: dont clear the currently selected font
*/
FontFactory::clear();
}
}
OptionSelectFont::~OptionSelectFont(){
// Nothing
}
void OptionSelectFont::logic(){
/* FIXME Get current font and display info */
switch (typeAdjust){
case fontName:{
std::string name;
if (Configuration::hasMenuFont()){
name = Configuration::getMenuFont()->getName();
} else {
name = "Default";
}
setText("Current Font: " + name);
break;
}
case fontWidth:{
ostringstream temp;
temp << "Font Width: " << Configuration::getMenuFontWidth();
setText(temp.str());
break;
}
case fontHeight:{
ostringstream temp;
temp << "Font Height: " << Configuration::getMenuFontHeight();
setText(temp.str());
break;
}
default: break;
}
if (lblue < 255){
lblue += 5;
}
if (rblue < 255){
rblue += 5;
}
if (lgreen < 255){
lgreen += 5;
}
if (rgreen < 255){
rgreen += 5;
}
}
void OptionSelectFont::run(const Menu::Context & context){
// throw Menu::MenuException(__FILE__, __LINE__);
/* throw something to quit back to the previous menu */
}
bool OptionSelectFont::leftKey(){
switch (typeAdjust){
case fontName:
nextIndex(false);
break;
case fontWidth:
Configuration::setMenuFontWidth(Configuration::getMenuFontWidth() - 1);
break;
case fontHeight:
Configuration::setMenuFontHeight(Configuration::getMenuFontHeight() - 1);
break;
default:
break;
}
lblue = lgreen = 0;
return true;
}
bool OptionSelectFont::rightKey(){
switch (typeAdjust){
case fontName:
nextIndex(true);
break;
case fontWidth:
Configuration::setMenuFontWidth(Configuration::getMenuFontWidth() + 1);
break;
case fontHeight:
Configuration::setMenuFontHeight(Configuration::getMenuFontHeight() + 1);
break;
default:
break;
}
rblue = rgreen = 0;
return true;
}
static bool saneFont(const Util::ReferenceCount<Menu::FontInfo> & info){
class Context: public Loader::LoadingContext {
public:
Context(const Util::ReferenceCount<Menu::FontInfo> & info):
info(info),
isok(false){
}
bool ok(){
try{
const Font & font = info->get();
return font.textLength("A") != 0 &&
font.getHeight() != 0;
} catch (const Exception::Base & ignore){
return true;
}
}
virtual void load(){
isok = ok();
}
const Util::ReferenceCount<Menu::FontInfo> & info;
bool isok;
};
if (info == NULL){
return true;
}
Context context(info);
/* an empty Info object, we don't really care about it */
Loader::Info level("Loading Font", Filesystem::AbsolutePath());
Loader::loadScreen(context, level, Loader::SimpleCircle);
return context.isok;
}
void OptionSelectFont::nextIndex(bool forward){
if (fonts.size() == 0){
return;
}
int index = 0;
for (unsigned int i = 0 ; i < fonts.size() ; ++i){
if ((Configuration::getMenuFont() == NULL && fonts[i] == NULL) ||
((Configuration::getMenuFont() != NULL && fonts[i] != NULL) &&
(*Configuration::getMenuFont() == *fonts[i]))){
index = i;
}
}
if (forward){
index++;
if (index >= (int) fonts.size()){
index = 0;
}
} else {
index--;
if (index < 0){
index = (int)fonts.size()-1;
}
}
while (!saneFont(fonts[index])){
Global::debug(0) << "Warning: erasing font `" << fonts[index]->getName() << "'" << endl;
int where = 0;
vector<Util::ReferenceCount<Menu::FontInfo> >::iterator it;
for (it = fonts.begin(); it != fonts.end() && where != index; it++, where++){
}
fonts.erase(it);
if (index >= (int) fonts.size()){
index = fonts.size() - 1;
}
}
Configuration::setMenuFont(fonts[index]);
/* FIXME */
/*
if (fonts[index] == "Default"){
Configuration::setMenuFont("");
} else {
Configuration::setMenuFont(fonts[index]);
}
*/
}
OptionSpeed::OptionSpeed(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if ( *token != "speed" )
throw LoadException(__FILE__, __LINE__, "Not speed option");
readName(token);
}
OptionSpeed::~OptionSpeed(){
// Nothing
}
std::string OptionSpeed::getText() const {
ostringstream out;
out << MenuOption::getText() << ": " << Configuration::getGameSpeed();
return out.str();
}
void OptionSpeed::logic(){
/*
//ostringstream temp;
char temp[255];
sprintf( temp, "%s: %0.2f", name.c_str(), MenuGlobals::getGameSpeed() );
setText(std::string(temp));
*/
}
void OptionSpeed::run(const Menu::Context & context){
}
bool OptionSpeed::leftKey(){
Configuration::setGameSpeed(Configuration::getGameSpeed() - 0.05);
if (Configuration::getGameSpeed() < 0.1){
Configuration::setGameSpeed(0.1);
}
return false;
}
bool OptionSpeed::rightKey(){
Configuration::setGameSpeed(Configuration::getGameSpeed() + 0.05);
rblue = rgreen = 0;
return false;
}
OptionTabMenu::OptionTabMenu(const Gui::ContextBox & parent, const Token *token, const Menu::OptionFactory & factory):
MenuOption(parent, token),
menu(0){
if (token->numTokens() == 1){
std::string temp;
token->view() >> temp;
menu = new Menu::Menu(Storage::instance().find(Filesystem::RelativePath(temp)), factory, Menu::Renderer::Tabbed);
} else {
menu = new Menu::Menu(token, factory, Menu::Renderer::Tabbed);
}
// this->setText(menu->getName());
// token->print("Menu: ");
const Token * tok = token->findToken("_/name");
if (tok != NULL){
std::string name;
tok->view() >> name;
// Global::debug(0, "menu") << "Menu name: " << name << endl;
this->setText(name);
} else {
// No name?
throw LoadException(__FILE__, __LINE__, "Menu has no name");
}
}
OptionTabMenu::~OptionTabMenu(){
// Delete our menu
if (menu){
delete menu;
}
}
void OptionTabMenu::logic(){
// Nothing
}
void OptionTabMenu::run(const Menu::Context & context){
// Do our new menu
// menu->run(context);
try{
menu->run(context);
} catch (const Exception::Return ignore){
throw Menu::Reload(__FILE__, __LINE__);
}
}
OptionSound::OptionSound(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if (*token != "sound" ){
throw LoadException(__FILE__, __LINE__, "Not a sound option");
}
readName(token);
originalName = getName();
}
OptionSound::~OptionSound(){
}
void OptionSound::logic(){
ostringstream temp;
temp << originalName << ": " << Configuration::getSoundVolume();
setText(temp.str());
}
void OptionSound::run(const Menu::Context & context){
}
void OptionSound::changeSound(int much){
int volume = Configuration::getSoundVolume();
volume += much;
if (volume < 0){
volume = 0;
}
if (volume > 100){
volume = 100;
}
Configuration::setSoundVolume(volume);
}
bool OptionSound::leftKey(){
changeSound(-1);
lblue = lgreen = 0;
return true;
}
bool OptionSound::rightKey(){
changeSound(+1);
rblue = rgreen = 0;
return true;
}
OptionMusic::OptionMusic(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if (*token != "music" ){
throw LoadException(__FILE__, __LINE__, "Not a music option");
}
readName(token);
originalName = getName();
}
void OptionMusic::logic(){
ostringstream temp;
temp << originalName << ": " << Configuration::getMusicVolume();
setText(temp.str());
}
void OptionMusic::run(const Menu::Context & context){
}
void OptionMusic::changeMusic(int much){
int volume = Configuration::getMusicVolume();
volume += much;
if (volume < 0){
volume = 0;
}
if (volume > 100){
volume = 100;
}
Configuration::setMusicVolume(volume);
Music::setVolume((double) volume / 100.0);
}
bool OptionMusic::leftKey(){
changeMusic(-1);
lblue = lgreen = 0;
return true;
}
bool OptionMusic::rightKey(){
changeMusic(+1);
lblue = lgreen = 0;
return true;
}
OptionMusic::~OptionMusic(){
}
OptionLanguage::OptionLanguage(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
readName(token);
#if 0
const Token * start = token->getRootParent();
vector<const Token*> tokens = start->findTokens("*/language");
vector<string> all;
for (vector<const Token*>::iterator it = tokens.begin(); it != tokens.end(); it++){
string language;
const Token * token = *it;
if (token->match("language", language)){
all.push_back(language);
}
}
sort(all.begin(), all.end());
unique_copy(all.begin(), all.end(), back_insert_iterator<vector<string> >(languages));
// Global::debug(0) << "Found " << languages.size() << " languages" << endl;
#endif
}
void OptionLanguage::run(const Menu::Context & context){
class LanguageOption: public MenuOption {
public:
LanguageOption(const Gui::ContextBox & parent, const string & language):
MenuOption(parent, NULL){
setText(language);
setInfoText(language);
}
virtual void logic(){
}
virtual void run(const ::Menu::Context & context){
Configuration::setLanguage(getText());
Configuration::saveConfiguration();
throw ::Menu::MenuException(__FILE__, __LINE__);
}
};
Util::NewReferenceCount<Menu::DefaultRenderer> renderer;
Menu::Menu temp(renderer);
Util::ReferenceCount<Menu::FontInfo> info(new Menu::RelativeFontInfo(Font::getDefaultFontPath(), 24, 24));
temp.setFont(info);
const Gui::ContextBox & box = renderer->getBox();
vector<string> languages = context.getLanguages();
for (vector<string>::iterator it = languages.begin(); it != languages.end(); it++){
temp.addOption(new LanguageOption(box, *it));
}
try {
temp.run(context);
} catch (const Exception::Return & ignore){
} catch (const Menu::MenuException & ex){
}
throw Menu::Reload(__FILE__, __LINE__);
// throw Exception::Return(__FILE__, __LINE__);
}
void OptionLanguage::logic(){
}
OptionJoystick::OptionJoystick(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token){
setRunnable(true);
if (*token != "joystick" ){
throw LoadException(__FILE__, __LINE__, "Not a joystick option");
}
readName(token);
}
void OptionJoystick::logic(){
}
class JoystickLogicDraw: public Util::Logic, public Util::Draw {
public:
enum Inputs{
Exit
};
static const int marginX = 20;
JoystickLogicDraw(int id, const Util::ReferenceCount<Joystick> & joystick, const ::Menu::Context & context):
id(id),
joystick(joystick),
quit(false),
context(context, Menu::Context()){
input.set(Keyboard::Key_ESC, Exit);
}
const int id;
Util::ReferenceCount<Joystick> joystick;
bool quit;
Menu::Context context;
InputMap<Inputs> input;
void doInput(){
vector<InputMap<Inputs>::InputEvent> out = InputManager::getEvents(input, InputSource(true));
for (vector<InputMap<Inputs>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<Inputs>::InputEvent & event = *it;
if (event.enabled){
if (event.out == Exit){
quit = true;
// context.finish();
}
}
}
}
virtual void run(){
doInput();
context.act();
}
bool done(){
return quit;
}
double ticks(double system){
return system * Global::ticksPerSecond(60);
}
void drawButtons(const Font & font, const Graphics::Bitmap & buffer, int y){
Graphics::Color color = Graphics::makeColor(255, 255, 255);
font.printf(marginX, y, color, buffer, "Up: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Down: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Left: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Right: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button1: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button2: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button3: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button4: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button5: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button6: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Select: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Quit: ", 0); y += font.getHeight() + 5;
}
void draw(const Graphics::Bitmap & buffer){
const Font & font = Menu::menuFontParameter.current()->get();
Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
work.start();
context.renderBackground(work);
/* FIXME: scale the joystck name down to fit */
font.printf(marginX, 1, Graphics::makeColor(255, 255, 255), work, "Joystick %d: %s", 0, id, joystick->getName().c_str());
drawButtons(font, work, 1 + font.getHeight() + 5);
context.renderForeground(work);
work.finish();
}
};
namespace{
struct Axis{
Axis():
stick(0),
axis(0),
first(0),
set(false),
last(0),
lastMotion(0){
}
int stick;
int axis;
/* first value from this axis. we assume that
* the first value is sufficiently close to 'zero' which
* can be any value, but most likely will either be
* -1, 0, or 1
*/
double first;
/* true if first has been set */
bool set;
/* keep track of last value for this axis */
double last;
/* last time an event was produced (or at least last
* time we read it). it might be better to get the actual
* time from the event itself.
*/
uint64_t lastMotion;
};
}
static void runJoystickMenu(int joystickId, const Util::ReferenceCount<Joystick> & joystick, const ::Menu::Context & context){
Util::NewReferenceCount<Menu::DefaultRenderer> renderer;
Menu::Menu menu(renderer);
Gui::ContextBox & box = renderer->getBox();
box.setListType(ContextBox::Normal);
Gui::ListValues attributes(box.getListValues());
attributes.setDistanceFade(false);
box.setListValues(attributes);
menu.setPosition(Gui::Coordinate(Gui::RelativePoint(-0.8, -0.3),
Gui::RelativePoint(0, 0.8)));
#define WAIT_TIME_MS (0.7 * 1000)
#define WAIT_TIME_AXIS_MS (1 * 1000)
#define AXIS_THRESHOLD 0.7
class JoystickButton: public MenuOption {
public:
JoystickButton(const Menu::Menu & menu, const Gui::ContextBox & parent, const Util::ReferenceCount<Joystick> & joystick, const string & name, Joystick::Key key):
MenuOption(parent, NULL),
menu(menu),
name(name),
joystick(joystick),
key(key){
setText(name);
setInfoText(name);
}
const Menu::Menu & menu;
string name;
Util::ReferenceCount<Joystick> joystick;
Joystick::Key key;
class ButtonListener: public JoystickListener {
public:
ButtonListener(const Util::ReferenceCount<Joystick> & joystick):
touched(false),
done(false),
chosen(-1),
chosenAxis(NULL){
map<int, map<int, double> > axisValues = joystick->getCurrentAxisValues();
for (map<int, map<int, double> >::iterator it = axisValues.begin(); it != axisValues.end(); it++){
int stick = it->first;
const map<int, double> & subMap = it->second;
for (map<int, double>::const_iterator it = subMap.begin(); it != subMap.end(); it++){
int axis = it->first;
double value = it->second;
Axis use;
use.stick = stick;
use.axis = axis;
use.first = value;
use.set = true;
use.last = value;
use.lastMotion = 0;
this->axis.push_back(use);
}
}
}
map<int, uint64_t> presses;
map<int, bool> pressed;
/* true if a button/axis is moved. flips to false when queried */
bool touched;
vector<Axis> axis;
bool done;
int chosen;
Axis * chosenAxis;
bool wasTouched(){
bool out = touched;
touched = false;
return out;
}
Axis & getAxis(int stick, int axis){
for (vector<Axis>::iterator it = this->axis.begin(); it != this->axis.end(); it++){
Axis & use = *it;
if (use.stick == stick && use.axis == axis){
return use;
}
}
Axis out;
out.stick = stick;
out.axis = axis;
this->axis.push_back(out);
return getAxis(stick, axis);
}
const vector<Axis> & getAllAxis() const {
return axis;
}
const map<int, uint64_t> & getPresses() const {
return presses;
}
int getButton() const {
return chosen;
}
Axis * getChosenAxis() const {
return chosenAxis;
}
virtual ~ButtonListener(){
}
bool isDone() const {
return (getButton() != -1 && !anyPressed()) ||
(getChosenAxis() != NULL);
}
void choose(){
uint64_t now = System::currentMilliseconds();
for (map<int, uint64_t>::const_iterator it = presses.begin(); it != presses.end(); it++){
uint64_t what = it->second;
if (what != 0 && now - what > WAIT_TIME_MS){
chosen = it->first;
}
}
for (vector<Axis>::iterator it = axis.begin(); it != axis.end(); it++){
Axis & use = *it;
if (fabs(use.last - use.first) > AXIS_THRESHOLD &&
now - use.lastMotion > WAIT_TIME_AXIS_MS){
chosenAxis = &use;
}
}
}
bool anyPressed() const {
for (map<int, bool>::const_iterator it = pressed.begin(); it != pressed.end(); it++){
if (it->second){
return true;
}
}
return false;
}
virtual void pressButton(Joystick * from, int button){
touched = true;
pressed[button] = true;
presses[button] = System::currentMilliseconds();
}
virtual void releaseButton(Joystick * from, int button){
presses[button] = 0;
pressed[button] = false;
}
/* either all increasing or all decreasing */
bool monotonic(const vector<double> & what){
if (what.size() == 0){
return true;
}
double first = what[0];
int direction = -1;
vector<double>::const_iterator it = what.begin();
it++;
for (/**/; it != what.end(); it++){
switch (direction){
case -1: {
if (*it < first){
direction = 1;
} else if (*it > first){
direction = 2;
}
first = *it;
break;
}
case 1: {
if (*it > first){
return false;
}
break;
}
case 2: {
if (*it < first){
return false;
}
break;
}
}
}
return true;
}
virtual void axisMotion(Joystick * from, int stick, int axis, double motion){
touched = true;
Axis & use = getAxis(stick, axis);
if (!use.set){
use.first = motion;
use.set = true;
}
use.last = motion;
use.lastMotion = System::currentMilliseconds();
/*
const double AXIS_THRESHOLD = 0.5;
Global::debug(0) << "stick " << stick << " axis " << axis << " first " << use.first << " last " << use.last << " diff " << fabs(use.last - use.first) << std::endl;
if (fabs(use.last - use.first) > AXIS_THRESHOLD){
Global::debug(0) << "stick " << stick << " axis " << axis << " motion " << motion << std::endl;
}
*/
}
virtual void hatMotion(Joystick * from, int motion){
}
};
void logic(){
ostringstream out;
int stick, axis;
double low, high;
if (joystick->getAxis(key, stick, axis, low, high)){
out << name << ": " << stick << "/" << axis << "/";
if (low < 0){
out << "-";
} else {
out << "+";
}
} else {
int button = joystick->getButton(key);
out << name << ": ";
if (button != -1){
out << joystick->getButton(key);
} else {
out << "unset";
}
}
setText(out.str());
}
void run(const Menu::Context & context){
class SetButton: public Util::Logic, public Util::Draw {
public:
SetButton(const Menu::Context & context, const Menu::Menu & menu, const string & name, const Util::ReferenceCount<Joystick> & joystick):
context(context),
menu(menu),
name(name),
listener(joystick),
joystick(joystick),
startingTime(System::currentMilliseconds()),
maxTime(5000){
input.set(Keyboard::Key_ESC, 0);
joystick->addListener(&listener);
vector<Graphics::BlendPoint> points;
points.push_back(Graphics::BlendPoint(Graphics::makeColor(255, 0, 0), 30));
points.push_back(Graphics::BlendPoint(Graphics::makeColor(0, 255, 0), 25));
points.push_back(Graphics::BlendPoint(Graphics::makeColor(255, 255, 255), 0));
colors = Graphics::blend_palette(points);
}
const Menu::Context & context;
const Menu::Menu & menu;
string name;
ButtonListener listener;
InputMap<int> input;
Util::ReferenceCount<Joystick> joystick;
vector<Graphics::Color> colors;
uint64_t startingTime;
const uint64_t maxTime;
virtual ~SetButton(){
joystick->removeListener(&listener);
}
virtual void showTimeLeft(const Graphics::Bitmap & screen){
int x1 = 1;
int y1 = 1;
int x2 = screen.getWidth() * (1 - (double) (System::currentMilliseconds() - startingTime) / (double) maxTime);
if (x2 < x1){
x2 = x1;
}
int y2 = 10;
int color = (1.0 - (double) (System::currentMilliseconds() - startingTime) / maxTime) * colors.size();
if (color < 0){
color = 0;
}
- if (color >= colors.size()){
+ if (color >= (int) colors.size()){
color = colors.size() - 1;
}
screen.rectangleFill(x1, y1, x2, y2, colors[color]);
}
void setButton(Joystick::Key key){
int button = listener.getButton();
if (button != -1){
Global::debug(1) << "Chosen button " << listener.getButton() << std::endl;
joystick->setCustomButton(listener.getButton(), key);
} else {
Axis * axis = listener.getChosenAxis();
double rangeLow = 0;
double rangeHigh = 0;
/* stick went negative and went to -1 */
if (axis->first <= 0 && axis->last < axis->first){
rangeLow = -1;
rangeHigh = -AXIS_THRESHOLD;
/* stick started at negative and went positive, possibly
* not above 0.
*/
} else if (axis->first < 0 && axis->last > axis->first){
rangeLow = 0;
rangeHigh = 1;
/* stick started positive and went to 1 */
} else if (axis->first >= 0 && axis->last > axis->first){
rangeLow = AXIS_THRESHOLD;
rangeHigh = 1;
/* stick started positive and went towards -1 */
} else if (axis->first > 0 && axis->last < axis->first){
rangeLow = -1;
rangeHigh = 0;
}
Global::debug(0) << "Set stick " << axis->stick << " axis " << axis->axis << " [" << rangeLow << ", " << rangeHigh << "]" << std::endl;
joystick->setCustomAxis(key, axis->stick, axis->axis, rangeLow, rangeHigh);
}
}
double ticks(double system){
return system * Global::ticksPerSecond(60);
}
bool done(){
return listener.isDone();
}
void run(){
vector<InputMap<int>::InputEvent> out = InputManager::getEvents(input, InputSource(true));
for (vector<InputMap<int>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<int>::InputEvent & event = *it;
if (event.enabled){
if (event.out == 0){
throw Exception::Return(__FILE__, __LINE__);
}
}
}
listener.choose();
if (listener.wasTouched()){
startingTime = System::currentMilliseconds();
}
if (System::currentMilliseconds() - startingTime >= maxTime){
throw Exception::Return(__FILE__, __LINE__);
}
}
void draw(const Graphics::Bitmap & buffer){
Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
work.start();
menu.render(context, work);
const Font & font = Menu::menuFontParameter.current()->get();
Gui::RelativePoint start(0.2, -0.3);
Gui::RelativePoint end(0.85, 0.8);
int x = start.getX();
int y = start.getY();
// font.printfWrap(x, y - font.getHeight() * 2 - 5, Graphics::makeColor(255, 255, 255), work, end.getX() - start.getX(), "Press and hold a button", 0);
font.printf(x, y - 18 - 5, 18, 18, Graphics::makeColor(255, 255, 255), work, "Press and hold a button", 0);
work.translucent(0, 0, 0, 128).rectangleFill(x, y, end.getX(), end.getY(), Graphics::makeColor(0, 0, 0));
uint64_t now = System::currentMilliseconds();
const map<int, uint64_t> & presses = listener.getPresses();
for (map<int, uint64_t>::const_iterator it = presses.begin(); it != presses.end(); it++){
int button = it->first;
uint64_t time = it->second;
if (time > 0){
int delta = now - time;
if (delta > WAIT_TIME_MS){
delta = WAIT_TIME_MS;
}
/* this shouldn't happen... */
if (delta < 0){
delta = 0;
}
Graphics::Color color;
color = Graphics::makeColor((int)(255.0 * (double) delta / (WAIT_TIME_MS)),
0, 255);
if (button == listener.getButton()){
color = Graphics::makeColor(255, 255, 255);
}
ostringstream text;
text << name << ": " << button;
font.printf(x, y, color, work, text.str(), 0);
y += font.getHeight() + 5;
}
}
const vector<Axis> & axis = listener.getAllAxis();
for (vector<Axis>::const_iterator it = axis.begin(); it != axis.end(); it++){
const Axis & use = *it;
if (fabs(use.last - use.first) > AXIS_THRESHOLD){
int delta = now - use.lastMotion;
if (delta > WAIT_TIME_AXIS_MS){
delta = WAIT_TIME_AXIS_MS;
}
/* this shouldn't happen... */
if (delta < 0){
delta = 0;
}
Graphics::Color color;
color = Graphics::makeColor((int)(255.0 * (double) delta / (WAIT_TIME_AXIS_MS)),
0, 255);
if (&use == listener.getChosenAxis()){
color = Graphics::makeColor(255, 255, 255);
}
ostringstream text;
text << name << ": stick " << use.stick << " axis " << use.axis;
if (use.last > use.first){
text << " +";
} else {
text << " -";
}
font.printf(x, y, 18, 18, color, work, text.str(), 0);
y += font.getHeight() + 5;
}
}
showTimeLeft(work);
work.finish();
}
void wait(){
while (listener.anyPressed()){
InputManager::poll();
Util::rest(1);
}
}
};
Global::debug(1) << "Set button " << getName() << std::endl;
SetButton set(context, menu, name, joystick);
try{
Util::standardLoop(set, set);
set.setButton(key);
} catch (const Exception::Return & quit){
}
set.wait();
}
};
menu.addOption(new JoystickButton(menu, box, joystick, "Up", Joystick::Up));
menu.addOption(new JoystickButton(menu, box, joystick, "Down", Joystick::Down));
menu.addOption(new JoystickButton(menu, box, joystick, "Left", Joystick::Left));
menu.addOption(new JoystickButton(menu, box, joystick, "Right", Joystick::Right));
menu.addOption(new JoystickButton(menu, box, joystick, "Button1", Joystick::Button1));
menu.addOption(new JoystickButton(menu, box, joystick, "Button2", Joystick::Button2));
menu.addOption(new JoystickButton(menu, box, joystick, "Button3", Joystick::Button3));
menu.addOption(new JoystickButton(menu, box, joystick, "Button4", Joystick::Button4));
menu.addOption(new JoystickButton(menu, box, joystick, "Button5", Joystick::Button5));
menu.addOption(new JoystickButton(menu, box, joystick, "Button6", Joystick::Button6));
menu.addOption(new JoystickButton(menu, box, joystick, "Start", Joystick::Start));
menu.addOption(new JoystickButton(menu, box, joystick, "Quit", Joystick::Quit));
try {
menu.run(context);
} catch (const Exception::Return & ignore){
} catch (const Menu::MenuException & ex){
}
}
void OptionJoystick::run(const Menu::Context & context){
class JoystickOption: public MenuOption {
public:
JoystickOption(const Gui::ContextBox & parent, int id, const Util::ReferenceCount<Joystick> & joystick):
MenuOption(parent, NULL),
joystick(joystick),
id(id){
ostringstream out;
out << "Joystick " << (id + 1);
setText(out.str());
setInfoText(joystick->getName());
}
const Util::ReferenceCount<Joystick> joystick;
const int id;
virtual void logic(){
}
virtual void run(const ::Menu::Context & context){
runJoystickMenu(id, joystick, context);
/*
JoystickLogicDraw mainLoop(id, joystick, context);
Util::standardLoop(mainLoop, mainLoop);
*/
throw ::Menu::MenuException(__FILE__, __LINE__);
}
};
Util::NewReferenceCount<Menu::DefaultRenderer> renderer;
Menu::Menu menu(renderer);
/*
Util::ReferenceCount<Menu::FontInfo> info(new Menu::RelativeFontInfo(Global::DEFAULT_FONT, 24, 24));
temp.setFont(info);
*/
Gui::ContextBox & box = renderer->getBox();
box.setListType(ContextBox::Normal);
map<int, Util::ReferenceCount<Joystick> > joysticks = InputManager::getJoysticks();
for (map<int, Util::ReferenceCount<Joystick> >::iterator it = joysticks.begin(); it != joysticks.end(); it++){
menu.addOption(new JoystickOption(box, it->first, it->second));
}
if (joysticks.size() == 0){
menu.addOption(new OptionDummy(box, "No joysticks found!"));
}
try {
menu.run(context);
} catch (const Exception::Return & ignore){
} catch (const Menu::MenuException & ex){
}
}
OptionJoystick::~OptionJoystick(){
}

File Metadata

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

Event Timeline