Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
81 KB
Referenced Files
None
Subscribers
None
This document is not UTF8. It was detected as JIS and converted to UTF8 for display.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 84d4cec..aa1b9d5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,101 +1,110 @@
Project (meandmyshadow)
CMake_Minimum_Required (VERSION 2.6)
Set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
#Option if the game should be compiled with hardware acceleration.
Option (HARDWARE_ACCELERATION "Use openGL as rendering backend" ON)
Option (DEBUG_MODE "Compile the game with debug mode enabled" OFF)
#Use openGL only when compiling with HARDWARE_ACCELERATION.
#Otherwise we try to find XLib.
if (HARDWARE_ACCELERATION)
Find_Package (OpenGL REQUIRED)
if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
Find_Package (X11 REQUIRED)
endif()
else (HARDWARE_ACCELERATION)
Include (FindX11)
endif (HARDWARE_ACCELERATION)
#Find the required libraries.
Find_Package (SDL REQUIRED)
Find_Package (SDL_gfx REQUIRED)
Find_Package (SDL_image REQUIRED)
Find_Package (SDL_ttf REQUIRED)
Find_Package (SDL_mixer REQUIRED)
Find_Package (CURL REQUIRED)
Find_Package (LibArchive REQUIRED)
# maybe Lua >= 5.3 is also OK
Find_Package (Lua 5.2 EXACT REQUIRED)
if (HARDWARE_ACCELERATION AND NOT OPENGL_FOUND)
message (FATAL_ERROR "OpenGL library could not be found!")
elseif (NOT HARDWARE_ACCELERATION AND NOT X11_FOUND)
message (FATAL_ERROR "X11 library could not be found!")
endif (HARDWARE_ACCELERATION AND NOT OPENGL_FOUND)
#Parse the configure file.
Configure_File (
"${PROJECT_SOURCE_DIR}/src/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
#Add the include directories of the (found) libraries.
Include_Directories(
${PROJECT_BINARY_DIR}
${X11_X11_INCLUDE_PATH}
${OPENGL_gl_INCLUDE_DIR}
${SDL_INCLUDE_DIR}
${SDLGFX_INCLUDE_DIR}
${SDLIMAGE_INCLUDE_DIR}
${SDLTTF_INCLUDE_DIR}
${SDLMIXER_INCLUDE_DIR}
${CURL_INCLUDE_DIR}
${LibArchive_INCLUDE_DIR}
${LUA_INCLUDE_DIR}
)
#Set the output path and the source path.
Set (EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
Set (SRC_DIR ${PROJECT_SOURCE_DIR}/src)
#List the source files.
File (GLOB SOURCES ${SRC_DIR}/*.cpp)
File (GLOB TINYGETTEXT ${SRC_DIR}/libs/tinygettext/*.cpp)
File (GLOB FINDLOCALE ${SRC_DIR}/libs/findlocale/*.cpp)
-Add_Executable (meandmyshadow ${SOURCES} ${TINYGETTEXT} ${FINDLOCALE})
+Set(OSX_SOURCES )
+
+if(APPLE)
+ Set(OSX_SOURCES
+ ${SRC_DIR}/mac/meandmyshadow/osx_helpers.mm
+ ${SRC_DIR}/mac/meandmyshadow/SDLMain.m
+ )
+endif()
+
+Add_Executable (meandmyshadow ${SOURCES} ${TINYGETTEXT} ${FINDLOCALE} ${OSX_SOURCES})
Target_Link_Libraries (
meandmyshadow
${OPENGL_gl_LIBRARY}
${X11_X11_LIB}
${SDL_LIBRARY}
${SDLGFX_LIBRARY}
${SDLIMAGE_LIBRARY}
${SDLTTF_LIBRARY}
${SDLMIXER_LIBRARY}
${CURL_LIBRARY}
${LibArchive_LIBRARY}
${LUA_LIBRARIES}
)
#Path options
Set (BINDIR "bin" CACHE STRING "Where to install binaries")
Set (DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE STRING "Sets the root of data directories to a non-default location")
Set (ICONDIR "${DATAROOTDIR}/icons" CACHE STRING "Sets the icon directory for desktop entry to a non-default location.")
Set (DESKTOPDIR "${DATAROOTDIR}/applications" CACHE STRING "Sets the desktop file directory for desktop entry to a non-default location.")
#Install locations
Install (DIRECTORY ${PROJECT_SOURCE_DIR}/data DESTINATION ${DATAROOTDIR}/meandmyshadow/)
Install (FILES AUTHORS DESTINATION ${DATAROOTDIR}/meandmyshadow/)
Install (TARGETS meandmyshadow RUNTIME DESTINATION ${BINDIR})
if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
Install (FILES meandmyshadow.desktop DESTINATION ${DESKTOPDIR})
Install (FILES icons/16x16/meandmyshadow.png DESTINATION ${ICONDIR}/hicolor/16x16/apps/)
Install (FILES icons/32x32/meandmyshadow.png DESTINATION ${ICONDIR}/hicolor/32x32/apps/)
Install (FILES icons/48x48/meandmyshadow.png DESTINATION ${ICONDIR}/hicolor/48x48/apps/)
Install (FILES icons/64x64/meandmyshadow.png DESTINATION ${ICONDIR}/hicolor/64x64/apps/)
endif ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
diff --git a/src/GUIListBox.cpp b/src/GUIListBox.cpp
index cb8d35b..39a29c4 100644
--- a/src/GUIListBox.cpp
+++ b/src/GUIListBox.cpp
@@ -1,528 +1,528 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GUIListBox.h"
using namespace std;
GUIListBox::GUIListBox(int left,int top,int width,int height,bool enabled,bool visible,int gravity):
GUIObject(left,top,width,height,NULL,-1,enabled,visible,gravity),selectable(true),clickEvents(false){
//Set the state -1.
state=-1;
//Create the scrollbar and add it to the children.
scrollBar=new GUIScrollBar(width-16,0,16,height,1,0,0,0,1,1,true,true);
childControls.push_back(scrollBar);
}
GUIListBox::~GUIListBox(){
//Remove all items and cache.
clearItems();
//We need to delete every child we have.
for(unsigned int i=0;i<childControls.size();i++){
delete childControls[i];
}
//Deleted the childs now empty the childControls vector.
childControls.clear();
}
bool GUIListBox::handleEvents(int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when he and his parent are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when he and his parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left;
y+=top;
//Update the scrollbar.
if(scrollBar->visible)
b=b||scrollBar->handleEvents(x,y,enabled,visible,b);
//Set state negative.
state=-1;
//Check if the GUIListBox is visible, enabled and no event has been processed before.
if(enabled&&visible&&!b){
//The mouse location (x=i, y=j) and the mouse button (k).
int i,j;
SDL_GetMouseState(&i,&j);
//Convert the mouse location to a relative location.
i-=x;
j-=y;
//Check if the mouse is inside the GUIListBox.
if(i>=0&&i<width-4&&j>=0&&j<height-4){
//Calculate selected item.
int idx=-1;
int yPos=-firstItemY;
int i=scrollBar->value;
if(yPos!=0) i--;
for(;i<images.size();++i){
SDL_Surface** c=&images.at(i);
if(*c) yPos+=(*c)->h;
if(j<yPos){
idx=i;
break;
}
}
//If the entry isn't above the max we have an event.
if(idx>=0&&idx<(int)item.size()&&selectable){
state=idx;
//Check if the left mouse button is pressed.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT){
//Check if the slected item changed.
if(value!=idx){
value=idx;
//If event callback is configured then add an event to the queue.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
//After possibly a change event, there will always be a click event.
if(eventCallback && clickEvents){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
}
}
//Check for mouse wheel scrolling.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELDOWN && scrollBar->enabled){
scrollBar->value++;
if(scrollBar->value>scrollBar->maxValue)
scrollBar->value=scrollBar->maxValue;
}else if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELUP && scrollBar->enabled){
scrollBar->value--;
if(scrollBar->value<0)
scrollBar->value=0;
}
}
}
//Process child controls event except for the scrollbar.
//That's why i starts at one.
for(unsigned int i=1;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
void GUIListBox::render(int x,int y,bool draw){
if(updateScrollbar){
//Calculate the height of the content.
int maxY=0;
for(unsigned int i=0;i<images.size();i++){
maxY+=images.at(i)->h;
}
//Check if we need to show the scrollbar for many entries.
if(maxY<height){
scrollBar->maxValue=0;
scrollBar->value=0;
scrollBar->visible=false;
}else{
scrollBar->visible=true;
scrollBar->maxValue=item.size();
int yy=0;
for(int i=images.size()-1;i>0;i--){
yy+=images.at(i)->h;
if(yy>height)
break;
else
scrollBar->maxValue--;
}
scrollBar->largeChange=item.size()/4;
}
updateScrollbar=false;
}
//Rectangle the size of the GUIObject, used to draw borders.
//SDL_Rect r; //Unused local variable :/
//There's no need drawing the GUIObject when it's invisible.
if(!visible||!draw)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Draw the background box.
SDL_Rect r={x,y,width,height};
SDL_FillRect(screen,&r,0xFFFFFFFF);
firstItemY=0;
//Loop through the entries and draw them.
if(scrollBar->value==scrollBar->maxValue&&scrollBar->visible){
int lowNumber=height;
int currentItem=images.size()-1;
while(lowNumber>=0&&currentItem>=0){
lowNumber-=images.at(currentItem)->h;
if(lowNumber>0){
if(selectable){
//Check if the mouse is hovering on current entry. If so draw borders around it.
if(state==currentItem)
drawGUIBox(x,y+lowNumber-1,width,images.at(currentItem)->h+1,screen,0x00000000);
//Check if the current entry is selected. If so draw a gray background.
if(value==currentItem)
drawGUIBox(x,y+lowNumber-1,width,images.at(currentItem)->h+1,screen,0xDDDDDDFF);
}
applySurface(x,y+lowNumber,images.at(currentItem),screen,NULL);
}else{
if(selectable){
//Check if the mouse is hovering on current entry. If so draw borders around it.
if(state==currentItem)
drawGUIBox(x,y,width,images.at(currentItem)->h+lowNumber+1,screen,0x00000000);
//Check if the current entry is selected. If so draw a gray background.
if(value==currentItem)
drawGUIBox(x,y,width,images.at(currentItem)->h+lowNumber+1,screen,0xDDDDDDFF);
}
firstItemY=-lowNumber;
SDL_Rect clip;
clip.x=0;
clip.y=-lowNumber;
clip.w=images.at(currentItem)->w;
clip.h=images.at(currentItem)->h+lowNumber;
applySurface(x,y,images.at(currentItem),screen,&clip);
break;
}
currentItem--;
}
}else{
- for(int i=scrollBar->value,j=y+1;j>height,i<(int)item.size();i++){
+ for(int i=scrollBar->value,j=y+1;j<=height&&i<(int)item.size();i++){
//Check if the current item is out side of the widget.
int yOver=images[i]->h;
if(j+images[i]->h>y+height)
yOver=y+height-j;
if(yOver>0){
if(selectable){
//Check if the mouse is hovering on current entry. If so draw borders around it.
if(state==i)
drawGUIBox(x,j-1,width,yOver+1,screen,0x00000000);
//Check if the current entry is selected. If so draw a gray background.
if(value==i)
drawGUIBox(x,j-1,width,yOver+1,screen,0xDDDDDDFF);
}
//Draw the image.
SDL_Rect clip;
clip.x=0;
clip.y=0;
clip.w=images[i]->w;
clip.h=yOver;
applySurface(x,j,images[i],screen,&clip);
}else{
break;
}
j+=images[i]->h;
}
}
//Draw borders around the whole thing.
drawGUIBox(x,y,width,height,screen,0x00000000);
//We now need to draw all the children of the GUIObject.
for(unsigned int i=0;i<childControls.size();i++){
childControls[i]->render(x,y,draw);
}
}
void GUIListBox::clearItems(){
item.clear();
for(unsigned int i=0;i<images.size();i++){
SDL_FreeSurface(images[i]);
}
images.clear();
}
void GUIListBox::addItem(std::string name, SDL_Surface* image){
item.push_back(name);
if(image){
images.push_back(image);
}else if(!image&&!name.empty()){
SDL_Color black={0,0,0,0};
SDL_Surface* tmp=TTF_RenderUTF8_Blended(fontText,name.c_str(),black);
images.push_back(tmp);
}
updateScrollbar=true;
}
void GUIListBox::updateItem(int index, std::string newText, SDL_Surface* newImage){
item.at(index)=newText;
if(newImage){
SDL_FreeSurface(images.at(index));
images.at(index)=newImage;
}else if(!newImage&&!newText.empty()){
SDL_FreeSurface(images.at(index));
SDL_Color black={0,0,0,0};
SDL_Surface* tmp=TTF_RenderUTF8_Blended(fontText,newText.c_str(),black);
images.at(index)=tmp;
}
updateScrollbar=true;
}
std::string GUIListBox::getItem(int index){
return item.at(index);
}
GUISingleLineListBox::GUISingleLineListBox(int left,int top,int width,int height,bool enabled,bool visible,int gravity):
GUIObject(left,top,width,height,NULL,-1,enabled,visible,gravity),animation(0){}
void GUISingleLineListBox::addItem(string name,string label){
//Check if the label is set, if not use the name.
if(label.size()==0)
label=name;
item.push_back(pair<string,string>(name,label));
}
void GUISingleLineListBox::addItems(vector<pair<string,string> > items){
vector<pair<string,string> >::iterator it;
for(it=items.begin();it!=items.end();++it){
addItem(it->first,it->second);
}
}
void GUISingleLineListBox::addItems(vector<string> items){
vector<string>::iterator it;
for(it=items.begin();it!=items.end();++it){
addItem(*it);
}
}
string GUISingleLineListBox::getName(unsigned int index){
- if(index==-1)
+ if(index==(unsigned int)-1)
index=value;
- if(index<0||index>item.size())
+ if(index>item.size())
return "";
return item[index].first;
}
bool GUISingleLineListBox::handleEvents(int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when he and his parent(s) are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when he and his parent(s) are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
state&=~0xF;
if(enabled&&visible){
//The mouse location (x=i, y=j) and the mouse button (k).
int i,j,k;
k=SDL_GetMouseState(&i,&j);
//Convert the mouse location to a relative location.
i-=x;
j-=y;
//The selected button.
//0=nothing 1=left 2=right.
int idx=0;
//Check which button the mouse is above.
if(i>=0&&i<width&&j>=0&&j<height){
if(i<26 && i<width/2){
//The left arrow.
idx=1;
}else if(i>=width-26){
//The right arrow.
idx=2;
}
}
//If idx is 0 it means the mous doesn't hover any arrow so reset animation.
if(idx==0)
animation=0;
//Check if there's a mouse button press or not.
if(k&SDL_BUTTON(1)){
if(((state>>4)&0xF)==idx)
state|=idx;
}else{
state|=idx;
}
//Check if there's a mouse press.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT && idx){
state=idx|(idx<<4);
}else if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT && idx && ((state>>4)&0xF)==idx){
int m=(int)item.size();
if(m>0){
if(idx==2){
idx=value+1;
if(idx<0||idx>=m) idx=0;
if(idx!=value){
value=idx;
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
}
}else if(idx==1){
idx=value-1;
if(idx<0||idx>=m) idx=m-1;
if(idx!=value){
value=idx;
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
}
}
}
}
if(event.type==SDL_MOUSEBUTTONUP) state&=0xF;
}else{
//Set state zero.
state=0;
}
return b;
}
void GUISingleLineListBox::render(int x,int y,bool draw){
//Rectangle the size of the GUIObject, used to draw borders.
SDL_Rect r;
//There's no need drawing the GUIObject when it's invisible.
if(!visible)
return;
//NOTE: logic in the render method since it's the only part that gets called every frame.
if((state&0xF)==0x1 || (state&0xF)==0x2){
animation++;
if(animation>20)
animation=-20;
}
//Get the absolute x and y location.
x+=left;
y+=top;
if(gravity==GUIGravityCenter)
gravityX=int(width/2);
else if(gravity==GUIGravityRight)
gravityX=width;
x-=gravityX;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
if(enabled!=cachedEnabled || item[value].second.compare(cachedCaption)!=0){
//Free the cache.
SDL_FreeSurface(cache);
cache=NULL;
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=item[value].second;
}
//Draw the text.
if(value>=0 && value<(int)item.size()){
//Get the text.
const char* lp=item[value].second.c_str();
//Check if the text is empty or not.
if(lp!=NULL && lp[0]){
if(!cache){
SDL_Color color;
if(inDialog)
color=themeTextColorDialog;
else
color=themeTextColor;
cache=TTF_RenderUTF8_Blended(fontGUI,lp,color);
//If the text is too wide then we change to smaller font (?)
//NOTE: The width is 32px smaller (2x16px for the arrows).
if(cache->w>width-32){
SDL_FreeSurface(cache);
cache=TTF_RenderUTF8_Blended(fontGUISmall,lp,color);
}
}
if(draw){
//Center the text both vertically as horizontally.
r.x=x+(width-cache->w)/2;
r.y=y+(height-cache->h)/2-GUI_FONT_RAISE;
//Draw the text and free the surface afterwards.
SDL_BlitSurface(cache,NULL,screen,&r);
}
}
}
if(draw){
//Draw the arrows.
SDL_Rect r2={48,0,16,16};
r.x=x;
if((state&0xF)==0x1)
r.x+=abs(animation/2);
r.y=y+4;
if(inDialog)
applySurface(r.x,r.y,arrowLeft2,screen,NULL);
else
applySurface(r.x,r.y,arrowLeft1,screen,NULL);
r.x=x+width-16;
if((state&0xF)==0x2)
r.x-=abs(animation/2);
if(inDialog)
applySurface(r.x,r.y,arrowRight2,screen,NULL);
else
applySurface(r.x,r.y,arrowRight1,screen,NULL);
}
}
diff --git a/src/GameObjects.h b/src/GameObjects.h
index ce476f4..6740139 100644
--- a/src/GameObjects.h
+++ b/src/GameObjects.h
@@ -1,174 +1,174 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GAME_OBJECTS_H
#define GAME_OBJECTS_H
#include "Globals.h"
#include "TreeStorageNode.h"
#include "Player.h"
#include <SDL.h>
#include <string>
#include <vector>
#include <utility>
#include <map>
class Game;
class Player;
//The different gameObject events.
enum GameObjectEventType{
//Event called when the player walks on the gameObject.
GameObjectEvent_PlayerWalkOn=1,
//Event called when the player is on the gameObject.
GameObjectEvent_PlayerIsOn,
//Event called when the player leaves the gameObject.
GameObjectEvent_PlayerLeave,
//Event called when the gameObject is created.
//Only used for scripting purpose.
GameObjectEvent_OnCreate,
//Event called every frame.
//Only used for scripting purpose.
GameObjectEvent_OnEnterFrame,
//Event called when the player toggles it. (DOWN key)
GameObjectEvent_OnToggle=0x10000,
//Event called when the player switches it on. (DOWN key)
GameObjectEvent_OnSwitchOn=0x10001,
//Event called when the player switches it off. (DOWN key)
GameObjectEvent_OnSwitchOff=0x10002,
};
//The different gameObject properties.
enum GameObjectPropertyType{
//If the player can walk on the gameObject.
GameObjectProperty_PlayerCanWalkOn=1,
//If the object is spiked.
GameObjectProperty_IsSpikes,
//If the gameObject has some flags.
GameObjectProperty_Flags,
};
//The different box types that can be requested using the getBox(int boxType) method.
enum GameObjectBoxType{
//Box of the current position.
BoxType_Current=0,
//Box of the base/start position.
BoxType_Base,
//Box of the previous position.
BoxType_Previous,
//The movement of the block since last position.
BoxType_Delta,
//The velocity for when the player is standing on it.
BoxType_Velocity,
};
//The GameObject class.
class GameObject{
protected:
//The box of the gameObject.
//It's used for the current location of the gameObject and its size.
SDL_Rect box;
//The base location of the game object.
SDL_Rect boxBase;
public:
//The type of the GameObject.
int type;
//Pointer to the Game state.
Game* parent;
//Constructor.
//parent: Pointer to the Game state.
GameObject(Game* parent);
//Destructor.
- ~GameObject();
+ virtual ~GameObject();
//Method used to retrieve a certain box from the GameObject.
//boxType: The type of box that is requested. (default=0)
//Returns: An SDL_Rect.
virtual SDL_Rect getBox(int boxType=0);
//This method is used to place the location on a given location.
//x: The x location to place the gameObject.
//y: The y location to place the gameObject.
virtual void setLocation(int x,int y);
//This method is used to set the base of an object to a given location.
//x: The x location to place the gameObject.
//y: The y location to place the gameObject.
virtual void setBaseLocation(int x,int y);
//This method sets the size of the object to a given size.
//w: The new width of the gameObject.
//h: The new height the gameObject.
virtual void setSize(int w,int h);
//This method sets the size of the base of the object to a given size.
//w: The new width of the gameObject.
//h: The new height of the gameObject.
virtual void setBaseSize(int w,int h);
//Method used to draw the GameObject.
virtual void show()=0;
//Save the state of the GameObject, used for moving blocks, etc.
virtual void saveState();
//Load the state of the GameObject, used for moving blocks, etc.
virtual void loadState();
//Reset the state of the GameObject, used for moving blocks, etc.
//save: Boolean if the saved state should also be reset.
virtual void reset(bool save);
//Play an animation.
virtual void playAnimation();
//Invoke an event of the GameObject.
//eventType: The event type.
virtual void onEvent(int eventType);
//Method used to request certain properties of the GameObject.
//propertyType: The property that is requested.
//obj: Pointer to the player.
virtual int queryProperties(int propertyType,Player* obj);
//Method used to retrieve the additional editor data for the GameObject.
//Used for messages, moving positions, etc...
//obj: Vector containing the editorData pairs. (key, value)
virtual void getEditorData(std::vector<std::pair<std::string,std::string> >& obj);
//Set the editorData.
//obj: Map containing the key/value for the editor data.
virtual void setEditorData(std::map<std::string,std::string>& obj);
//Get a single property of the block.
//property: The property to return.
//Returns: The value for the requested property.
virtual std::string getEditorProperty(std::string property);
//Set a single property of the block.
//property: The property to set.
//value: The new value for the property.
virtual void setEditorProperty(std::string property,std::string value);
//Method for loading the GameObject from a node.
//objNode: Pointer to the storage node to load from.
//Returns: True if it succeeds without errors.
virtual bool loadFromNode(TreeStorageNode* objNode);
//Method that is called before the move method.
//It can be used to reset variables like delta movement and velocity.
virtual void prepareFrame();
//Update method for GameObjects, used for moving blocks.
virtual void move();
};
#endif
diff --git a/src/StatisticsManager.cpp b/src/StatisticsManager.cpp
index 1a010c5..d5d37a5 100644
--- a/src/StatisticsManager.cpp
+++ b/src/StatisticsManager.cpp
@@ -1,774 +1,768 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "StatisticsManager.h"
#include "FileManager.h"
#include "TreeStorageNode.h"
#include "POASerializer.h"
#include "Functions.h"
#include "LevelPackManager.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include "libs/tinyformat/tinyformat.h"
#include <SDL_gfxPrimitives.h>
+#if defined(WIN32)
+#define PRINTF_LONGLONG "%I64d"
+#else
+#define PRINTF_LONGLONG "%lld"
+#endif
+
using namespace std;
StatisticsManager statsMgr;
static const int achievementDisplayTime=(FPS*4500)/1000;
static const int achievementIntervalTime=achievementDisplayTime+(FPS*500)/1000;
#include "AchievementList.h"
static map<string,AchievementInfo*> avaliableAchievements;
//================================================================
StatisticsManager::StatisticsManager(){
bmDropShadow=NULL;
bmQuestionMark=NULL;
bmAchievement=NULL;
startTime=time(NULL);
tutorialLevels=0;
clear();
}
StatisticsManager::~StatisticsManager(){
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
}
void StatisticsManager::clear(){
playerTravelingDistance=shadowTravelingDistance=0.0f;
playerJumps=shadowJumps
=playerDies=shadowDies
=playerSquashed=shadowSquashed
=completedLevels=silverLevels=goldLevels
=recordTimes=switchTimes=swapTimes=saveTimes=loadTimes
=playTime=levelEditTime
=createdLevels=tutorialCompleted=tutorialGold=0;
achievements.clear();
queuedAchievements.clear();
achievementTime=0;
currentAchievement=0;
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
}
#define LOAD_STATS(var,func) { \
vector<string> &v=node.attributes[ #var ]; \
if(!v.empty() && !v[0].empty()) \
var=func(v[0].c_str()); \
}
void StatisticsManager::loadFile(const std::string& fileName){
clear();
ifstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
POASerializer serializer;
if(!serializer.readNode(file,&node,true)) return;
//load statistics
LOAD_STATS(playerTravelingDistance,atof);
LOAD_STATS(shadowTravelingDistance,atof);
LOAD_STATS(playerJumps,atoi);
LOAD_STATS(shadowJumps,atoi);
LOAD_STATS(playerDies,atoi);
LOAD_STATS(shadowDies,atoi);
LOAD_STATS(playerSquashed,atoi);
LOAD_STATS(shadowSquashed,atoi);
LOAD_STATS(recordTimes,atoi);
LOAD_STATS(switchTimes,atoi);
LOAD_STATS(swapTimes,atoi);
LOAD_STATS(saveTimes,atoi);
LOAD_STATS(loadTimes,atoi);
LOAD_STATS(playTime,atoi);
LOAD_STATS(levelEditTime,atoi);
LOAD_STATS(createdLevels,atoi);
//load achievements.
//format is: name;time,name;time,...
{
vector<string> &v=node.attributes["achievements"];
for(unsigned int i=0;i<v.size();i++){
string s=v[i];
time_t t=0;
string::size_type lps=s.find(';');
if(lps!=string::npos){
string s1=s.substr(lps+1);
s=s.substr(0,lps);
long long n;
- sscanf(s1.c_str(),
-#ifdef WIN32
- "%I64d",
-#else
- "%Ld",
-#endif
- &n);
+ sscanf(s1.c_str(),PRINTF_LONGLONG,&n);
t=(time_t)n;
}
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(s);
if(it!=avaliableAchievements.end()){
OwnedAchievement ach={t,it->second};
achievements[it->first]=ach;
}
}
}
}
//Call when level edit is start
void StatisticsManager::startLevelEdit(){
levelEditStartTime=time(NULL);
}
//Call when level edit is end
void StatisticsManager::endLevelEdit(){
levelEditTime+=time(NULL)-levelEditStartTime;
}
//update in-game time
void StatisticsManager::updatePlayTime(){
time_t endTime=time(NULL);
playTime+=endTime-startTime;
startTime=endTime;
}
#define SAVE_STATS(var,pattern) { \
sprintf(s,pattern,var); \
node.attributes[ #var ].push_back(s); \
}
void StatisticsManager::saveFile(const std::string& fileName){
char s[64];
//update in-game time
updatePlayTime();
ofstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
//save statistics
SAVE_STATS(playerTravelingDistance,"%.2f");
SAVE_STATS(shadowTravelingDistance,"%.2f");
SAVE_STATS(playerJumps,"%d");
SAVE_STATS(shadowJumps,"%d");
SAVE_STATS(playerDies,"%d");
SAVE_STATS(shadowDies,"%d");
SAVE_STATS(playerSquashed,"%d");
SAVE_STATS(shadowSquashed,"%d");
SAVE_STATS(recordTimes,"%d");
SAVE_STATS(switchTimes,"%d");
SAVE_STATS(swapTimes,"%d");
SAVE_STATS(saveTimes,"%d");
SAVE_STATS(loadTimes,"%d");
SAVE_STATS(playTime,"%d");
SAVE_STATS(levelEditTime,"%d");
SAVE_STATS(createdLevels,"%d");
//save achievements.
//format is: name;time,name;time,...
{
vector<string>& v=node.attributes["achievements"];
for(map<string,OwnedAchievement>::iterator it=achievements.begin();it!=achievements.end();++it){
stringstream strm;
char s[32];
long long n=it->second.achievedTime;
- sprintf(s,
-#ifdef WIN32
- "%I64d",
-#else
- "%Ld",
-#endif
- n);
+ sprintf(s,PRINTF_LONGLONG,n);
strm<<it->first<<";"<<s;
v.push_back(strm.str());
}
}
POASerializer serializer;
serializer.writeNode(&node,file,true,true);
}
void StatisticsManager::loadPicture(){
//Load drop shadow picture
bmDropShadow=loadImage(getDataPath()+"gfx/dropshadow.png");
bmQuestionMark=loadImage(getDataPath()+"gfx/menu/questionmark.png");
}
void StatisticsManager::registerAchievements(){
if(!avaliableAchievements.empty()) return;
for(int i=0;achievementList[i].id!=NULL;i++){
avaliableAchievements[achievementList[i].id]=&achievementList[i];
if(achievementList[i].imageFile!=NULL){
achievementList[i].imageSurface=loadImage(getDataPath()+achievementList[i].imageFile);
}
}
}
void StatisticsManager::render(){
if(achievementTime==0 && bmAchievement==NULL && currentAchievement<(int)queuedAchievements.size()){
//create surface
bmAchievement=createAchievementSurface(queuedAchievements[currentAchievement++]);
drawGUIBox(0,0,bmAchievement->w,bmAchievement->h,bmAchievement,0xFFFFFF00);
//check if queue is empty
if(currentAchievement>=(int)queuedAchievements.size()){
queuedAchievements.clear();
currentAchievement=0;
}
//play a sound
getSoundManager()->playSound("achievement");
}
//check if we need to display achievements
if(bmAchievement){
achievementTime++;
if(achievementTime<=0){
return;
}else if(achievementTime<=5){
drawAchievement(achievementTime);
}else if(achievementTime<=achievementDisplayTime-5){
drawAchievement(5);
}else if(achievementTime<achievementDisplayTime){
drawAchievement(achievementDisplayTime-achievementTime);
}else if(achievementTime>=achievementIntervalTime){
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
achievementTime=0;
}
}
}
void StatisticsManager::newAchievement(const std::string& id,bool save){
//check avaliable achievements
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(id);
if(it==avaliableAchievements.end()) return;
//check if already have this achievement
if(save){
map<string,OwnedAchievement>::iterator it2=achievements.find(id);
if(it2!=achievements.end()) return;
OwnedAchievement ach={time(NULL),it->second};
achievements[id]=ach;
}
//add it to queue
queuedAchievements.push_back(it->second);
}
float StatisticsManager::getAchievementProgress(AchievementInfo* info){
if(!strcmp(info->id,"experienced")){
return float(completedLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"expert")){
return float(goldLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"tutorial")){
if(tutorialLevels>0)
return float(tutorialCompleted)/float(tutorialLevels)*100.0f;
else
return 0.0f;
}
if(!strcmp(info->id,"tutorialGold")){
if(tutorialLevels>0)
return float(tutorialGold)/float(tutorialLevels)*100.0f;
else
return 0.0f;
}
if(!strcmp(info->id,"create50")){
return float(createdLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"frog")){
return float(playerJumps+shadowJumps)/1000.0f*100.0f;
}
if(!strcmp(info->id,"die50")){
return float(playerDies+shadowDies)/50.0f*100.0f;
}
if(!strcmp(info->id,"die1000")){
return float(playerDies+shadowDies)/1000.0f*100.0f;
}
if(!strcmp(info->id,"suqash50")){
return float(playerSquashed+shadowSquashed)/50.0f*100.0f;
}
if(!strcmp(info->id,"travel100")){
return (playerTravelingDistance+shadowTravelingDistance)/100.0f*100.0f;
}
if(!strcmp(info->id,"travel1k")){
return (playerTravelingDistance+shadowTravelingDistance)/1000.0f*100.0f;
}
if(!strcmp(info->id,"travel10k")){
return (playerTravelingDistance+shadowTravelingDistance)/10000.0f*100.0f;
}
if(!strcmp(info->id,"travel42k")){
return (playerTravelingDistance+shadowTravelingDistance)/42195.0f*100.0f;
}
if(!strcmp(info->id,"record100")){
return float(recordTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"record1k")){
return float(recordTimes)/1000.0f*100.0f;
}
if(!strcmp(info->id,"switch100")){
return float(switchTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"switch1k")){
return float(switchTimes)/1000.0f*100.0f;
}
if(!strcmp(info->id,"swap100")){
return float(swapTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"swap1k")){
return float(swapTimes)/1000.0f*100.0f;
}
//not found
return 0.0f;
}
SDL_Surface* StatisticsManager::createAchievementSurface(AchievementInfo* info,SDL_Surface* surface,SDL_Rect* rect,bool showTip,const time_t *achievedTime){
if(info==NULL || info->id==NULL) return NULL;
//prepare text
SDL_Surface *title0=NULL,*title1=NULL;
vector<SDL_Surface*> descSurfaces;
SDL_Color fg={0,0,0};
int fontHeight=TTF_FontLineSkip(fontText);
bool showDescription=false;
bool showImage=false;
float achievementProgress=0.0f;
if(showTip){
title0=TTF_RenderUTF8_Blended(fontText,_("New achievement:"),fg);
title1=TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg);
showDescription=showImage=true;
}else if(achievedTime){
char s[128];
strftime(s,sizeof(s),"%c",localtime(achievedTime));
stringstream strm;
tinyformat::format(strm,_("Achieved on %s"),s);
title1=TTF_RenderUTF8_Blended(fontText,strm.str().c_str(),fg);
title0=TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg);
showDescription=showImage=true;
}else if(info->displayStyle==ACHIEVEMENT_HIDDEN){
title0=TTF_RenderUTF8_Blended(fontGUISmall,_("Unknown achievement"),fg);
}else{
if(info->displayStyle==ACHIEVEMENT_PROGRESS){
achievementProgress=getAchievementProgress(info);
stringstream strm;
tinyformat::format(strm,_("Achieved %1.0f%%"),achievementProgress);
title1=TTF_RenderUTF8_Blended(fontText,strm.str().c_str(),fg);
}else{
title1=TTF_RenderUTF8_Blended(fontText,_("Not achieved"),fg);
}
title0=TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg);
showDescription= info->displayStyle==ACHIEVEMENT_ALL || info->displayStyle==ACHIEVEMENT_PROGRESS;
showImage=true;
}
if(info->description!=NULL && showDescription){
string description=_(info->description);
string::size_type lps=0,lpe;
for(;;){
lpe=description.find('\n',lps);
if(lpe==string::npos){
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps)+' ').c_str(),fg));
break;
}else{
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps,lpe-lps)+' ').c_str(),fg));
lps=lpe+1;
}
}
}
//calculate the size
int w=0,h=0,w1=8,h1=0;
if(title0!=NULL){
if(title0->w>w) w=title0->w;
h1+=title0->h;
}
if(title1!=NULL){
if(title1->w>w) w=title1->w;
h1+=title1->h;
/*//calc progress bar size
if(!showTip && !achievedTime && info->displayStyle==ACHIEVEMENT_PROGRESS){
h1+=4;
}*/
}
if(showImage){
if(info->imageSurface!=NULL){
w1+=info->r.w+8;
w+=info->r.w+8;
if(info->r.h>h1) h1=info->r.h;
}
}else{
w1+=bmQuestionMark->w+8;
w+=bmQuestionMark->w+8;
if(bmQuestionMark->h>h1) h1=bmQuestionMark->h;
}
h=h1+8;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
if(descSurfaces[i]->w>w) w=descSurfaces[i]->w;
}
}
h+=descSurfaces.size()*fontHeight;
w+=16;
h+=16;
//check if size is specified
int left=0,top=0;
if(rect!=NULL){
if(surface!=NULL){
left=rect->x;
top=rect->y;
}
if(rect->w>0) w=rect->w;
else rect->w=w;
rect->h=h;
}
//create surface if necessary
if(surface==NULL){
surface=SDL_CreateRGBSurface(SCREEN_FLAGS,w,h,
screen->format->BitsPerPixel,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0);
}
//draw background
SDL_Rect r={left,top,w,h};
if(showTip || achievedTime){
SDL_FillRect(surface,&r,SDL_MapRGB(surface->format,255,255,255));
}else{
SDL_FillRect(surface,&r,SDL_MapRGB(surface->format,192,192,192));
}
//draw picture
if(showImage){
if(info->imageSurface!=NULL){
SDL_Rect r={left+8,top+8+(h1-info->r.h)/2,0,0};
SDL_BlitSurface(info->imageSurface,&info->r,surface,&r);
}
}else{
SDL_Rect r={left+8,top+8+(h1-bmQuestionMark->h)/2,0,0};
SDL_BlitSurface(bmQuestionMark,NULL,surface,&r);
}
//draw text
h=8;
if(title0!=NULL){
SDL_Rect r={left+w1,top+h,0,0};
SDL_BlitSurface(title0,NULL,surface,&r);
h+=title0->h;
}
if(title1!=NULL){
SDL_Rect r={left+w1,top+h,0,0};
//Draw progress bar.
if(!showTip && !achievedTime && info->displayStyle==ACHIEVEMENT_PROGRESS){
//Draw borders.
SDL_Rect r1={r.x,r.y,w-8-r.x,title1->h};
drawGUIBox(r1.x,r1.y,r1.w,r1.h,surface,0x1D);
//Draw progress.
r1.x++;
r1.y++;
r1.w=int(achievementProgress/100.0f*float(r1.w));
r1.h-=3;
boxRGBA(surface,r1.x,r1.y,r1.x+r1.w,r1.y+r1.h,0,0,0,100);
//???
r.x+=2;
r.y+=2;
}
//Draw text.
SDL_BlitSurface(title1,NULL,surface,&r);
}
h=h1+16;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
SDL_Rect r={left+8,top+h+i*fontHeight,0,0};
SDL_BlitSurface(descSurfaces[i],NULL,surface,&r);
}
}
//clean up
if(title0) SDL_FreeSurface(title0);
if(title1) SDL_FreeSurface(title1);
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
SDL_FreeSurface(descSurfaces[i]);
}
}
//over
return surface;
}
void StatisticsManager::drawAchievement(int alpha){
if(bmAchievement==NULL) return;
if(alpha<=0) return;
if(alpha>5) alpha=5;
SDL_Rect r={screen->w-32-bmAchievement->w,32,
bmAchievement->w,bmAchievement->h};
//draw the surface
SDL_SetAlpha(bmAchievement,SDL_SRCALPHA,alpha*40);
SDL_BlitSurface(bmAchievement,NULL,screen,&r);
//draw drop shadow - corner
{
int w1=r.w/2,w2=r.w-w1,h1=r.h/2,h2=r.h-h1;
if(w1>16) w1=16;
if(w2>16) w2=16;
if(h1>16) h1=16;
if(h2>16) h2=16;
int x=(5-alpha)*64;
//top-left
SDL_Rect r1={x,0,w1+16,h1+16},r2={r.x-16,r.y-16,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//top-right
r1.x=x+48-w2;r1.w=w2+16;r2.x=r.x+r.w-w2;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom-right
r1.y=48-h2;r1.h=h2+16;r2.y=r.y+r.h-h2;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom-left
r1.x=x;r1.w=w1+16;r2.x=r.x-16;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
}
//draw drop shadow - border
int i=r.w-32;
while(i>0){
int ii=i>128?128:i;
//top
SDL_Rect r1={0,256-alpha*16,ii,16},r2={r.x+r.w-16-i,r.y-16,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom
r1.x=128;r2.y=r.y+r.h;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
i-=ii;
}
i=r.h-32;
while(i>0){
int ii=i>128?128:i;
//top
SDL_Rect r1={512-alpha*16,0,16,ii},r2={r.x-16,r.y+r.h-16-i,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom
r1.y=128;r2.x=r.x+r.w;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
i-=ii;
}
}
void StatisticsManager::reloadCompletedLevelsAndAchievements(){
completedLevels=silverLevels=goldLevels=0;
LevelPackManager *lpm=getLevelPackManager();
vector<pair<string,string> > v=lpm->enumLevelPacks();
bool tutorial=false,tutorialIsGold=false;
for(unsigned int i=0;i<v.size();i++){
string& s=v[i].first;
LevelPack *levels=lpm->getLevelPack(s);
levels->loadProgress();
bool b=false;
if(s==lpm->tutorialLevelPackPath){
tutorialLevels=levels->getLevelCount();
tutorialCompleted=tutorialGold=0;
b=tutorial=tutorialIsGold=true;
}
for(int n=0,m=levels->getLevelCount();n<m;n++){
LevelPack::Level *lv=levels->getLevel(n);
int medal=lv->won;
if(medal){
if(lv->targetTime<0 || lv->time<=lv->targetTime)
medal++;
if(lv->targetRecordings<0 || lv->recordings<=lv->targetRecordings)
medal++;
completedLevels++;
if(b) tutorialCompleted++;
if(medal==2) silverLevels++;
if(medal==3){
goldLevels++;
if(b) tutorialGold++;
}
if(medal!=3 && b) tutorialIsGold=false;
}else if(b){
tutorial=tutorialIsGold=false;
}
}
}
//upadte achievements
updateLevelAchievements();
updateTutorialAchievementsInternal((tutorial?1:0)|(tutorialIsGold?2:0));
}
void StatisticsManager::reloadOtherAchievements(){
int i;
if(playTime>=7200) newAchievement("addicted");
if(playTime>=86400) newAchievement("loyalFan");
if(levelEditTime>=7200) newAchievement("constructor");
if(levelEditTime>=86400) newAchievement("constructor2");
if(createdLevels>=1) newAchievement("create1");
if(createdLevels>=50) newAchievement("create50");
i=playerJumps+shadowJumps;
if(i>=1000) newAchievement("frog");
i=playerDies+shadowDies;
if(i>=1) newAchievement("die1");
if(i>=50) newAchievement("die50");
if(i>=1000) newAchievement("die1000");
i=playerSquashed+shadowSquashed;
if(i>=1) newAchievement("squash1");
if(i>=50) newAchievement("squash50");
float d=playerTravelingDistance+shadowTravelingDistance;
if(d>=100.0f) newAchievement("travel100");
if(d>=1000.0f) newAchievement("travel1k");
if(d>=10000.0f) newAchievement("travel10k");
if(d>=42195.0f) newAchievement("travel42k");
if(recordTimes>=100) newAchievement("record100");
if(recordTimes>=1000) newAchievement("record1k");
if(switchTimes>=100) newAchievement("switch100");
if(switchTimes>=1000) newAchievement("switch1k");
if(swapTimes>=100) newAchievement("swap100");
if(swapTimes>=1000) newAchievement("swap1k");
if(saveTimes>=1000) newAchievement("save1k");
if(loadTimes>=1000) newAchievement("load1k");
if(version.find("Development")!=string::npos) newAchievement("programmer");
}
//Update level specified achievements.
//Make sure the completed level count is correct.
void StatisticsManager::updateLevelAchievements(){
if(completedLevels>=1) newAchievement("newbie");
if(goldLevels>=1) newAchievement("goodjob");
if(completedLevels>=50) newAchievement("experienced");
if(goldLevels>=50) newAchievement("expert");
}
//Update tutorial specified achievements.
//Make sure the level progress of tutorial is correct.
void StatisticsManager::updateTutorialAchievements(){
//find tutorial level pack
LevelPackManager *lpm=getLevelPackManager();
LevelPack *levels=lpm->getTutorialLevelPack();
if(levels==NULL) return;
bool tutorial=true,tutorialIsGold=true;
tutorialLevels=levels->getLevelCount();
tutorialCompleted=tutorialGold=0;
for(int n=0,m=levels->getLevelCount();n<m;n++){
LevelPack::Level *lv=levels->getLevel(n);
int medal=lv->won;
if(medal){
if(lv->targetTime<0 || lv->time<=lv->targetTime)
medal++;
if(lv->targetRecordings<0 || lv->recordings<=lv->targetRecordings)
medal++;
tutorialCompleted++;
if(medal!=3) tutorialIsGold=false;
else tutorialGold++;
}else{
tutorial=tutorialIsGold=false;
break;
}
}
//upadte achievements
updateTutorialAchievementsInternal((tutorial?1:0)|(tutorialIsGold?2:0));
}
//internal function
//flags: a bit-field value indicates which achievements we have.
void StatisticsManager::updateTutorialAchievementsInternal(int flags){
if(flags&1) newAchievement("tutorial");
if(flags&2) newAchievement("tutorialGold");
}
diff --git a/src/libs/tinygettext/language.cpp b/src/libs/tinygettext/language.cpp
index 1b6aac0..561e97d 100644
--- a/src/libs/tinygettext/language.cpp
+++ b/src/libs/tinygettext/language.cpp
@@ -1,568 +1,568 @@
// tinygettext - A gettext replacement that works directly on .po files
// Copyright (C) 2006 Ingo Ruhnke <grumbel@gmx.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "language.hpp"
#include <map>
#include <assert.h>
#include <vector>
namespace tinygettext {
struct LanguageSpec {
/** Language code: "de", "en", ... */
const char* language;
/** Country code: "BR", "DE", ..., can be 0 */
const char* country;
/** Modifier/Varint: "Latn", "ije", "latin"..., can be 0 */
const char* modifier;
/** Language name: "German", "English", "French", ... */
const char* name;
};
/** Language Definitions */
//*{
LanguageSpec languages[] = {
{ "aa", 0, 0, "Afar" },
{ "af", 0, 0, "Afrikaans" },
{ "af", "ZA", 0, "Afrikaans (South Africa)" },
{ "am", 0, 0, "Amharic" },
{ "ar", 0, 0, "Arabic" },
{ "ar", "AR", 0, "Arabic (Argentina)" },
{ "ar", "OM", 0, "Arabic (Oman)" },
{ "ar", "SA", 0, "Arabic (Saudi Arabia)" },
{ "ar", "SY", 0, "Arabic (Syrian Arab Republic)" },
{ "ar", "TN", 0, "Arabic (Tunisia)" },
{ "as", 0, 0, "Assamese" },
{ "ast",0, 0, "Asturian" },
{ "ay", 0, 0, "Aymara" },
{ "az", 0, 0, "Azerbaijani" },
{ "az", "IR", 0, "Azerbaijani (Iran)" },
{ "be", 0, 0, "Belarusian" },
{ "be", 0, "latin", "Belarusian" },
{ "bg", 0, 0, "Bulgarian" },
{ "bg", "BG", 0, "Bulgarian (Bulgaria)" },
{ "bn", 0, 0, "Bengali" },
{ "bn", "BD", 0, "Bengali (Bangladesh)" },
{ "bn", "IN", 0, "Bengali (India)" },
{ "bo", 0, 0, "Tibetan" },
{ "br", 0, 0, "Breton" },
{ "bs", 0, 0, "Bosnian" },
{ "bs", "BA", 0, "Bosnian (Bosnia/Herzegovina)"},
{ "bs", "BS", 0, "Bosnian (Bahamas)" },
{ "ca", "ES", "valencia", "Catalan (valencia)" },
{ "ca", "ES", 0, "Catalan (Spain)" },
{ "ca", 0, "valencia", "Catalan (valencia)" },
{ "ca", 0, 0, "Catalan" },
{ "co", 0, 0, "Corsican" },
{ "cs", 0, 0, "Czech" },
{ "cs", "CZ", 0, "Czech (Czech Republic)" },
{ "cy", 0, 0, "Welsh" },
{ "cy", "GB", 0, "Welsh (Great Britain)" },
{ "cz", 0, 0, "Unknown language" },
{ "da", 0, 0, "Danish" },
{ "da", "DK", 0, "Danish (Denmark)" },
{ "de", 0, 0, "German" },
{ "de", "AT", 0, "German (Austria)" },
{ "de", "CH", 0, "German (Switzerland)" },
{ "de", "DE", 0, "German (Germany)" },
{ "dk", 0, 0, "Unknown language" },
{ "dz", 0, 0, "Dzongkha" },
{ "el", 0, 0, "Greek" },
{ "el", "GR", 0, "Greek (Greece)" },
{ "en", 0, 0, "English" },
{ "en", "AU", 0, "English (Australia)" },
{ "en", "CA", 0, "English (Canada)" },
{ "en", "GB", 0, "English (Great Britain)" },
{ "en", "US", 0, "English (United States)" },
{ "en", "ZA", 0, "English (South Africa)" },
{ "en", 0, "boldquot", "English" },
{ "en", 0, "quot", "English" },
{ "en", "US", "piglatin", "English" },
{ "eo", 0, 0, "Esperanto" },
{ "es", 0, 0, "Spanish" },
{ "es", "AR", 0, "Spanish (Argentina)" },
{ "es", "CL", 0, "Spanish (Chile)" },
{ "es", "CO", 0, "Spanish (Colombia)" },
{ "es", "CR", 0, "Spanish (Costa Rica)" },
{ "es", "DO", 0, "Spanish (Dominican Republic)"},
{ "es", "EC", 0, "Spanish (Ecuador)" },
{ "es", "ES", 0, "Spanish (Spain)" },
{ "es", "GT", 0, "Spanish (Guatemala)" },
{ "es", "HN", 0, "Spanish (Honduras)" },
{ "es", "LA", 0, "Spanish (Laos)" },
{ "es", "MX", 0, "Spanish (Mexico)" },
{ "es", "NI", 0, "Spanish (Nicaragua)" },
{ "es", "PA", 0, "Spanish (Panama)" },
{ "es", "PE", 0, "Spanish (Peru)" },
{ "es", "PR", 0, "Spanish (Puerto Rico)" },
{ "es", "SV", 0, "Spanish (El Salvador)" },
{ "es", "UY", 0, "Spanish (Uruguay)" },
{ "es", "VE", 0, "Spanish (Venezuela)" },
{ "et", 0, 0, "Estonian" },
{ "et", "EE", 0, "Estonian (Estonia)" },
{ "et", "ET", 0, "Estonian (Ethiopia)" },
{ "eu", 0, 0, "Basque" },
{ "eu", "ES", 0, "Basque (Spain)" },
{ "fa", 0, 0, "Persian" },
{ "fa", "AF", 0, "Persian (Afghanistan)" },
{ "fa", "IR", 0, "Persian (Iran)" },
{ "fi", 0, 0, "Finnish" },
{ "fi", "FI", 0, "Finnish (Finland)" },
{ "fo", 0, 0, "Faroese" },
{ "fo", "FO", 0, "Faeroese (Faroe Islands)" },
{ "fr", 0, 0, "French" },
{ "fr", "CA", 0, "French (Canada)" },
{ "fr", "CH", 0, "French (Switzerland)" },
{ "fr", "FR", 0, "French (France)" },
{ "fr", "LU", 0, "French (Luxembourg)" },
{ "fy", 0, 0, "Frisian" },
{ "ga", 0, 0, "Irish" },
{ "gd", 0, 0, "Gaelic Scots" },
{ "gl", 0, 0, "Galician" },
{ "gl", "ES", 0, "Galician (Spain)" },
{ "gn", 0, 0, "Guarani" },
{ "gu", 0, 0, "Gujarati" },
{ "gv", 0, 0, "Manx" },
{ "ha", 0, 0, "Hausa" },
{ "he", 0, 0, "Hebrew" },
{ "he", "IL", 0, "Hebrew (Israel)" },
{ "hi", 0, 0, "Hindi" },
{ "hr", 0, 0, "Croatian" },
{ "hr", "HR", 0, "Croatian (Croatia)" },
{ "hu", 0, 0, "Hungarian" },
{ "hu", "HU", 0, "Hungarian (Hungary)" },
{ "hy", 0, 0, "Armenian" },
{ "ia", 0, 0, "Interlingua" },
{ "id", 0, 0, "Indonesian" },
{ "id", "ID", 0, "Indonesian (Indonesia)" },
{ "is", 0, 0, "Icelandic" },
{ "is", "IS", 0, "Icelandic (Iceland)" },
{ "it", 0, 0, "Italian" },
{ "it", "CH", 0, "Italian (Switzerland)" },
{ "it", "IT", 0, "Italian (Italy)" },
{ "iu", 0, 0, "Inuktitut" },
{ "ja", 0, 0, "Japanese" },
{ "ja", "JP", 0, "Japanese (Japan)" },
{ "ka", 0, 0, "Georgian" },
{ "kk", 0, 0, "Kazakh" },
{ "kl", 0, 0, "Kalaallisut" },
{ "km", 0, 0, "Khmer" },
{ "km", "KH", 0, "Khmer (Cambodia)" },
{ "kn", 0, 0, "Kannada" },
{ "ko", 0, 0, "Korean" },
{ "ko", "KR", 0, "Korean (Korea)" },
{ "ku", 0, 0, "Kurdish" },
{ "kw", 0, 0, "Cornish" },
{ "ky", 0, 0, "Kirghiz" },
{ "la", 0, 0, "Latin" },
{ "lo", 0, 0, "Lao" },
{ "lt", 0, 0, "Lithuanian" },
{ "lt", "LT", 0, "Lithuanian (Lithuania)" },
{ "lv", 0, 0, "Latvian" },
{ "lv", "LV", 0, "Latvian (Latvia)" },
{ "mg", 0, 0, "Malagasy" },
{ "mi", 0, 0, "Maori" },
{ "mk", 0, 0, "Macedonian" },
{ "mk", "MK", 0, "Macedonian (Macedonia)" },
{ "ml", 0, 0, "Malayalam" },
{ "mn", 0, 0, "Mongolian" },
{ "mr", 0, 0, "Marathi" },
{ "ms", 0, 0, "Malay" },
{ "ms", "MY", 0, "Malay (Malaysia)" },
{ "mt", 0, 0, "Maltese" },
{ "my", 0, 0, "Burmese" },
{ "my", "MM", 0, "Burmese (Myanmar)" },
{ "nb", 0, 0, "Norwegian Bokmal" },
- { "nb", "NO", 0, "Norwegian Bokm?l (Norway)" },
+ { "nb", "NO", 0, "Norwegian Bokmテ・l (Norway)" },
{ "ne", 0, 0, "Nepali" },
{ "nl", 0, 0, "Dutch" },
{ "nl", "BE", 0, "Dutch (Belgium)" },
{ "nl", "NL", 0, "Dutch (Netherlands)" },
{ "nn", 0, 0, "Norwegian Nynorsk" },
{ "nn", "NO", 0, "Norwegian Nynorsk (Norway)" },
{ "no", 0, 0, "Norwegian" },
{ "no", "NO", 0, "Norwegian (Norway)" },
{ "no", "NY", 0, "Norwegian (NY)" },
{ "nr", 0, 0, "Ndebele, South" },
{ "oc", 0, 0, "Occitan post 1500" },
{ "om", 0, 0, "Oromo" },
{ "or", 0, 0, "Oriya" },
{ "pa", 0, 0, "Punjabi" },
{ "pl", 0, 0, "Polish" },
{ "pl", "PL", 0, "Polish (Poland)" },
{ "ps", 0, 0, "Pashto" },
{ "pt", 0, 0, "Portuguese" },
{ "pt", "BR", 0, "Brazilian" },
{ "pt", "PT", 0, "Portuguese (Portugal)" },
{ "qu", 0, 0, "Quechua" },
{ "rm", 0, 0, "Rhaeto-Romance" },
{ "ro", 0, 0, "Romanian" },
{ "ro", "RO", 0, "Romanian (Romania)" },
{ "ru", 0, 0, "Russian" },
{ "ru", "RU", 0, "Russian (Russia" },
{ "rw", 0, 0, "Kinyarwanda" },
{ "sa", 0, 0, "Sanskrit" },
{ "sd", 0, 0, "Sindhi" },
{ "se", 0, 0, "Sami" },
{ "se", "NO", 0, "Sami (Norway)" },
{ "si", 0, 0, "Sinhalese" },
{ "sk", 0, 0, "Slovak" },
{ "sk", "SK", 0, "Slovak (Slovakia)" },
{ "sl", 0, 0, "Slovenian" },
{ "sl", "SI", 0, "Slovenian (Slovenia)" },
{ "sl", "SL", 0, "Slovenian (Sierra Leone)" },
{ "sm", 0, 0, "Samoan" },
{ "so", 0, 0, "Somali" },
{ "sp", 0, 0, "Unknown language" },
{ "sq", 0, 0, "Albanian" },
{ "sq", "AL", 0, "Albanian (Albania)" },
{ "sr", 0, 0, "Serbian" },
{ "sr", "YU", 0, "Serbian (Yugoslavia)" },
{ "sr", 0,"ije", "Serbian" },
{ "sr", 0, "latin", "Serbian" },
{ "sr", 0, "Latn", "Serbian" },
{ "ss", 0, 0, "Swati" },
{ "st", 0, 0, "Sotho" },
{ "sv", 0, 0, "Swedish" },
{ "sv", "SE", 0, "Swedish (Sweden)" },
{ "sv", "SV", 0, "Swedish (El Salvador)" },
{ "sw", 0, 0, "Swahili" },
{ "ta", 0, 0, "Tamil" },
{ "te", 0, 0, "Telugu" },
{ "tg", 0, 0, "Tajik" },
{ "th", 0, 0, "Thai" },
{ "th", "TH", 0, "Thai (Thailand)" },
{ "ti", 0, 0, "Tigrinya" },
{ "tk", 0, 0, "Turkmen" },
{ "tl", 0, 0, "Tagalog" },
{ "to", 0, 0, "Tonga" },
{ "tr", 0, 0, "Turkish" },
{ "tr", "TR", 0, "Turkish (Turkey)" },
{ "ts", 0, 0, "Tsonga" },
{ "tt", 0, 0, "Tatar" },
{ "ug", 0, 0, "Uighur" },
{ "uk", 0, 0, "Ukrainian" },
{ "uk", "UA", 0, "Ukrainian (Ukraine)" },
{ "ur", 0, 0, "Urdu" },
{ "ur", "PK", 0, "Urdu (Pakistan)" },
{ "uz", 0, 0, "Uzbek" },
{ "uz", 0, "cyrillic", "Uzbek" },
{ "vi", 0, 0, "Vietnamese" },
{ "vi", "VN", 0, "Vietnamese (Vietnam)" },
{ "wa", 0, 0, "Walloon" },
{ "wo", 0, 0, "Wolof" },
{ "xh", 0, 0, "Xhosa" },
{ "yi", 0, 0, "Yiddish" },
{ "yo", 0, 0, "Yoruba" },
{ "zh", 0, 0, "Chinese" },
{ "zh", "CN", 0, "Chinese (simplified)" },
{ "zh", "HK", 0, "Chinese (Hong Kong)" },
{ "zh", "TW", 0, "Chinese (traditional)" },
{ "zu", 0, 0, "Zulu" },
{ NULL, 0, 0, NULL }
};
//*}
std::string
resolve_language_alias(const std::string& name)
{
typedef std::map<std::string, std::string> Aliases;
static Aliases language_aliases;
if (language_aliases.empty())
{
// FIXME: Many of those are not useful for us, since we leave
// encoding to the app, not to the language, we could/should
// also match against all language names, not just aliases from
// locale.alias
// Aliases taken from /etc/locale.alias
language_aliases["bokmal"] = "nb_NO.ISO-8859-1";
- language_aliases["bokm?l"] = "nb_NO.ISO-8859-1";
+ language_aliases["bokmテ・l"] = "nb_NO.ISO-8859-1";
language_aliases["catalan"] = "ca_ES.ISO-8859-1";
language_aliases["croatian"] = "hr_HR.ISO-8859-2";
language_aliases["czech"] = "cs_CZ.ISO-8859-2";
language_aliases["danish"] = "da_DK.ISO-8859-1";
language_aliases["dansk"] = "da_DK.ISO-8859-1";
language_aliases["deutsch"] = "de_DE.ISO-8859-1";
language_aliases["dutch"] = "nl_NL.ISO-8859-1";
language_aliases["eesti"] = "et_EE.ISO-8859-1";
language_aliases["estonian"] = "et_EE.ISO-8859-1";
language_aliases["finnish"] = "fi_FI.ISO-8859-1";
- language_aliases["fran?ais"] = "fr_FR.ISO-8859-1";
+ language_aliases["franテァais"] = "fr_FR.ISO-8859-1";
language_aliases["french"] = "fr_FR.ISO-8859-1";
language_aliases["galego"] = "gl_ES.ISO-8859-1";
language_aliases["galician"] = "gl_ES.ISO-8859-1";
language_aliases["german"] = "de_DE.ISO-8859-1";
language_aliases["greek"] = "el_GR.ISO-8859-7";
language_aliases["hebrew"] = "he_IL.ISO-8859-8";
language_aliases["hrvatski"] = "hr_HR.ISO-8859-2";
language_aliases["hungarian"] = "hu_HU.ISO-8859-2";
language_aliases["icelandic"] = "is_IS.ISO-8859-1";
language_aliases["italian"] = "it_IT.ISO-8859-1";
language_aliases["japanese"] = "ja_JP.eucJP";
language_aliases["japanese.euc"] = "ja_JP.eucJP";
language_aliases["ja_JP"] = "ja_JP.eucJP";
language_aliases["ja_JP.ujis"] = "ja_JP.eucJP";
language_aliases["japanese.sjis"] = "ja_JP.SJIS";
language_aliases["korean"] = "ko_KR.eucKR";
language_aliases["korean.euc"] = "ko_KR.eucKR";
language_aliases["ko_KR"] = "ko_KR.eucKR";
language_aliases["lithuanian"] = "lt_LT.ISO-8859-13";
language_aliases["no_NO"] = "nb_NO.ISO-8859-1";
language_aliases["no_NO.ISO-8859-1"] = "nb_NO.ISO-8859-1";
language_aliases["norwegian"] = "nb_NO.ISO-8859-1";
language_aliases["nynorsk"] = "nn_NO.ISO-8859-1";
language_aliases["polish"] = "pl_PL.ISO-8859-2";
language_aliases["portuguese"] = "pt_PT.ISO-8859-1";
language_aliases["romanian"] = "ro_RO.ISO-8859-2";
language_aliases["russian"] = "ru_RU.ISO-8859-5";
language_aliases["slovak"] = "sk_SK.ISO-8859-2";
language_aliases["slovene"] = "sl_SI.ISO-8859-2";
language_aliases["slovenian"] = "sl_SI.ISO-8859-2";
language_aliases["spanish"] = "es_ES.ISO-8859-1";
language_aliases["swedish"] = "sv_SE.ISO-8859-1";
language_aliases["thai"] = "th_TH.TIS-620";
language_aliases["turkish"] = "tr_TR.ISO-8859-9";
}
std::string name_lowercase;
name_lowercase.resize(name.size());
for(std::string::size_type i = 0; i < name.size(); ++i)
name_lowercase[i] = static_cast<char>(tolower(name[i]));
Aliases::iterator i = language_aliases.find(name_lowercase);
if (i != language_aliases.end())
{
return i->second;
}
else
{
return name;
}
}
Language
Language::from_spec(const std::string& language, const std::string& country, const std::string& modifier)
{
static std::map<std::string, std::vector<LanguageSpec*> > language_map;
if (language_map.empty())
{ // Init language_map
for(int i = 0; languages[i].language != NULL; ++i)
language_map[languages[i].language].push_back(&languages[i]);
}
std::map<std::string, std::vector<LanguageSpec*> >::iterator i = language_map.find(language);
if (i != language_map.end())
{
std::vector<LanguageSpec*>& lst = i->second;
LanguageSpec tmpspec;
tmpspec.language = language.c_str();
tmpspec.country = country.c_str();
tmpspec.modifier = modifier.c_str();
Language tmplang(&tmpspec);
LanguageSpec* best_match = 0;
int best_match_score = 0;
for(std::vector<LanguageSpec*>::iterator j = lst.begin(); j != lst.end(); ++j)
{ // Search for the language that best matches the given spec, value country more then modifier
int score = Language::match(Language(*j), tmplang);
if (score > best_match_score)
{
best_match = *j;
best_match_score = score;
}
}
assert(best_match);
return Language(best_match);
}
else
{
return Language();
}
}
Language
Language::from_name(const std::string& spec_str)
{
return from_env(resolve_language_alias(spec_str));
}
Language
Language::from_env(const std::string& env)
{
// Split LANGUAGE_COUNTRY.CODESET@MODIFIER into parts
std::string::size_type ln = env.find('_');
std::string::size_type dt = env.find('.');
std::string::size_type at = env.find('@');
std::string language;
std::string country;
std::string codeset;
std::string modifier;
//std::cout << ln << " " << dt << " " << at << std::endl;
language = env.substr(0, std::min(std::min(ln, dt), at));
if (ln != std::string::npos && ln+1 < env.size()) // _
{
country = env.substr(ln+1, (std::min(dt, at) == std::string::npos) ? std::string::npos : std::min(dt, at) - (ln+1));
}
if (dt != std::string::npos && dt+1 < env.size()) // .
{
codeset = env.substr(dt+1, (at == std::string::npos) ? std::string::npos : (at - (dt+1)));
}
if (at != std::string::npos && at+1 < env.size()) // @
{
modifier = env.substr(at+1);
}
return from_spec(language, country, modifier);
}
Language::Language(LanguageSpec* language_spec_)
: language_spec(language_spec_)
{
}
Language::Language()
: language_spec(0)
{
}
int
Language::match(const Language& lhs, const Language& rhs)
{
if (lhs.get_language() != rhs.get_language())
{
return 0;
}
else
{
static int match_tbl[3][3] = {
// modifier match, wildchard, miss
{ 9, 8, 5 }, // country match
{ 7, 6, 3 }, // country wildcard
{ 4, 2, 1 }, // country miss
};
int c;
if (lhs.get_country() == rhs.get_country())
c = 0;
else if (lhs.get_country().empty() || rhs.get_country().empty())
c = 1;
else
c = 2;
int m;
if (lhs.get_modifier() == rhs.get_modifier())
m = 0;
else if (lhs.get_modifier().empty() || rhs.get_modifier().empty())
m = 1;
else
m = 2;
return match_tbl[c][m];
}
}
std::string
Language::get_language() const
{
if (language_spec)
return language_spec->language;
else
return "";
}
std::string
Language::get_country() const
{
if (language_spec && language_spec->country)
return language_spec->country;
else
return "";
}
std::string
Language::get_modifier() const
{
if (language_spec && language_spec->modifier)
return language_spec->modifier;
else
return "";
}
std::string
Language::get_name() const
{
if (language_spec)
return language_spec->name;
else
return "";
}
std::string
Language::str() const
{
if (language_spec)
{
std::string var;
var += language_spec->language;
if (language_spec->country)
{
var += "_";
var += language_spec->country;
}
if (language_spec->modifier)
{
var += "@";
var += language_spec->modifier;
}
return var;
}
else
{
return "";
}
}
bool
Language::operator==(const Language& rhs) const
{
return language_spec == rhs.language_spec;
}
bool
Language::operator!=(const Language& rhs) const
{
return language_spec != rhs.language_spec;
}
} // namespace tinygettext
/* EOF */
diff --git a/src/libs/tinygettext/po_parser.cpp b/src/libs/tinygettext/po_parser.cpp
index f489891..1937f1c 100644
--- a/src/libs/tinygettext/po_parser.cpp
+++ b/src/libs/tinygettext/po_parser.cpp
@@ -1,506 +1,506 @@
// tinygettext - A gettext replacement that works directly on .po files
// Copyright (C) 2009 Ingo Ruhnke <grumbel@gmx.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "po_parser.hpp"
#include <iostream>
#include <ctype.h>
#include <string>
#include <istream>
#include <string.h>
#include <map>
#include <stdlib.h>
#include "language.hpp"
#include "log_stream.hpp"
#include "dictionary.hpp"
#include "plural_forms.hpp"
//FIXME: Because I'm too aggressive, I disabled C++ exceptions when compiling for Android :|
#if defined(ANDROID)
#define NO_EXCEPTIONS
#endif
namespace tinygettext {
bool POParser::pedantic = true;
void
POParser::parse(const std::string& filename, std::istream& in, Dictionary& dict)
{
POParser parser(filename, in, dict);
parser.parse();
}
class POParserError {};
POParser::POParser(const std::string& filename_, std::istream& in_, Dictionary& dict_, bool use_fuzzy_) :
filename(filename_),
in(in_),
dict(dict_),
use_fuzzy(use_fuzzy_),
running(false),
eof(false),
big5(false),
line_number(0),
current_line()//,
//conv()
{
}
POParser::~POParser()
{
}
void
POParser::warning(const std::string& msg)
{
log_warning << filename << ":" << line_number << ": warning: " << msg << ": " << current_line << std::endl;
//log_warning << "Line: " << current_line << std::endl;
}
void
POParser::error(const std::string& msg)
{
log_error << filename << ":" << line_number << ": error: " << msg << ": " << current_line << std::endl;
// Try to recover from an error by searching for start of another entry
do
next_line();
while(!eof && !is_empty_line());
#ifndef NO_EXCEPTIONS
throw POParserError();
#endif
}
void
POParser::next_line()
{
line_number += 1;
if (!std::getline(in, current_line))
eof = true;
}
void
POParser::get_string_line(std::ostringstream& out,unsigned int skip)
{
if (skip+1 >= static_cast<unsigned int>(current_line.size()))
error("unexpected end of line");
if (current_line[skip] != '"')
error("expected start of string '\"'");
std::string::size_type i;
for(i = skip+1; current_line[i] != '\"'; ++i)
{
if (big5 && static_cast<unsigned char>(current_line[i]) >= 0x81 && static_cast<unsigned char>(current_line[i]) <= 0xfe)
{
out << current_line[i];
i += 1;
if (i >= current_line.size())
error("invalid big5 encoding");
out << current_line[i];
}
else if (i >= current_line.size())
{
error("unexpected end of string");
}
else if (current_line[i] == '\\')
{
i += 1;
if (i >= current_line.size())
error("unexpected end of string in handling '\\'");
switch (current_line[i])
{
case 'a': out << '\a'; break;
case 'b': out << '\b'; break;
case 'v': out << '\v'; break;
case 'n': out << '\n'; break;
case 't': out << '\t'; break;
case 'r': out << '\r'; break;
case '"': out << '"'; break;
case '\\': out << '\\'; break;
default:
std::ostringstream err;
err << "unhandled escape '\\" << current_line[i] << "'";
warning(err.str());
out << current_line[i-1] << current_line[i];
break;
}
}
else
{
out << current_line[i];
}
}
// process trailing garbage in line and warn if there is any
for(i = i+1; i < current_line.size(); ++i)
if (!isspace(current_line[i]))
{
warning("unexpected garbage after string ignoren");
break;
}
}
std::string
POParser::get_string(unsigned int skip)
{
std::ostringstream out;
if (skip+1 >= static_cast<unsigned int>(current_line.size()))
error("unexpected end of line");
if (current_line[skip] == ' ' && current_line[skip+1] == '"')
{
get_string_line(out, skip+1);
}
else
{
if (pedantic)
warning("keyword and string must be separated by a single space");
for(;;)
{
if (skip >= static_cast<unsigned int>(current_line.size()))
error("unexpected end of line");
else if (current_line[skip] == '\"')
{
get_string_line(out, skip);
break;
}
else if (!isspace(current_line[skip]))
{
error("string must start with '\"'");
}
else
{
// skip space
}
skip += 1;
}
}
next:
next_line();
for(std::string::size_type i = 0; i < current_line.size(); ++i)
{
if (current_line[i] == '"')
{
if (i == 1)
if (pedantic)
warning("leading whitespace before string");
get_string_line(out, i);
goto next;
}
else if (isspace(current_line[i]))
{
// skip
}
else
{
break;
}
}
return out.str();
}
static bool has_prefix(const std::string& lhs, const std::string rhs)
{
if (lhs.length() < rhs.length())
return false;
else
return lhs.compare(0, rhs.length(), rhs) == 0;
}
void
POParser::parse_header(const std::string& header)
{
std::string from_charset;
std::string::size_type start = 0;
for(std::string::size_type i = 0; i < header.length(); ++i)
{
if (header[i] == '\n')
{
std::string line = header.substr(start, i - start);
if (has_prefix(line, "Content-Type:"))
{
// from_charset = line.substr(len);
unsigned int len = strlen("Content-Type: text/plain; charset=");
if (line.compare(0, len, "Content-Type: text/plain; charset=") == 0)
{
from_charset = line.substr(len);
for(std::string::iterator ch = from_charset.begin(); ch != from_charset.end(); ++ch)
*ch = static_cast<char>(toupper(*ch));
}
else
{
warning("malformed Content-Type header");
}
}
else if (has_prefix(line, "Plural-Forms:"))
{
PluralForms plural_forms = PluralForms::from_string(line);
if (!plural_forms)
{
warning("unknown Plural-Forms given");
}
else
{
if (!dict.get_plural_forms())
{
dict.set_plural_forms(plural_forms);
}
else
{
if (dict.get_plural_forms() != plural_forms)
{
warning("Plural-Forms missmatch between .po file and dictionary");
}
}
}
}
start = i+1;
}
}
if (from_charset.empty() || from_charset == "CHARSET")
{
warning("charset not specified for .po, fallback to utf-8");
from_charset = "UTF-8";
}
else if (from_charset == "BIG5")
{
big5 = true;
}
//conv.set_charsets(from_charset, dict.get_charset());
}
bool
POParser::is_empty_line()
{
if (current_line.empty())
{
return true;
}
else if (current_line[0] == '#')
{ // handle comments as empty lines
if (current_line.size() == 1 || (current_line.size() >= 2 && isspace(current_line[1])))
return true;
else
return false;
}
else
{
for(std::string::iterator i = current_line.begin(); i != current_line.end(); ++i)
{
if (!isspace(*i))
return false;
}
}
return true;
}
bool
POParser::prefix(const char* prefix_str)
{
return current_line.compare(0, strlen(prefix_str), prefix_str) == 0;
}
void
POParser::parse()
{
next_line();
// skip UTF-8 intro that some text editors produce
// see http://en.wikipedia.org/wiki/Byte-order_mark
if (current_line.size() >= 3 &&
- current_line[0] == static_cast<unsigned char>(0xef) &&
- current_line[1] == static_cast<unsigned char>(0xbb) &&
- current_line[2] == static_cast<unsigned char>(0xbf))
+ current_line[0] == static_cast<char>(0xef) &&
+ current_line[1] == static_cast<char>(0xbb) &&
+ current_line[2] == static_cast<char>(0xbf))
{
current_line = current_line.substr(3);
}
// Parser structure
while(!eof)
{
#ifndef NO_EXCEPTIONS
try
{
#endif
bool fuzzy = false;
bool has_msgctxt = false;
std::string msgctxt;
std::string msgid;
while(prefix("#"))
{
if (current_line.size() >= 2 && current_line[1] == ',')
{
// FIXME: Rather simplistic hunt for fuzzy flag
if (current_line.find("fuzzy", 2) != std::string::npos)
fuzzy = true;
}
next_line();
}
if (!is_empty_line())
{
if (prefix("msgctxt"))
{
has_msgctxt = true;
msgctxt = get_string(7);
}
if (prefix("msgid"))
msgid = get_string(5);
else
error("expected 'msgid'");
if (prefix("msgid_plural"))
{
std::string msgid_plural = get_string(12);
std::vector<std::string> msgstr_num;
bool saw_nonempty_msgstr = false;
next:
if (is_empty_line())
{
if (msgstr_num.empty())
error("expected 'msgstr[N] (0 <= N <= 9)'");
}
else if (prefix("msgstr[") &&
current_line.size() > 8 &&
isdigit(current_line[7]) && current_line[8] == ']')
{
unsigned int number = static_cast<unsigned int>(current_line[7] - '0');
std::string msgstr = get_string(9);
if(!msgstr.empty())
saw_nonempty_msgstr = true;
if (number >= msgstr_num.size())
msgstr_num.resize(number+1);
msgstr_num[number] = msgstr; //conv.convert(msgstr);
goto next;
}
else
{
error("expected 'msgstr[N]'");
}
if (!is_empty_line())
error("expected 'msgstr[N]' or empty line");
if (saw_nonempty_msgstr)
{
if (use_fuzzy || !fuzzy)
{
if (!dict.get_plural_forms())
{
warning("msgstr[N] seen, but no Plural-Forms given");
}
else
{
if (msgstr_num.size() != dict.get_plural_forms().get_nplural())
{
warning("msgstr[N] count doesn't match Plural-Forms.nplural");
}
}
if (has_msgctxt)
dict.add_translation(msgctxt, msgid, msgid_plural, msgstr_num);
else
dict.add_translation(msgid, msgid_plural, msgstr_num);
}
if (0)
{
std::cout << (fuzzy?"fuzzy":"not-fuzzy") << std::endl;
std::cout << "msgid \"" << msgid << "\"" << std::endl;
std::cout << "msgid_plural \"" << msgid_plural << "\"" << std::endl;
for(std::vector<std::string>::size_type i = 0; i < msgstr_num.size(); ++i)
std::cout << "msgstr[" << i << "] \"" << msgstr_num[i] /*conv.convert(msgstr_num[i])*/ << "\"" << std::endl;
std::cout << std::endl;
}
}
}
else if (prefix("msgstr"))
{
std::string msgstr = get_string(6);
if (msgid.empty())
{
parse_header(msgstr);
}
else if(!msgstr.empty())
{
if (use_fuzzy || !fuzzy)
{
if (has_msgctxt)
dict.add_translation(msgctxt, msgid, msgstr /*conv.convert(msgstr)*/);
else
dict.add_translation(msgid, msgstr /*conv.convert(msgstr)*/);
}
if (0)
{
std::cout << (fuzzy?"fuzzy":"not-fuzzy") << std::endl;
std::cout << "msgid \"" << msgid << "\"" << std::endl;
std::cout << "msgstr \"" << msgstr /*conv.convert(msgstr)*/ << "\"" << std::endl;
std::cout << std::endl;
}
}
}
else
{
error("expected 'msgstr' or 'msgid_plural'");
}
}
if (!is_empty_line())
error("expected empty line");
next_line();
#ifndef NO_EXCEPTIONS
}
catch(POParserError&)
{
}
#endif
}
}
} // namespace tinygettext
/* EOF */

File Metadata

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

Event Timeline