Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F118841
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
99 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/data/locale/messages.pot b/data/locale/messages.pot
index c1a0e37..feff56a 100644
--- a/data/locale/messages.pot
+++ b/data/locale/messages.pot
@@ -1,2071 +1,2093 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the meandmyshadow package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: meandmyshadow 0.5svn\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-05 01:55+0800\n"
+"POT-Creation-Date: 2018-09-09 16:18+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: ../src/AchievementList.cpp:43
msgid "Newbie"
msgstr ""
#: ../src/AchievementList.cpp:43
msgid "Complete a level."
msgstr ""
#: ../src/AchievementList.cpp:44
msgid "Experienced player"
msgstr ""
#: ../src/AchievementList.cpp:44
msgid "Complete 50 levels."
msgstr ""
#: ../src/AchievementList.cpp:45
msgid "Good job!"
msgstr ""
#: ../src/AchievementList.cpp:45
msgid "Receive a gold medal."
msgstr ""
#: ../src/AchievementList.cpp:46
msgid "Expert"
msgstr ""
#: ../src/AchievementList.cpp:46
msgid "Earn 50 gold medal."
msgstr ""
#: ../src/AchievementList.cpp:48
msgid "Graduate"
msgstr ""
#: ../src/AchievementList.cpp:48
msgid "Complete the tutorial level pack."
msgstr ""
#: ../src/AchievementList.cpp:49
msgid "Outstanding graduate"
msgstr ""
#: ../src/AchievementList.cpp:49
msgid "Complete the tutorial level pack with gold for all levels."
msgstr ""
#: ../src/AchievementList.cpp:51
msgid "Hooked"
msgstr ""
#: ../src/AchievementList.cpp:51
msgid "Play Me and My Shadow for more than 2 hours."
msgstr ""
#: ../src/AchievementList.cpp:52
msgid "Loyal fan of Me and My Shadow"
msgstr ""
#: ../src/AchievementList.cpp:52
msgid "Play Me and My Shadow for more than 24 hours."
msgstr ""
#: ../src/AchievementList.cpp:54
msgid "Constructor"
msgstr ""
#: ../src/AchievementList.cpp:54
msgid "Use the level editor for more than 2 hours."
msgstr ""
#: ../src/AchievementList.cpp:55
msgid "The creator"
msgstr ""
#: ../src/AchievementList.cpp:55
msgid "Use the level editor for more than 24 hours."
msgstr ""
#: ../src/AchievementList.cpp:57
msgid "Look, cute level!"
msgstr ""
#: ../src/AchievementList.cpp:57
msgid "Create a level for the first time."
msgstr ""
#: ../src/AchievementList.cpp:58
msgid "The level museum"
msgstr ""
#: ../src/AchievementList.cpp:58
msgid "Create 50 levels."
msgstr ""
#: ../src/AchievementList.cpp:60
msgid "Hello, World!"
msgstr ""
#: ../src/AchievementList.cpp:60
msgid "Write a script for the first time."
msgstr ""
#: ../src/AchievementList.cpp:62
msgid "Frog"
msgstr ""
#: ../src/AchievementList.cpp:62
msgid "Jump 1000 times."
msgstr ""
#: ../src/AchievementList.cpp:64
msgid "Wanderer"
msgstr ""
#: ../src/AchievementList.cpp:64
msgid "Travel 100 meters."
msgstr ""
#: ../src/AchievementList.cpp:65
msgid "Runner"
msgstr ""
#: ../src/AchievementList.cpp:65
msgid "Travel 1 kilometer."
msgstr ""
#: ../src/AchievementList.cpp:66
msgid "Long distance runner"
msgstr ""
#: ../src/AchievementList.cpp:66
msgid "Travel 10 kilometers."
msgstr ""
#: ../src/AchievementList.cpp:67
msgid "Marathon runner"
msgstr ""
#: ../src/AchievementList.cpp:67
msgid "Travel 42,195 meters."
msgstr ""
#: ../src/AchievementList.cpp:69
msgid "Be careful!"
msgstr ""
#: ../src/AchievementList.cpp:69
msgid "Die for the first time."
msgstr ""
#: ../src/AchievementList.cpp:70
msgid "It doesn't matter..."
msgstr ""
#: ../src/AchievementList.cpp:70
msgid "Die 50 times."
msgstr ""
#: ../src/AchievementList.cpp:71
msgid "Expert of trial and error"
msgstr ""
#: ../src/AchievementList.cpp:71
msgid "Die 1000 times."
msgstr ""
#: ../src/AchievementList.cpp:73
msgid "Keep an eye for moving blocks!"
msgstr ""
#: ../src/AchievementList.cpp:73
msgid "Get squashed for the first time."
msgstr ""
#: ../src/AchievementList.cpp:74
msgid "Potato masher"
msgstr ""
#: ../src/AchievementList.cpp:74
msgid "Get squashed 50 times."
msgstr ""
#: ../src/AchievementList.cpp:76
msgid "Double kill"
msgstr ""
#: ../src/AchievementList.cpp:76
msgid "Get both the player and the shadow dead."
msgstr ""
#: ../src/AchievementList.cpp:78
msgid "Bad luck"
msgstr ""
#: ../src/AchievementList.cpp:78
msgid "Die 5 times in under 5 seconds."
msgstr ""
#: ../src/AchievementList.cpp:79
msgid "This level is too dangerous"
msgstr ""
#: ../src/AchievementList.cpp:79
msgid "Die 10 times in under 5 seconds."
msgstr ""
#: ../src/AchievementList.cpp:81
msgid "You forgot your friend"
msgstr ""
#: ../src/AchievementList.cpp:81
msgid "Finish the level with the player or the shadow dead."
msgstr ""
#: ../src/AchievementList.cpp:82
msgid "Just in time"
msgstr ""
#: ../src/AchievementList.cpp:82
msgid "Reach the exit with the player and the shadow simultaneously."
msgstr ""
#: ../src/AchievementList.cpp:84
msgid "Recorder"
msgstr ""
#: ../src/AchievementList.cpp:84
msgid "Record 100 times."
msgstr ""
#: ../src/AchievementList.cpp:85
msgid "Shadowmaster"
msgstr ""
#: ../src/AchievementList.cpp:85
msgid "Record 1000 times."
msgstr ""
#: ../src/AchievementList.cpp:87
msgid "Switch puller"
msgstr ""
#: ../src/AchievementList.cpp:87
msgid "Pull the switch 100 times."
msgstr ""
#: ../src/AchievementList.cpp:88
msgid "The switch is broken!"
msgstr ""
#: ../src/AchievementList.cpp:88
msgid "Pull the switch 1000 times."
msgstr ""
#: ../src/AchievementList.cpp:90
msgid "Swapper"
msgstr ""
#: ../src/AchievementList.cpp:90
msgid "Swap 100 times."
msgstr ""
#: ../src/AchievementList.cpp:91
msgid "Player to shadow to player to shadow..."
msgstr ""
#: ../src/AchievementList.cpp:91
msgid "Swap 1000 times."
msgstr ""
#: ../src/AchievementList.cpp:93
msgid "Play it save"
msgstr ""
#: ../src/AchievementList.cpp:93
msgid "Save 1000 times."
msgstr ""
#: ../src/AchievementList.cpp:94
msgid "This game is too hard"
msgstr ""
#: ../src/AchievementList.cpp:94
msgid "Load the game 1000 times."
msgstr ""
#: ../src/AchievementList.cpp:96
msgid "No, thanks"
msgstr ""
#: ../src/AchievementList.cpp:96
msgid "Complete a level with checkpoint, but without saving."
msgstr ""
#: ../src/AchievementList.cpp:98
msgid "Panic save"
msgstr ""
#: ../src/AchievementList.cpp:98
msgid "Save twice in 1 second."
msgstr ""
#: ../src/AchievementList.cpp:99
msgid "Panic load"
msgstr ""
#: ../src/AchievementList.cpp:99
msgid "Load twice in 1 second."
msgstr ""
#: ../src/AchievementList.cpp:101
msgid "Bad saving position"
msgstr ""
#: ../src/AchievementList.cpp:101
msgid "Load the game and die within 1 second."
msgstr ""
#: ../src/AchievementList.cpp:102
msgid "This level is too hard"
msgstr ""
#: ../src/AchievementList.cpp:102
msgid "Load the same save and die 100 times."
msgstr ""
#: ../src/AchievementList.cpp:104
msgid "Quick swap"
msgstr ""
#: ../src/AchievementList.cpp:104
msgid "Swap twice in under a second."
msgstr ""
#: ../src/AchievementList.cpp:107
msgid "Horizontal confusion"
msgstr ""
#: ../src/AchievementList.cpp:107
msgid "Press left and right simultaneously."
msgstr ""
#: ../src/AchievementList.cpp:109
msgid "Cheater"
msgstr ""
#: ../src/AchievementList.cpp:109
msgid "Cheat in game."
msgstr ""
#: ../src/AchievementList.cpp:111
msgid "Programmer"
msgstr ""
#: ../src/AchievementList.cpp:111
msgid "Play the development version of Me and My Shadow."
msgstr ""
#: ../src/Addons.cpp:44 ../src/LevelPackManager.cpp:108
msgid "Levels"
msgstr ""
#: ../src/Addons.cpp:44
msgid "Single level which usually contain demanding puzzles"
msgstr ""
#: ../src/Addons.cpp:45
msgid "Levelpacks"
msgstr ""
#: ../src/Addons.cpp:45
msgid "Collection of levels with the same author or style"
msgstr ""
#: ../src/Addons.cpp:46
msgid "Themes"
msgstr ""
#: ../src/Addons.cpp:46
msgid "Give every block and background a new look and feel"
msgstr ""
#: ../src/Addons.cpp:55 ../src/TitleMenu.cpp:46
msgid "Addons"
msgstr ""
#: ../src/Addons.cpp:87
msgid "Unable to initialize addon menu:"
msgstr ""
#: ../src/Addons.cpp:95 ../src/Addons.cpp:158 ../src/Addons.cpp:662
#: ../src/Addons.cpp:690 ../src/CreditsMenu.cpp:89 ../src/LevelSelect.cpp:168
#: ../src/StatisticsScreen.cpp:159
msgid "Back"
msgstr ""
#: ../src/Addons.cpp:169
msgid "ERROR: unable to download addons file!"
msgstr ""
# TRANSLATORS: addon_list is the name of a file and should not be translated.
#: ../src/Addons.cpp:182
msgid "ERROR: unable to load addon_list file!"
msgstr ""
#: ../src/Addons.cpp:193
msgid "ERROR: Invalid file format of addons file!"
msgstr ""
#: ../src/Addons.cpp:205
msgid "ERROR: Addon list version is unsupported!"
msgstr ""
# TRANSLATORS: installed_addons is the name of a file and should not be translated.
#: ../src/Addons.cpp:226
msgid "ERROR: Unable to create the installed_addons file."
msgstr ""
#: ../src/Addons.cpp:238
msgid "ERROR: Invalid file format of the installed_addons!"
msgstr ""
# TRANSLATORS: indicates the author of an addon.
#: ../src/Addons.cpp:389 ../src/Addons.cpp:621
#, c-format
msgid "by %s"
msgstr ""
#: ../src/Addons.cpp:397
msgid "Installed"
msgstr ""
#: ../src/Addons.cpp:402
msgid "Updatable"
msgstr ""
#: ../src/Addons.cpp:412
msgid "Not installed"
msgstr ""
#: ../src/Addons.cpp:625
#, c-format
msgid "Version: %d\n"
msgstr ""
#: ../src/Addons.cpp:627
#, c-format
msgid "Installed version: %d\n"
msgstr ""
#: ../src/Addons.cpp:630
#, c-format
msgid "License: %s\n"
msgstr ""
#: ../src/Addons.cpp:633
#, c-format
msgid "Website: %s\n"
msgstr ""
#: ../src/Addons.cpp:637
msgid "(No descriptions provided)"
msgstr ""
#: ../src/Addons.cpp:657 ../src/Addons.cpp:684
msgid "Remove"
msgstr ""
#: ../src/Addons.cpp:673
msgid "Update"
msgstr ""
#: ../src/Addons.cpp:679
msgid "Install"
msgstr ""
#: ../src/Addons.cpp:774
#, c-format
msgid "This addon can't be removed because it's needed by %s."
msgstr ""
#: ../src/Addons.cpp:774 ../src/Addons.cpp:1051
msgid "Dependency"
msgstr ""
#: ../src/Addons.cpp:803
#, c-format
msgid "WARNING: File '%s' appears to have been removed already."
msgstr ""
#: ../src/Addons.cpp:803 ../src/Addons.cpp:810 ../src/Addons.cpp:818
#: ../src/Addons.cpp:825 ../src/Addons.cpp:834 ../src/Addons.cpp:840
#: ../src/Addons.cpp:859 ../src/Addons.cpp:866 ../src/Addons.cpp:893
#: ../src/Addons.cpp:900 ../src/Addons.cpp:907 ../src/Addons.cpp:918
#: ../src/Addons.cpp:947 ../src/Addons.cpp:952 ../src/Addons.cpp:962
#: ../src/Addons.cpp:968 ../src/Addons.cpp:981 ../src/Addons.cpp:986
#: ../src/Addons.cpp:1008 ../src/Addons.cpp:1014 ../src/Addons.cpp:1044
msgid "Addon error"
msgstr ""
#: ../src/Addons.cpp:810
#, c-format
msgid "ERROR: Unable to remove file '%s'!"
msgstr ""
#: ../src/Addons.cpp:818
#, c-format
msgid "WARNING: Directory '%s' appears to have been removed already."
msgstr ""
#: ../src/Addons.cpp:825
#, c-format
msgid "ERROR: Unable to remove directory '%s'!"
msgstr ""
#: ../src/Addons.cpp:834
#, c-format
msgid "WARNING: Level '%s' appears to have been removed already."
msgstr ""
#: ../src/Addons.cpp:840
#, c-format
msgid "ERROR: Unable to remove level '%s'!"
msgstr ""
#: ../src/Addons.cpp:859
#, c-format
msgid "WARNING: Levelpack directory '%s' appears to have been removed already."
msgstr ""
#: ../src/Addons.cpp:866
#, c-format
msgid "ERROR: Unable to remove levelpack directory '%s'!"
msgstr ""
#: ../src/Addons.cpp:893
#, c-format
msgid "ERROR: Unable to download addon file %s."
msgstr ""
#: ../src/Addons.cpp:900
#, c-format
msgid "ERROR: Unable to extract addon file %s."
msgstr ""
#: ../src/Addons.cpp:907
msgid "ERROR: Addon is missing metadata!"
msgstr ""
#: ../src/Addons.cpp:918
msgid "ERROR: Invalid file format for metadata file!"
msgstr ""
#: ../src/Addons.cpp:947
#, c-format
msgid "WARNING: File '%s' already exists, addon may be broken or not working!"
msgstr ""
#: ../src/Addons.cpp:952
#, c-format
msgid ""
"WARNING: Unable to copy file '%s' to '%s', addon may be broken or not "
"working!"
msgstr ""
#: ../src/Addons.cpp:962
#, c-format
msgid ""
"WARNING: Destination directory '%s' already exists, addon may be broken or "
"not working!"
msgstr ""
#: ../src/Addons.cpp:968 ../src/Addons.cpp:1014
#, c-format
msgid ""
"WARNING: Unable to move directory '%s' to '%s', addon may be broken or not "
"working!"
msgstr ""
#: ../src/Addons.cpp:981
#, c-format
msgid "WARNING: Level '%s' already exists, addon may be broken or not working!"
msgstr ""
#: ../src/Addons.cpp:986
#, c-format
msgid ""
"WARNING: Unable to copy level '%s' to '%s', addon may be broken or not "
"working!"
msgstr ""
#: ../src/Addons.cpp:1008
#, c-format
msgid ""
"WARNING: Levelpack directory '%s' already exists, addon may be broken or not "
"working!"
msgstr ""
#: ../src/Addons.cpp:1044
#, c-format
msgid "ERROR: Addon requires another addon (%s) which can't be found!"
msgstr ""
#: ../src/Addons.cpp:1051
#, c-format
msgid "The addon %s is needed and will be installed now."
msgstr ""
#: ../src/Block.cpp:822 ../src/LevelEditor.cpp:265
msgid "On"
msgstr ""
#: ../src/Block.cpp:823 ../src/LevelEditor.cpp:266
msgid "Off"
msgstr ""
#: ../src/CommandManager.cpp:41
#, c-format
msgid "Undo %s"
msgstr ""
#: ../src/CommandManager.cpp:43
msgid "Can't undo"
msgstr ""
#: ../src/CommandManager.cpp:49
#, c-format
msgid "Redo %s"
msgstr ""
#: ../src/CommandManager.cpp:51
msgid "Can't redo"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/Commands.cpp:190
msgid "Resize level"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/Commands.cpp:807
msgid "Modify level property"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/Commands.cpp:919
#, c-format
msgid "Add scenery layer %s"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/Commands.cpp:921
#, c-format
msgid "Delete scenery layer %s"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/Commands.cpp:951
#, c-format
msgid "Modify the property of scenery layer %s"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/Commands.cpp:1040
#, c-format
msgid "Move %d object from layer %s to layer %s"
msgid_plural "Move %d objects from layer %s to layer %s"
msgstr[0] ""
msgstr[1] ""
#: ../src/CreditsMenu.cpp:35 ../src/TitleMenu.cpp:53
msgid "Credits"
msgstr ""
# TRANSLATORS: Font used in GUI:
# - Use "knewave" for languages using Latin and Latin-derived alphabets
# - "DroidSansFallback" can be used for non-Latin writing systems
#: ../src/Functions.cpp:569 ../src/Functions.cpp:570 ../src/Functions.cpp:571
#: ../src/Functions.cpp:588
msgid "knewave"
msgstr ""
# TRANSLATORS: Font used for normal text:
# - Use "Blokletters-Viltstift" for languages using Latin and Latin-derived alphabets
# - "DroidSansFallback" can be used for non-Latin writing systems
#: ../src/Functions.cpp:575
msgid "Blokletters-Viltstift"
msgstr ""
#: ../src/Functions.cpp:674
msgid "Loading..."
msgstr ""
#: ../src/Functions.cpp:1243 ../src/Functions.cpp:1270
#: ../src/LevelEditor.cpp:559 ../src/LevelEditor.cpp:693
#: ../src/LevelEditor.cpp:758 ../src/LevelEditor.cpp:821
#: ../src/LevelEditor.cpp:908 ../src/LevelEditor.cpp:1033
#: ../src/LevelEditor.cpp:1083 ../src/LevelEditor.cpp:1180
#: ../src/LevelEditor.cpp:1244 ../src/LevelEditor.cpp:2923
#: ../src/LevelEditSelect.cpp:244 ../src/LevelEditSelect.cpp:277
#: ../src/LevelEditSelect.cpp:317
msgid "OK"
msgstr ""
#: ../src/Functions.cpp:1244 ../src/Functions.cpp:1256
#: ../src/Functions.cpp:1266 ../src/LevelEditor.cpp:565
#: ../src/LevelEditor.cpp:699 ../src/LevelEditor.cpp:764
#: ../src/LevelEditor.cpp:827 ../src/LevelEditor.cpp:914
#: ../src/LevelEditor.cpp:1039 ../src/LevelEditor.cpp:1089
#: ../src/LevelEditor.cpp:1186 ../src/LevelEditor.cpp:1250
#: ../src/LevelEditor.cpp:2929 ../src/LevelEditSelect.cpp:248
#: ../src/LevelEditSelect.cpp:281 ../src/LevelEditSelect.cpp:321
#: ../src/OptionsMenu.cpp:289
msgid "Cancel"
msgstr ""
#: ../src/Functions.cpp:1248
msgid "Abort"
msgstr ""
#: ../src/Functions.cpp:1249 ../src/Functions.cpp:1265
msgid "Retry"
msgstr ""
#: ../src/Functions.cpp:1250
msgid "Ignore"
msgstr ""
#: ../src/Functions.cpp:1254 ../src/Functions.cpp:1260
msgid "Yes"
msgstr ""
#: ../src/Functions.cpp:1255 ../src/Functions.cpp:1261
msgid "No"
msgstr ""
# TRANSLATORS: Please do not remove %s or %d from your translation:
# - %d means the level number in a levelpack
# - %s means the name of current level
#: ../src/Game.cpp:280 ../src/Game.cpp:1236
#, c-format
msgid "Level %d %s"
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with current action key
#: ../src/Game.cpp:915
#, c-format
msgid "Press %s key to save the game."
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with current action key
#: ../src/Game.cpp:920
#, c-format
msgid "Press %s key to swap the position of player and shadow."
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with current action key
#: ../src/Game.cpp:925
#, c-format
msgid "Press %s key to activate the switch."
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with current action key
#: ../src/Game.cpp:930
#, c-format
msgid "Press %s key to teleport."
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - first %s means currently configured key to restart game
# - Second %s means configured key to load from last save
#: ../src/Game.cpp:972
#, c-format
msgid "Press %s to restart current level or press %s to load the game."
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with currently configured key to restart game
#: ../src/Game.cpp:983
#, c-format
msgid "Press %s to restart current level."
msgstr ""
#: ../src/Game.cpp:996
msgid "Your shadow has died."
msgstr ""
#: ../src/Game.cpp:1052
#, c-format
msgid "%d recording"
msgid_plural "%d recordings"
msgstr[0] ""
msgstr[1] ""
#: ../src/Game.cpp:1224
msgid "You've finished:"
msgstr ""
# TRANSLATORS: Please do not remove %-.2f from your translation:
# - %-.2f means time in seconds
# - s is shortened form of a second. Try to keep it so.
#: ../src/Game.cpp:1291
#, c-format
msgid "Time: %-.2fs"
msgstr ""
# TRANSLATORS: Please do not remove %-.2f from your translation:
# - %-.2f means time in seconds
# - s is shortened form of a second. Try to keep it so.
#: ../src/Game.cpp:1300
#, c-format
msgid "Best time: %-.2fs"
msgstr ""
#: ../src/Game.cpp:1311
#, c-format
msgid "Target time: %-.2fs"
msgstr ""
# TRANSLATORS: Please do not remove %d from your translation:
# - %d means the number of recordings user has made
#: ../src/Game.cpp:1332
#, c-format
msgid "Recordings: %d"
msgstr ""
# TRANSLATORS: Please do not remove %d from your translation:
# - %d means the number of recordings user has made
#: ../src/Game.cpp:1340
#, c-format
msgid "Best recordings: %d"
msgstr ""
#: ../src/Game.cpp:1350
#, c-format
msgid "Target recordings: %d"
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with name of a prize medal (gold, silver or bronze)
#: ../src/Game.cpp:1363
#, c-format
msgid "You earned the %s medal"
msgstr ""
#: ../src/Game.cpp:1363
msgid "GOLD"
msgstr ""
#: ../src/Game.cpp:1363
msgid "SILVER"
msgstr ""
#: ../src/Game.cpp:1363
msgid "BRONZE"
msgstr ""
# TRANSLATORS: used as return to the level selector menu
#: ../src/Game.cpp:1390
msgid "Menu"
msgstr ""
# TRANSLATORS: used as restart level
#: ../src/Game.cpp:1397 ../src/InputManager.cpp:47
msgid "Restart"
msgstr ""
# TRANSLATORS: used as next level
#: ../src/Game.cpp:1404
msgid "Next"
msgstr ""
#: ../src/Game.cpp:1430
msgid "Game replay is done."
msgstr ""
#: ../src/Game.cpp:1430
msgid "Game Replay"
msgstr ""
#: ../src/Game.cpp:1767 ../src/Game.cpp:1769
msgid "Congratulations"
msgstr ""
#: ../src/Game.cpp:1769
msgid "You have finished the levelpack!"
msgstr ""
#: ../src/InputManager.cpp:46
msgid "Up (in menu)"
msgstr ""
#: ../src/InputManager.cpp:46
msgid "Down (in menu)"
msgstr ""
#: ../src/InputManager.cpp:46
msgid "Left"
msgstr ""
#: ../src/InputManager.cpp:46
msgid "Right"
msgstr ""
#: ../src/InputManager.cpp:46
msgid "Jump"
msgstr ""
#: ../src/InputManager.cpp:46
msgid "Action"
msgstr ""
#: ../src/InputManager.cpp:46
msgid "Space (Record)"
msgstr ""
#: ../src/InputManager.cpp:46
msgid "Cancel recording"
msgstr ""
#: ../src/InputManager.cpp:47
msgid "Escape"
msgstr ""
#: ../src/InputManager.cpp:47
msgid "Tab (View shadow/Level prop.)"
msgstr ""
#: ../src/InputManager.cpp:47
msgid "Save game (in editor)"
msgstr ""
#: ../src/InputManager.cpp:47
msgid "Load game"
msgstr ""
#: ../src/InputManager.cpp:47
msgid "Swap (in editor)"
msgstr ""
#: ../src/InputManager.cpp:48
msgid "Teleport (in editor)"
msgstr ""
#: ../src/InputManager.cpp:48
msgid "Suicide (in editor)"
msgstr ""
#: ../src/InputManager.cpp:48
msgid "Shift (in editor)"
msgstr ""
#: ../src/InputManager.cpp:48
msgid "Next block type (in Editor)"
msgstr ""
#: ../src/InputManager.cpp:49
msgid "Previous block type (in editor)"
msgstr ""
#: ../src/InputManager.cpp:49
msgid "Select (in menu)"
msgstr ""
# TRANSLAOTRS: This is used when the name of the key code is not found.
#: ../src/InputManager.cpp:156
#, c-format
msgid "(Key %d)"
msgstr ""
#: ../src/InputManager.cpp:163
#, c-format
msgid "Joystick axis %d %s"
msgstr ""
#: ../src/InputManager.cpp:166
#, c-format
msgid "Joystick button %d"
msgstr ""
#: ../src/InputManager.cpp:171
#, c-format
msgid "Joystick hat %d left"
msgstr ""
#: ../src/InputManager.cpp:174
#, c-format
msgid "Joystick hat %d right"
msgstr ""
#: ../src/InputManager.cpp:177
#, c-format
msgid "Joystick hat %d up"
msgstr ""
#: ../src/InputManager.cpp:180
#, c-format
msgid "Joystick hat %d down"
msgstr ""
# TRANSLAOTRS: This is used when the JOYSTICK_HAT value is invalid.
#: ../src/InputManager.cpp:185
#, c-format
msgid "Joystick hat %d %d"
msgstr ""
#: ../src/InputManager.cpp:202
msgid "OR"
msgstr ""
#: ../src/InputManager.cpp:416
msgid "Select an item and press a key to change it."
msgstr ""
#: ../src/InputManager.cpp:419
msgid "Press backspace to clear the selected item."
msgstr ""
#: ../src/LevelEditor.cpp:56
msgid "Block"
msgstr ""
#: ../src/LevelEditor.cpp:56
msgid "Player Start"
msgstr ""
#: ../src/LevelEditor.cpp:56
msgid "Shadow Start"
msgstr ""
#: ../src/LevelEditor.cpp:57
msgid "Exit"
msgstr ""
#: ../src/LevelEditor.cpp:57
msgid "Shadow Block"
msgstr ""
#: ../src/LevelEditor.cpp:57
msgid "Spikes"
msgstr ""
#: ../src/LevelEditor.cpp:58
msgid "Checkpoint"
msgstr ""
#: ../src/LevelEditor.cpp:58 ../src/LevelEditSelect.cpp:312
msgid "Swap"
msgstr ""
#: ../src/LevelEditor.cpp:58
msgid "Fragile"
msgstr ""
#: ../src/LevelEditor.cpp:59
msgid "Moving Block"
msgstr ""
#: ../src/LevelEditor.cpp:59
msgid "Moving Shadow Block"
msgstr ""
#: ../src/LevelEditor.cpp:59
msgid "Moving Spikes"
msgstr ""
#: ../src/LevelEditor.cpp:60
msgid "Teleporter"
msgstr ""
#: ../src/LevelEditor.cpp:60
msgid "Button"
msgstr ""
#: ../src/LevelEditor.cpp:60
msgid "Switch"
msgstr ""
#: ../src/LevelEditor.cpp:61
msgid "Conveyor Belt"
msgstr ""
#: ../src/LevelEditor.cpp:61
msgid "Shadow Conveyor Belt"
msgstr ""
#: ../src/LevelEditor.cpp:61
msgid "Notification Block"
msgstr ""
#: ../src/LevelEditor.cpp:61
msgid "Collectable"
msgstr ""
#: ../src/LevelEditor.cpp:61
msgid "Pushable"
msgstr ""
#: ../src/LevelEditor.cpp:65 ../src/LevelEditor.cpp:310
msgid "Select"
msgstr ""
#: ../src/LevelEditor.cpp:65
msgid "Add"
msgstr ""
#: ../src/LevelEditor.cpp:65 ../src/LevelEditor.cpp:311
msgid "Delete"
msgstr ""
#: ../src/LevelEditor.cpp:65 ../src/LevelPlaySelect.cpp:66
#: ../src/TitleMenu.cpp:43
msgid "Play"
msgstr ""
#: ../src/LevelEditor.cpp:65 ../src/LevelEditor.cpp:2852
msgid "Level settings"
msgstr ""
#: ../src/LevelEditor.cpp:65
msgid "Save level"
msgstr ""
#: ../src/LevelEditor.cpp:65
msgid "Back to menu"
msgstr ""
#: ../src/LevelEditor.cpp:65
msgid "Configure"
msgstr ""
#: ../src/LevelEditor.cpp:84
#, c-format
msgid "%s (Scenery)"
msgstr ""
#: ../src/LevelEditor.cpp:267
msgid "Toggle"
msgstr ""
#: ../src/LevelEditor.cpp:270
msgid "Complete"
msgstr ""
#: ../src/LevelEditor.cpp:271
msgid "One step"
msgstr ""
#: ../src/LevelEditor.cpp:272
msgid "Two steps"
msgstr ""
#: ../src/LevelEditor.cpp:273
msgid "Gone"
msgstr ""
#: ../src/LevelEditor.cpp:291
msgid "Negative infinity"
msgstr ""
#: ../src/LevelEditor.cpp:293
msgid "Zero"
msgstr ""
#: ../src/LevelEditor.cpp:295
msgid "Level size"
msgstr ""
#: ../src/LevelEditor.cpp:297
msgid "Positive infinity"
msgstr ""
#: ../src/LevelEditor.cpp:299
msgid "Default"
msgstr ""
#: ../src/LevelEditor.cpp:308
msgid "Deselect"
msgstr ""
#: ../src/LevelEditor.cpp:318 ../src/LevelEditor.cpp:1136
#, c-format
msgid "Horizontal repeat start: %s"
msgstr ""
#: ../src/LevelEditor.cpp:320 ../src/LevelEditor.cpp:1137
#, c-format
msgid "Horizontal repeat end: %s"
msgstr ""
#: ../src/LevelEditor.cpp:322 ../src/LevelEditor.cpp:1138
#, c-format
msgid "Vertical repeat start: %s"
msgstr ""
#: ../src/LevelEditor.cpp:324 ../src/LevelEditor.cpp:1139
#, c-format
msgid "Vertical repeat end: %s"
msgstr ""
#: ../src/LevelEditor.cpp:329 ../src/LevelEditor.cpp:1150
msgid "Custom scenery"
msgstr ""
#: ../src/LevelEditor.cpp:335 ../src/LevelEditor.cpp:600
#: ../src/LevelEditor.cpp:602
msgid "Visible"
msgstr ""
#: ../src/LevelEditor.cpp:344
msgid "Link"
msgstr ""
#: ../src/LevelEditor.cpp:345
msgid "Remove Links"
msgstr ""
#: ../src/LevelEditor.cpp:349 ../src/LevelEditor.cpp:624
#: ../src/LevelEditor.cpp:626
msgid "Automatic"
msgstr ""
#: ../src/LevelEditor.cpp:359 ../src/LevelEditor.cpp:649
#, c-format
msgid "Behavior: %s"
msgstr ""
#: ../src/LevelEditor.cpp:362
msgid "Path"
msgstr ""
#: ../src/LevelEditor.cpp:363
msgid "Remove Path"
msgstr ""
#: ../src/LevelEditor.cpp:365 ../src/LevelEditor.cpp:371
#: ../src/LevelEditor.cpp:587 ../src/LevelEditor.cpp:589
msgid "Activated"
msgstr ""
#: ../src/LevelEditor.cpp:366 ../src/LevelEditor.cpp:612
#: ../src/LevelEditor.cpp:614
msgid "Looping"
msgstr ""
#: ../src/LevelEditor.cpp:372 ../src/LevelEditor.cpp:3526
msgid "Speed"
msgstr ""
#: ../src/LevelEditor.cpp:378 ../src/LevelEditor.cpp:668
#, c-format
msgid "State: %s"
msgstr ""
#: ../src/LevelEditor.cpp:382 ../src/LevelEditor.cpp:3511
msgid "Message"
msgstr ""
#: ../src/LevelEditor.cpp:384 ../src/LevelEditor.cpp:1202
#: ../src/LevelEditor.cpp:3825
msgid "Appearance"
msgstr ""
#: ../src/LevelEditor.cpp:389 ../src/LevelEditor.cpp:431
#: ../src/LevelEditor.cpp:715
msgid "Scripting"
msgstr ""
#: ../src/LevelEditor.cpp:402 ../src/LevelEditor.cpp:867
#: ../src/LevelEditor.cpp:885
#, c-format
msgid "Background layer: %s"
msgstr ""
#: ../src/LevelEditor.cpp:409 ../src/LevelEditor.cpp:866
#: ../src/LevelEditor.cpp:884
msgid "Blocks layer"
msgstr ""
#: ../src/LevelEditor.cpp:417 ../src/LevelEditor.cpp:867
#: ../src/LevelEditor.cpp:885
#, c-format
msgid "Foreground layer: %s"
msgstr ""
#: ../src/LevelEditor.cpp:423
msgid "Add new layer"
msgstr ""
#: ../src/LevelEditor.cpp:424
msgid "Delete selected layer"
msgstr ""
#: ../src/LevelEditor.cpp:425
msgid "Configure selected layer"
msgstr ""
#: ../src/LevelEditor.cpp:426
msgid "Move selected object to layer"
msgstr ""
#: ../src/LevelEditor.cpp:430 ../src/OptionsMenu.cpp:55
msgid "Settings"
msgstr ""
#: ../src/LevelEditor.cpp:463
msgid ""
"NOTE: the layers are sorted by name alphabetically.\n"
"The layer is background layer if its name is < 'f'\n"
"by dictionary order, otherwise it's foreground layer."
msgstr ""
#: ../src/LevelEditor.cpp:539
msgid "Notification block"
msgstr ""
#: ../src/LevelEditor.cpp:545
msgid "Enter message here:"
msgstr ""
#: ../src/LevelEditor.cpp:646
msgid "Behavior"
msgstr ""
#: ../src/LevelEditor.cpp:665
msgid "State"
msgstr ""
#: ../src/LevelEditor.cpp:673
msgid "Conveyor belt speed"
msgstr ""
#: ../src/LevelEditor.cpp:679
msgid "Enter speed here:"
msgstr ""
#: ../src/LevelEditor.cpp:690
msgid "NOTE: 1 Speed = 0.08 block/s"
msgstr ""
#: ../src/LevelEditor.cpp:721
msgid "Id:"
msgstr ""
#: ../src/LevelEditor.cpp:787
msgid "Level Scripting"
msgstr ""
#: ../src/LevelEditor.cpp:892
msgid "Add layer"
msgstr ""
#: ../src/LevelEditor.cpp:898
msgid "Enter the layer name:"
msgstr ""
#: ../src/LevelEditor.cpp:943
#, c-format
msgid "Are you sure you want to delete layer '%s'?"
msgstr ""
#: ../src/LevelEditor.cpp:944
msgid "Delete layer"
msgstr ""
#: ../src/LevelEditor.cpp:968
msgid "Layer settings"
msgstr ""
#: ../src/LevelEditor.cpp:974
msgid "Layer name:"
msgstr ""
#: ../src/LevelEditor.cpp:989
msgid "Layer moving speed (1 speed = 0.8 block/s):"
msgstr ""
#: ../src/LevelEditor.cpp:1010
msgid "Speed of following camera:"
msgstr ""
#: ../src/LevelEditor.cpp:1062
msgid "Move to layer"
msgstr ""
#: ../src/LevelEditor.cpp:1068
msgid "Enter the layer name (create new layer if necessary):"
msgstr ""
#: ../src/LevelEditor.cpp:1132
msgid "Repeat mode"
msgstr ""
#: ../src/LevelEditor.cpp:1156
msgid "Custom scenery:"
msgstr ""
#: ../src/LevelEditor.cpp:1219
msgid "(Use the default appearance for this block)"
msgstr ""
# TRANSLATORS: Block name
# TRANSLATORS: Context: Resize/Move ...
# TRANSLATORS: Context: Add/Remove ...
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1465 ../src/LevelEditor.cpp:1707
#: ../src/LevelEditor.cpp:1723 ../src/LevelEditor.cpp:1772
#: ../src/LevelEditor.cpp:4400
msgid "Custom scenery block"
msgstr ""
#: ../src/LevelEditor.cpp:1673
msgid "Toolbox"
msgstr ""
#: ../src/LevelEditor.cpp:1705
#, c-format
msgid "Resize %s"
msgstr ""
#: ../src/LevelEditor.cpp:1705
#, c-format
msgid "Move %s"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1713
#, c-format
msgid "Move %d object"
msgid_plural "Move %d objects"
msgstr[0] ""
msgstr[1] ""
#: ../src/LevelEditor.cpp:1721
#, c-format
msgid "Add %s"
msgstr ""
#: ../src/LevelEditor.cpp:1721
#, c-format
msgid "Remove %s"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1729
#, c-format
msgid "Add %d object"
msgid_plural "Add %d objects"
msgstr[0] ""
msgstr[1] ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1731
#, c-format
msgid "Remove %d object"
msgid_plural "Remove %d objects"
msgstr[0] ""
msgstr[1] ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1739
#, c-format
msgid "Add path to %s"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1741
#, c-format
msgid "Remove a path point from %s"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1747
#, c-format
msgid "Remove all paths from %s"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1753
#, c-format
msgid "Add link from %s to %s"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1759
#, c-format
msgid "Remove all links from %s"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1766
msgid "Modify the %2 property of %1"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1818
#, c-format
msgid "Edit the script of %s"
msgstr ""
# TRANSLATORS: Context: Undo/Redo ...
#: ../src/LevelEditor.cpp:1821
msgid "Edit the script of level"
msgstr ""
#: ../src/LevelEditor.cpp:2146 ../src/LevelEditor.cpp:2226
msgid "The level has unsaved changes."
msgstr ""
#: ../src/LevelEditor.cpp:2150 ../src/LevelEditor.cpp:2228
msgid "Are you sure you want to quit?"
msgstr ""
#: ../src/LevelEditor.cpp:2150 ../src/LevelEditor.cpp:2228
msgid "Quit prompt"
msgstr ""
#: ../src/LevelEditor.cpp:2797 ../src/LevelEditor.cpp:2799
#, c-format
msgid "Level \"%s\" saved"
msgstr ""
#: ../src/LevelEditor.cpp:2797 ../src/LevelEditor.cpp:2799
msgid "Saved"
msgstr ""
#: ../src/LevelEditor.cpp:2859 ../src/LevelEditSelect.cpp:208
msgid "Name:"
msgstr ""
#: ../src/LevelEditor.cpp:2866
msgid "Theme:"
msgstr ""
#: ../src/LevelEditor.cpp:2873
msgid "Examples: %DATA%/themes/classic"
msgstr ""
#: ../src/LevelEditor.cpp:2875
msgid "or %USER%/themes/Orange"
msgstr ""
#: ../src/LevelEditor.cpp:2878
msgid "Music:"
msgstr ""
#: ../src/LevelEditor.cpp:2887
msgid "Target time (s):"
msgstr ""
#: ../src/LevelEditor.cpp:2903
msgid "Target recordings:"
msgstr ""
#: ../src/LevelEditor.cpp:2919
msgid "Restart level editor is required"
msgstr ""
#: ../src/LevelEditor.cpp:3679 ../src/LevelEditor.cpp:3718
#: ../src/LevelEditor.cpp:3739
msgid "Please enter a layer name."
msgstr ""
#: ../src/LevelEditor.cpp:3679 ../src/LevelEditor.cpp:3683
#: ../src/LevelEditor.cpp:3718 ../src/LevelEditor.cpp:3722
#: ../src/LevelEditor.cpp:3739 ../src/LevelEditor.cpp:3743
-#: ../src/LevelEditSelect.cpp:643 ../src/LevelEditSelect.cpp:781
+#: ../src/LevelEditSelect.cpp:644 ../src/LevelEditSelect.cpp:683
+#: ../src/LevelEditSelect.cpp:688 ../src/LevelEditSelect.cpp:693
+#: ../src/LevelEditSelect.cpp:698 ../src/LevelEditSelect.cpp:796
msgid "Error"
msgstr ""
#: ../src/LevelEditor.cpp:3683 ../src/LevelEditor.cpp:3722
#, c-format
msgid "The layer '%s' already exists."
msgstr ""
#: ../src/LevelEditor.cpp:3743
msgid "Source and destination layers are the same."
msgstr ""
#: ../src/LevelEditor.cpp:3760
msgid "Scenery"
msgstr ""
#: ../src/LevelEditor.cpp:4190 ../src/LevelEditor.cpp:4218
#, c-format
msgid "Speed: %d = %0.2f block/s"
msgstr ""
#: ../src/LevelEditor.cpp:4203
msgid "Stop at this point"
msgstr ""
#: ../src/LevelEditor.cpp:4208
#, c-format
msgid "Pause: %d = %0.3fs"
msgstr ""
#: ../src/LevelEditSelect.cpp:41 ../src/TitleMenu.cpp:45
msgid "Map Editor"
msgstr ""
#: ../src/LevelEditSelect.cpp:66
msgid "New Levelpack"
msgstr ""
#: ../src/LevelEditSelect.cpp:71
msgid "Pack Properties"
msgstr ""
#: ../src/LevelEditSelect.cpp:76
msgid "Remove Pack"
msgstr ""
#: ../src/LevelEditSelect.cpp:81
msgid "Move Map"
msgstr ""
#: ../src/LevelEditSelect.cpp:89
msgid "Remove Map"
msgstr ""
#: ../src/LevelEditSelect.cpp:94
msgid "Edit Map"
msgstr ""
#: ../src/LevelEditSelect.cpp:205
msgid "Properties"
msgstr ""
#: ../src/LevelEditSelect.cpp:217
msgid "Description:"
msgstr ""
#: ../src/LevelEditSelect.cpp:226
msgid "Congratulation text:"
msgstr ""
#: ../src/LevelEditSelect.cpp:235
msgid "Music list:"
msgstr ""
#: ../src/LevelEditSelect.cpp:265 ../src/LevelEditSelect.cpp:485
msgid "Add level"
msgstr ""
#: ../src/LevelEditSelect.cpp:268
msgid "File name:"
msgstr ""
#: ../src/LevelEditSelect.cpp:293
msgid "Move level"
msgstr ""
#: ../src/LevelEditSelect.cpp:296
msgid "Level: "
msgstr ""
#: ../src/LevelEditSelect.cpp:310
msgid "Before"
msgstr ""
#: ../src/LevelEditSelect.cpp:311
msgid "After"
msgstr ""
#: ../src/LevelEditSelect.cpp:368 ../src/LevelPlaySelect.cpp:124
msgid "Individual levels which are not contained in any level packs"
msgstr ""
#: ../src/LevelEditSelect.cpp:577
#, c-format
msgid "Are you sure remove the level pack '%s'?"
msgstr ""
-#: ../src/LevelEditSelect.cpp:577 ../src/LevelEditSelect.cpp:606
+#: ../src/LevelEditSelect.cpp:577 ../src/LevelEditSelect.cpp:607
msgid "Remove prompt"
msgstr ""
-#: ../src/LevelEditSelect.cpp:606
+#: ../src/LevelEditSelect.cpp:607
#, c-format
msgid "Are you sure remove the map '%s'?"
msgstr ""
-#: ../src/LevelEditSelect.cpp:643
+#: ../src/LevelEditSelect.cpp:644
msgid "Levelpack name cannot be empty."
msgstr ""
-#: ../src/LevelEditSelect.cpp:743
+#: ../src/LevelEditSelect.cpp:683
+#, c-format
+msgid "The levelpack directory '%s' already exists!"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:688
+#, c-format
+msgid "Unable to create levelpack directory '%s'!"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:693
+#, c-format
+msgid "The levelpack file '%s' already exists!"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:698
+#, c-format
+msgid "Unable to create levelpack file '%s'!"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:758
msgid "No file name given for the new level."
msgstr ""
-#: ../src/LevelEditSelect.cpp:743
+#: ../src/LevelEditSelect.cpp:758
msgid "Missing file name"
msgstr ""
-#: ../src/LevelEditSelect.cpp:781
+#: ../src/LevelEditSelect.cpp:796
#, c-format
msgid "The file %s already exists."
msgstr ""
-#: ../src/LevelEditSelect.cpp:834
+#: ../src/LevelEditSelect.cpp:849
msgid "The entered level number isn't valid!"
msgstr ""
-#: ../src/LevelEditSelect.cpp:834
+#: ../src/LevelEditSelect.cpp:849
msgid "Illegal number"
msgstr ""
#: ../src/LevelInfoRender.cpp:19
msgid "Choose a level"
msgstr ""
#: ../src/LevelInfoRender.cpp:20
msgid "Time:"
msgstr ""
#: ../src/LevelInfoRender.cpp:21 ../src/StatisticsScreen.cpp:259
msgid "Recordings:"
msgstr ""
#: ../src/LevelPackManager.cpp:124
msgid "Custom Levels"
msgstr ""
#: ../src/LevelPlaySelect.cpp:41
msgid "Select Level"
msgstr ""
# TRANSLATORS: Used for button which clear any level progress like unlocked levels and highscores.
#: ../src/OptionsMenu.cpp:66
msgid "Clear Progress"
msgstr ""
#: ../src/OptionsMenu.cpp:109
msgid "General"
msgstr ""
#: ../src/OptionsMenu.cpp:110
msgid "Controls"
msgstr ""
#: ../src/OptionsMenu.cpp:121
msgid "Music"
msgstr ""
#: ../src/OptionsMenu.cpp:129
msgid "Sound"
msgstr ""
#: ../src/OptionsMenu.cpp:137
msgid "Resolution"
msgstr ""
#: ../src/OptionsMenu.cpp:177
msgid "Language"
msgstr ""
# TRANSLATORS: as detect user's language automatically
#: ../src/OptionsMenu.cpp:185
msgid "Auto-Detect"
msgstr ""
#: ../src/OptionsMenu.cpp:209
msgid "Theme"
msgstr ""
#: ../src/OptionsMenu.cpp:247
msgid "Internet proxy"
msgstr ""
#: ../src/OptionsMenu.cpp:256
msgid "Fullscreen"
msgstr ""
#: ../src/OptionsMenu.cpp:261
msgid "Quick record"
msgstr ""
#: ../src/OptionsMenu.cpp:266
msgid "Internet"
msgstr ""
#: ../src/OptionsMenu.cpp:271
msgid "Fade transition"
msgstr ""
#: ../src/OptionsMenu.cpp:294
msgid "Save Changes"
msgstr ""
#: ../src/OptionsMenu.cpp:513
msgid "Do you really want to reset level progress?"
msgstr ""
#: ../src/OptionsMenu.cpp:513
msgid "Warning"
msgstr ""
#: ../src/StatisticsManager.cpp:386
msgid "New achievement:"
msgstr ""
#: ../src/StatisticsManager.cpp:394
#, c-format
msgid "Achieved on %s"
msgstr ""
#: ../src/StatisticsManager.cpp:400
msgid "Unknown achievement"
msgstr ""
#: ../src/StatisticsManager.cpp:406
#, c-format
msgid "Achieved %1.0f%%"
msgstr ""
#: ../src/StatisticsManager.cpp:410
msgid "Not achieved"
msgstr ""
#: ../src/StatisticsScreen.cpp:57 ../src/TitleMenu.cpp:55
msgid "Achievements and Statistics"
msgstr ""
#: ../src/StatisticsScreen.cpp:166
msgid "Achievements"
msgstr ""
#: ../src/StatisticsScreen.cpp:167
msgid "Statistics"
msgstr ""
#: ../src/StatisticsScreen.cpp:234
msgid "Total"
msgstr ""
#: ../src/StatisticsScreen.cpp:246
msgid "Traveling distance (m)"
msgstr ""
#: ../src/StatisticsScreen.cpp:247
msgid "Jump times"
msgstr ""
#: ../src/StatisticsScreen.cpp:248
msgid "Die times"
msgstr ""
#: ../src/StatisticsScreen.cpp:249
msgid "Squashed times"
msgstr ""
#: ../src/StatisticsScreen.cpp:260
msgid "Switch pulled times:"
msgstr ""
#: ../src/StatisticsScreen.cpp:261
msgid "Swap times:"
msgstr ""
#: ../src/StatisticsScreen.cpp:262
msgid "Save times:"
msgstr ""
#: ../src/StatisticsScreen.cpp:263
msgid "Load times:"
msgstr ""
#: ../src/StatisticsScreen.cpp:268
msgid "Completed levels:"
msgstr ""
#: ../src/StatisticsScreen.cpp:306
msgid "In-game time:"
msgstr ""
#: ../src/StatisticsScreen.cpp:308
msgid "Level editing time:"
msgstr ""
#: ../src/StatisticsScreen.cpp:310
msgid "Created levels:"
msgstr ""
#: ../src/TitleMenu.cpp:44
msgid "Options"
msgstr ""
#: ../src/TitleMenu.cpp:47
msgid "Quit"
msgstr ""
#: ../src/TitleMenu.cpp:131
msgid "Enable internet in order to install addons."
msgstr ""
#: ../src/TitleMenu.cpp:131
msgid "Internet disabled"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Return"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Escape"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Backspace"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Tab"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Space"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "CapsLock"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "PrintScreen"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "ScrollLock"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Pause"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Insert"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Home"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "PageUp"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Delete"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "End"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "PageDown"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Right"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Left"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Down"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Up"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Numlock"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "SysReq"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Left Ctrl"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Left Shift"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Left Alt"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Left GUI"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Right Ctrl"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Right Shift"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Right Alt"
msgstr ""
# TRANSLATORS: name of a key
msgctxt "keys"
msgid "Right GUI"
msgstr ""
diff --git a/src/FileManager.cpp b/src/FileManager.cpp
index 2ca5dc3..3aac6fc 100644
--- a/src/FileManager.cpp
+++ b/src/FileManager.cpp
@@ -1,867 +1,874 @@
/*
* 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 <stdio.h>
#include <iostream>
#include <string>
#include <vector>
#include "config.h"
#include "FileManager.h"
#include "Functions.h"
#include <SDL.h>
#include <SDL_syswm.h>
#include <archive.h>
#include <archive_entry.h>
using namespace std;
#ifdef WIN32
#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <shellapi.h>
#include <direct.h>
#pragma comment(lib,"shell32.lib")
#pragma comment(lib,"shlwapi.lib")
#else
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#endif
//Included for the downloadFile method.
#include <curl/curl.h>
//Under Windows there's just one userpath.
#ifdef WIN32
string userPath,dataPath,appPath,exeName;
#define TO_UTF8(SRC, DEST) WideCharToMultiByte(CP_UTF8, 0, SRC, -1, DEST, sizeof(DEST), NULL, NULL)
#define TO_UTF16(SRC, DEST) MultiByteToWideChar(CP_UTF8, 0, SRC, -1, DEST, sizeof(DEST)/sizeof(DEST[0]))
#else
//But on other platforms we make a difference between the userPath (config files) and the userDataPath (data files).
//Finally there's the path for cache data userCachePath.
string userPath,userDataPath,userCachePath,dataPath,appPath,exeName;
#endif
bool configurePaths() {
//Get the appPath and the exeName.
{
char s[4096];
int i,m;
#ifdef WIN32
wchar_t ws[4096];
m = GetModuleFileNameW(NULL, ws, sizeof(ws) / sizeof(ws[0]));
ws[m] = 0;
TO_UTF8(ws, s);
m = strlen(s);
#elif defined(ANDROID)
//FIXME: Oops. There are no any executable files in Android.
strcpy(s,"./meandmyshadow");
m=strlen(s);
#else
m=readlink("/proc/self/exe",s,sizeof(s));
#endif
s[m]=0;
for(i=m-1;i>=0&&i<4096;i--){
if(s[i]=='/'||s[i]=='\\'){
s[i]=0;
break;
}
}
appPath=s;
exeName=s+i+1;
}
//TODO: Check if the userpath is empty before setting userPath???
//Check if the userPath is empty.
if(getUserPath().empty()){
#ifdef WIN32
//Get the userPath.
char s[4096];
wchar_t ws[4096];
SHGetSpecialFolderPathW(NULL,ws,CSIDL_PERSONAL,1);
TO_UTF8(ws, s);
userPath=s;
userPath+="\\My Games\\meandmyshadow\\";
#elif defined(ANDROID)
//FIXME: These paths are relative to SDL Android data path.
userPath="./";
userDataPath="./";
userCachePath="./";
#else
//Temp variable that is used to prevent NULL assignement.
char* env;
//First get the $XDG_CONFIG_HOME env var.
env=getenv("XDG_CONFIG_HOME");
//If it's null set userPath to $HOME/.config/.
if(env!=NULL){
userPath=env;
}else{
userPath=getenv("HOME");
userPath+="/.config";
}
//And add meandmyshadow to it.
userPath+="/meandmyshadow/";
//Now get the $XDG_DATA_HOME env var.
env=getenv("XDG_DATA_HOME");
//If it's null set userDataPath to $HOME/.local/share.
if(env!=NULL){
userDataPath=env;
}else{
userDataPath=getenv("HOME");
userDataPath+="/.local/share";
}
//And add meandmyshadow to it.
userDataPath+="/meandmyshadow/";
//Now get the $XDG_CACHE_HOME env var.
env=getenv("XDG_CACHE_HOME");
//If it's null set userCachePath to $HOME/.cache.
if(env!=NULL){
userCachePath=env;
}else{
userCachePath=getenv("HOME");
userCachePath+="/.cache";
}
//And add meandmyshadow to it.
userCachePath+="/meandmyshadow/";
//Set env null.
env=NULL;
#endif
//Print the userPath.
cout<<"User preferences will be fetched from: "<<userPath<<endl;
#ifndef WIN32
//In case of a non-Windows computer show the user data path.
cout<<"User data will be fetched from: "<<userDataPath<<endl;
#endif
}
#ifdef WIN32
//Create the userPath folder and other subfolders.
createDirectory(userPath.c_str());
createDirectory((userPath+"levels").c_str());
createDirectory((userPath+"levelpacks").c_str());
createDirectory((userPath+"themes").c_str());
createDirectory((userPath+"music").c_str());
//The progress folder and subfolders.
createDirectory((userPath+"progress").c_str());
createDirectory((userPath+"progress\\main").c_str());
createDirectory((userPath+"progress\\addon").c_str());
createDirectory((userPath+"progress\\custom").c_str());
createDirectory((userPath+"tmp").c_str());
createDirectory((userPath+"images").c_str());
//The records folder for recordings.
createDirectory((userPath+"records").c_str());
createDirectory((userPath+"records\\autosave").c_str());
//And the custom folder inside the userpath.
createDirectory((userPath+"custom").c_str());
createDirectory((userPath+"custom\\levels").c_str());
createDirectory((userPath+"custom\\levelpacks").c_str());
#else
//Create the userPath.
createDirectory(userPath.c_str());
createDirectory(userDataPath.c_str());
createDirectory(userCachePath.c_str());
//Also create other folders in the userpath.
createDirectory((userDataPath+"/levels").c_str());
createDirectory((userDataPath+"/levelpacks").c_str());
createDirectory((userDataPath+"/themes").c_str());
createDirectory((userDataPath+"/music").c_str());
//The progress folder and subfolders.
createDirectory((userDataPath+"/progress").c_str());
createDirectory((userDataPath+"/progress/main").c_str());
createDirectory((userDataPath+"/progress/addon").c_str());
createDirectory((userDataPath+"/progress/custom").c_str());
createDirectory((userCachePath+"/tmp").c_str());
createDirectory((userCachePath+"/images").c_str());
//The records folder for recordings.
createDirectory((userDataPath+"/records").c_str());
createDirectory((userDataPath+"/records/autosave").c_str());
//And the custom folder inside the userpath.
createDirectory((userDataPath+"/custom").c_str());
createDirectory((userDataPath+"/custom/levels").c_str());
createDirectory((userDataPath+"/custom/levelpacks").c_str());
#endif
//Get the dataPath by trying multiple relative locations.
{
FILE *f;
string s;
while(true){
//try existing one
if(!dataPath.empty()){
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
}
//try "./"
dataPath="./data/";
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
//try "../"
dataPath="../data/";
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
//try App.Path
dataPath=getAppPath()+"/data/";
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
//try App.Path+"/../"
dataPath=getAppPath()+"/../data/";
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
//try DATA_PATH
#ifdef DATA_PATH
dataPath=DATA_PATH;
std::cout << "trying: " << dataPath << std::endl;
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
#endif
#ifdef __APPLE__
// TODO:
/*extern std::string get_data_path();
dataPath = get_data_path();
dataPath=get_data_path();
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
*/
#endif
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error! Faild to find game data!",
"The game data files could not be found!", NULL);
//error: can't find file
return false;
}
//Print the dataPath.
cout<<"Data files will be fetched from: "<<dataPath<<endl;
}
return true;
}
std::vector<std::string> enumAllFiles(std::string path,const char* extension,bool containsPath){
vector<string> v;
#ifdef WIN32
string s1;
WIN32_FIND_DATAW f;
if(!path.empty()){
char c=path[path.size()-1];
if(c!='/'&&c!='\\') path+="\\";
}
s1=path;
if(extension!=NULL && *extension){
s1+="*.";
s1+=extension;
}else{
s1+="*";
}
wchar_t ws[4096];
TO_UTF16(s1.c_str(), ws);
HANDLE h=FindFirstFileW(ws,&f);
if(h==NULL||h==INVALID_HANDLE_VALUE) return v;
do{
if(!(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
char s[1024];
TO_UTF8(f.cFileName, s);
if(containsPath){
v.push_back(path+s);
}else{
v.push_back(s);
}
}
}while(FindNextFileW(h,&f));
FindClose(h);
return v;
#else
int len=0;
if(extension!=NULL && *extension) len=strlen(extension);
if(!path.empty()){
char c=path[path.size()-1];
if(c!='/'&&c!='\\') path+="/";
}
DIR *pDir;
struct dirent *pDirent;
pDir=opendir(path.c_str());
if(pDir==NULL) return v;
while((pDirent=readdir(pDir))!=NULL){
if(pDirent->d_name[0]=='.'){
if(pDirent->d_name[1]==0||
(pDirent->d_name[1]=='.'&&pDirent->d_name[2]==0)) continue;
}
string s1=path+pDirent->d_name;
struct stat S_stat;
lstat(s1.c_str(),&S_stat);
if(!S_ISDIR(S_stat.st_mode)){
if(len>0){
if((int)s1.size()<len+1) continue;
if(s1[s1.size()-len-1]!='.') continue;
if(strcasecmp(&s1[s1.size()-len],extension)) continue;
}
if(containsPath){
v.push_back(s1);
}else{
v.push_back(string(pDirent->d_name));
}
}
}
closedir(pDir);
return v;
#endif
}
std::vector<std::string> enumAllDirs(std::string path,bool containsPath){
vector<string> v;
#ifdef WIN32
string s1;
WIN32_FIND_DATAW f;
if(!path.empty()){
char c=path[path.size()-1];
if(c!='/'&&c!='\\') path+="\\";
}
s1=path+"*";
wchar_t ws[4096];
TO_UTF16(s1.c_str(), ws);
HANDLE h = FindFirstFileW(ws, &f);
if(h==NULL||h==INVALID_HANDLE_VALUE) return v;
do{
// skip '.' and '..' and hidden folders
if(f.cFileName[0]=='.'){
/*if(f.cFileName[1]==0||
(f.cFileName[1]=='.'&&f.cFileName[2]==0))*/ continue;
}
if(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
char s[1024];
TO_UTF8(f.cFileName, s);
if (containsPath){
v.push_back(path+s);
}else{
v.push_back(s);
}
}
}while(FindNextFileW(h,&f));
FindClose(h);
return v;
#else
if(!path.empty()){
char c=path[path.size()-1];
if(c!='/'&&c!='\\') path+="/";
}
DIR *pDir;
struct dirent *pDirent;
pDir=opendir(path.c_str());
if(pDir==NULL) return v;
while((pDirent=readdir(pDir))!=NULL){
if(pDirent->d_name[0]=='.'){
if(pDirent->d_name[1]==0||
(pDirent->d_name[1]=='.'&&pDirent->d_name[2]==0)) continue;
}
string s1=path+pDirent->d_name;
struct stat S_stat;
lstat(s1.c_str(),&S_stat);
if(S_ISDIR(S_stat.st_mode)){
//Skip hidden folders.
s1=string(pDirent->d_name);
if(s1.find('.')==0) continue;
//Add result to vector.
if(containsPath){
v.push_back(path+pDirent->d_name);
}else{
v.push_back(s1);
}
}
}
closedir(pDir);
return v;
#endif
}
std::string processFileName(const std::string& s){
string prefix=dataPath;
//FIXME: Do we still need those last three?
//REMARK: maybe 'return prefix+s;' is not needed (?)
// it causes some bugs such as can't save level progress
if(s.compare(0,6,"%DATA%")==0){
if(s.size()>6 && (s[6]=='/' || s[6]=='\\')){
return dataPath+s.substr(7);
}else{
return dataPath+s.substr(6);
}
}else if(s.compare(0,6,"%USER%")==0){
if(s.size()>6 && (s[6]=='/' || s[6]=='\\')){
return getUserPath(USER_DATA)+s.substr(7);
}else{
return getUserPath(USER_DATA)+s.substr(6);
}
}else if(s.compare(0,9,"%LVLPACK%")==0){
if(s.size()>9 && (s[9]=='/' || s[9]=='\\')){
return prefix+"levelpacks/"+s.substr(10);
}else{
return prefix+"levelpacks/"+s.substr(9);
}
}else if(s.compare(0,5,"%LVL%")==0){
if(s.size()>5 && (s[5]=='/' || s[5]=='\\')){
return prefix+"levels/"+s.substr(6);
}else{
return prefix+"levels/"+s.substr(5);
}
}else if(s.compare(0,8,"%THEMES%")==0){
if(s.size()>8 && (s[8]=='/' || s[8]=='\\')){
return prefix+"themes/"+s.substr(9);
}else{
return prefix+"themes/"+s.substr(8);
}
}else if(s.size()>0 && (s[0]=='/' || s[0]=='\\')){
return s;
#ifdef WIN32
// Another fix for Windows :(
}else if(s.size()>1 && (s[1]==':')){
return s;
#endif
}else{
#if defined(ANDROID)
//REMARK: maybe 'return prefix+s;' is not needed (?)
// it causes some bugs such as can't save level progress in Android.
return s;
#else
return prefix+s;
#endif
}
}
std::string compressFileName(const std::string& s){
//FIXME: Do we need the other ones from processFileName?
//Check if the data path is at the start.
size_t pos=s.find(getDataPath());
if(pos!=string::npos){
pos+=getDataPath().size();
return "%DATA%/"+s.substr(pos);
}
pos=s.find(getUserPath(USER_DATA));
if(pos!=string::npos){
pos+=getUserPath(USER_DATA).size();
return "%USER%/"+s.substr(pos);
}
return s;
}
std::string fileNameFromPath(const std::string &path, const bool webURL){
std::string filename;
size_t pos;
#ifdef WIN32
// NOTE: '/' in string should be '/' not '\/',
// we don't need to escape it
if(webURL){
pos = path.find_last_of("/");
}else{
// NOTE: sometimes path separator in Windows can be '/',
// so we must check botn '\' and '/'
pos = path.find_last_of("\\/");
}
#else
// NOTE: '/' in string should be '/' not '\/',
// we don't need to escape it
pos = path.find_last_of("/");
#endif
if(pos != std::string::npos)
filename.assign(path.begin() + pos + 1, path.end());
else
filename=path;
return filename;
}
std::string pathFromFileName(const std::string &filename){
std::string path;
// NOTE: '/' in string should be '/' not '\/',
// we don't need to escape it
#ifdef WIN32
// NOTE: sometimes path separator in Windows can be '/',
// so we must check botn '\' and '/'
size_t pos = filename.find_last_of("\\/");
#else
size_t pos = filename.find_last_of("/");
#endif
if(pos != std::string::npos)
path.assign(filename.begin(), filename.begin() + pos +1);
else
path=filename;
return path;
}
bool downloadFile(const string &path, const string &destination) {
string filename=fileNameFromPath(path,true);
FILE* file = fopen((destination+filename).c_str(), "wb");
bool status=downloadFile(path,file);
fclose(file);
//And return the status.
return status;
}
bool downloadFile(const string &path, FILE* destination) {
CURL* curl=curl_easy_init();
// proxy test (test only)
string internetProxy = getSettings()->getValue("internet-proxy");
size_t pos = internetProxy.find_first_of(":");
if(pos!=string::npos){
curl_easy_setopt(curl,CURLOPT_PROXYPORT,atoi(internetProxy.substr(pos+1).c_str()));
internetProxy = internetProxy.substr(0,pos);
curl_easy_setopt(curl,CURLOPT_PROXY,internetProxy.c_str());
}
// NEW: append the path to addon_url if the path is relative
std::string newPath;
if (path.find("://") == string::npos) {
newPath = getSettings()->getValue("addon_url");
size_t p = newPath.find_last_of("\\/");
if (p != string::npos) newPath = newPath.substr(0, p + 1);
newPath += path;
} else {
newPath = path;
}
// debug
#ifdef _DEBUG
printf("Downloading %s\n", newPath.c_str());
#endif
curl_easy_setopt(curl, CURLOPT_URL, newPath.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, destination);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 8);
CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
return (res==0);
}
size_t writeData(void *ptr, size_t size, size_t nmemb, void *stream){
return fwrite(ptr, size, nmemb, (FILE *)stream);
}
bool extractFile(const string &fileName, const string &destination) {
//Create the archive we're going to extract.
archive* file;
//Create the destination we're going to extract to.
archive* dest;
file=archive_read_new();
dest=archive_write_disk_new();
archive_write_disk_set_options(dest, ARCHIVE_EXTRACT_TIME);
archive_read_support_format_zip(file);
//Now read the archive.
if(archive_read_open_filename(file,fileName.c_str(),10240)) {
cerr<<"ERROR: An error occurred while reading archive "+fileName<<endl;
return false;
}
//Now write every entry to disk.
int status;
archive_entry* entry=NULL;
while(true) {
status=archive_read_next_header(file,&entry);
if(status==ARCHIVE_EOF){
break;
}
if(status!=ARCHIVE_OK){
cerr<<"ERROR: An error occurred while reading archive "+fileName<<endl;
return false;
}
archive_entry_copy_pathname(entry,(destination+archive_entry_pathname(entry)).c_str());
status=archive_write_header(dest,entry);
if(status!=ARCHIVE_OK){
cerr<<"ERROR: An error occurred while extracting archive "+fileName<<endl;
return false;
}else{
copyData(file, dest);
status=archive_write_finish_entry(dest);
if(status!=ARCHIVE_OK){
cerr<<"ERROR: An error occurred while extracting archive "+fileName<<endl;
return false;
}
}
}
//Finally close the archive.
archive_read_close(file);
archive_read_free(file);
return true;
}
bool dirExists(const char* dir){
#if defined(WIN32)
wchar_t ws[4096];
TO_UTF16(dir, ws);
DWORD attr=GetFileAttributesW(ws);
if(attr==INVALID_FILE_ATTRIBUTES) return false;
return (attr & FILE_ATTRIBUTE_DIRECTORY)!=0;
#else
struct stat sb;
if(stat(dir,&sb) == 0 && S_ISDIR(sb.st_mode)){
return true;
}
return false;
#endif
}
bool createDirectory(const char* path){
#ifdef WIN32
wchar_t s0[4096],s1[4096],s[4096];
GetCurrentDirectoryW(sizeof(s0)/sizeof(s0[0]),s0);
TO_UTF16(path, s1);
PathCombineW(s,s0,s1);
for(unsigned int i=0;i<sizeof(s)/sizeof(s[0]);i++){
if(s[i]=='\0') break;
else if(s[i]=='/') s[i]='\\';
}
- //printf("createDirectory:%s\n",s);
- return SHCreateDirectoryExW(NULL,s,NULL)!=0;
+ int ret = SHCreateDirectoryExW(NULL, s, NULL);
+ switch(ret) {
+ case ERROR_SUCCESS:
+ case ERROR_FILE_EXISTS:
+ case ERROR_ALREADY_EXISTS:
+ return true;
+ default:
+ return false;
+ }
#else
return mkdir(path,0777)==0;
#endif
}
bool removeDirectory(const char *path){
#ifdef WIN32
SDL_SysWMinfo info = {};
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(sdlWindow, &info);
wchar_t s0[4096], s1[4096], s[4096];
GetCurrentDirectoryW(sizeof(s0) / sizeof(s0[0]), s0);
TO_UTF16(path, s1);
PathCombineW(s, s0, s1);
for (unsigned int i = 0; i<sizeof(s) / sizeof(s[0]); i++){
if (s[i] == '\0') {
s[i + 1] = '\0';
break;
}
else if (s[i] == '/') s[i] = '\\';
}
SHFILEOPSTRUCTW op = {
info.info.win.window,
FO_DELETE,
s,
NULL,
FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT,
FALSE,
0,
NULL,
};
int ret = SHFileOperationW(&op);
return ret == 0 && op.fAnyOperationsAborted == FALSE;
#else
//Open the directory that needs to be removed.
DIR* d=opendir(path);
//Get the path length
size_t path_len = strlen(path);
//Boolean if the directory is emptied.
//True: succes False: failure
//Default is true because if the directory is empty it will never enter the while loop, but we still have success.
bool r = true;
//Check if the directory exists.
if(d) {
//Pointer to an entry of the directory.
struct dirent* p;
//Loop the entries of the directory that needs to be removed as long as there's no error.
while(r && (p=readdir(d))) {
/* Skip the names "." and ".." as we don't want to recurse on them. */
if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) {
//The filename is . or .. so we continue to the next entry.
continue;
} else {
//r2 tells if the entry is deleted.
//True: succes False: failure
//Default is false.
bool r2 = false;
char* buf;
size_t len;
//Get the length of the path + the directory entry name.
len = path_len + strlen(p->d_name) + 2;
buf = (char*) malloc(len);
if(buf) {
struct stat statbuf;
snprintf(buf, len, "%s/%s", path, p->d_name);
if(!stat(buf, &statbuf)){
//Check if the entry is a directory or a file.
if (S_ISDIR(statbuf.st_mode)){
//We call ourself(removeDirectory) recursively.
//We return true on success.
r2 = removeDirectory(buf);
}else{
//unlink() returns zero on succes so we set r2 to the unlink(buf)==0.
r2 = unlink(buf)==0;
}
}
//Free the buf.
free(buf);
}
//We set r to r2 since r2 contains the status of the latest deletion.
r = r2;
}
}
//Close the directory.
closedir(d);
}
//The while loop has ended, meaning we (tried) cleared the directory.
//If r is true, meaning no errors we can delete the directory.
if(r){
//The return value of rmdir is 0 when it succeeded.
r = rmdir(path)==0;
}
//Return the status.
return r;
#endif
}
bool renameDirectory(const char* oldPath,const char* newPath){
return rename(oldPath,newPath)==0;
}
void copyData(archive* file, archive* dest) {
int status;
const void* buff;
size_t size;
#if ARCHIVE_VERSION_NUMBER < 3000000
off_t offset;
#else
int64_t offset;
#endif
while(true) {
status=archive_read_data_block(file, &buff, &size, &offset);
if(status==ARCHIVE_EOF){
return;
}
if(status!=ARCHIVE_OK){
cerr<<"ERROR: An error occurred while writing data to disk."<<endl;
return;
}
status=archive_write_data_block(dest, buff, size, offset);
if(status!=ARCHIVE_OK) {
cerr<<"ERROR: An error occurred while writing data to disk."<<endl;
return;
}
}
}
bool copyFile(const char* source,const char* dest){
//Open the source file.
ifstream fin(source,fstream::binary);
if(!fin)
return false;
//Open the dest file.
ofstream fout(dest,fstream::trunc|fstream::binary);
if(!fout)
return false;
//Copy.
fout<<fin.rdbuf();
return true;
}
bool removeFile(const char* file){
return remove(file)==0;
}
bool fileExists(const char* file){
#ifdef WIN32
wchar_t ws[4096];
TO_UTF16(file, ws);
bool ret=false;
HANDLE h=CreateFileW(ws,0,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,NULL,OPEN_EXISTING,0,NULL);
if(h!=INVALID_HANDLE_VALUE){
ret=true;
CloseHandle(h);
}
return ret;
#else
return (access(file,F_OK)==0);
#endif
}
bool createFile(const char* file){
//Open the file with write permission.
FILE* f=fopen(file,"wb");
//Check if there are no problems.
if(f){
//Close the file.
fclose(f);
return true;
}else{
return false;
}
}
diff --git a/src/LevelEditSelect.cpp b/src/LevelEditSelect.cpp
index 763aff3..d22fcc5 100644
--- a/src/LevelEditSelect.cpp
+++ b/src/LevelEditSelect.cpp
@@ -1,877 +1,892 @@
/*
* Copyright (C) 2012-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/>.
*/
#include "LevelEditSelect.h"
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIScrollBar.h"
#include "GUISpinBox.h"
#include "InputManager.h"
#include "StatisticsManager.h"
#include "Game.h"
#include "GUIOverlay.h"
#include <algorithm>
#include <string>
#include <iostream>
#include "libs/tinyformat/tinyformat.h"
using namespace std;
LevelEditSelect::LevelEditSelect(ImageManager& imageManager, SDL_Renderer& renderer):LevelSelect(imageManager,renderer,_("Map Editor"),LevelPackManager::CUSTOM_PACKS){
//Create the gui.
createGUI(imageManager,renderer, true);
//Set the levelEditGUIObjectRoot.
levelEditGUIObjectRoot=GUIObjectRoot;
//show level list
changePack();
refresh(imageManager, renderer);
}
LevelEditSelect::~LevelEditSelect(){
selectedNumber=NULL;
}
void LevelEditSelect::createGUI(ImageManager& imageManager,SDL_Renderer &renderer, bool initial){
if(initial){
//The levelpack name text field.
levelpackName=new GUITextBox(imageManager,renderer,280,104,240,32);
levelpackName->eventCallback=this;
levelpackName->visible=false;
GUIObjectRoot->addChild(levelpackName);
//Create the six buttons at the bottom of the screen.
newPack = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("New Levelpack"));
newPack->name = "cmdNewLvlpack";
newPack->eventCallback = this;
GUIObjectRoot->addChild(newPack);
propertiesPack = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Pack Properties"), 0, true, true, GUIGravityCenter);
propertiesPack->name = "cmdLvlpackProp";
propertiesPack->eventCallback = this;
GUIObjectRoot->addChild(propertiesPack);
removePack = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Remove Pack"), 0, true, true, GUIGravityRight);
removePack->name = "cmdRmLvlpack";
removePack->eventCallback = this;
GUIObjectRoot->addChild(removePack);
move = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Move Map"));
move->name = "cmdMoveMap";
move->eventCallback = this;
//NOTE: Set enabled equal to the inverse of initial.
//When resizing the window initial will be false and therefor the move button can stay enabled.
move->enabled = false;
GUIObjectRoot->addChild(move);
remove = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Remove Map"), 0, false, true, GUIGravityCenter);
remove->name = "cmdRmMap";
remove->eventCallback = this;
GUIObjectRoot->addChild(remove);
edit = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Edit Map"), 0, false, true, GUIGravityRight);
edit->name = "cmdEdit";
edit->eventCallback = this;
GUIObjectRoot->addChild(edit);
}
//Move buttons to default position
const int x1 = int(SCREEN_WIDTH*0.02), x2 = int(SCREEN_WIDTH*0.5), x3 = int(SCREEN_WIDTH*0.98);
const int y1 = SCREEN_HEIGHT - 120, y2 = SCREEN_HEIGHT - 60;
newPack->left = x1; newPack->top = y1; newPack->gravity = GUIGravityLeft;
propertiesPack->left = x2; propertiesPack->top = y1; propertiesPack->gravity = GUIGravityCenter;
removePack->left = x3; removePack->top = y1; removePack->gravity = GUIGravityRight;
move->left = x1; move->top = y2; move->gravity = GUIGravityLeft;
remove->left = x2; remove->top = y2; remove->gravity = GUIGravityCenter;
edit->left = x3; edit->top = y2; edit->gravity = GUIGravityRight;
isVertical = false;
//Reset the font size
newPack->smallFont = false; newPack->width = -1;
propertiesPack->smallFont = false; propertiesPack->width = -1;
removePack->smallFont = false; removePack->width = -1;
move->smallFont = false; move->width = -1;
remove->smallFont = false; remove->width = -1;
edit->smallFont = false; edit->width = -1;
//Now update widgets and then check if they overlap
GUIObjectRoot->render(renderer, 0, 0, false);
if (propertiesPack->left - propertiesPack->gravityX < newPack->left + newPack->width ||
propertiesPack->left - propertiesPack->gravityX + propertiesPack->width > removePack->left - removePack->gravityX)
{
newPack->smallFont = true; newPack->width = -1;
propertiesPack->smallFont = true; propertiesPack->width = -1;
removePack->smallFont = true; removePack->width = -1;
move->smallFont = true; move->width = -1;
remove->smallFont = true; remove->width = -1;
edit->smallFont = true; edit->width = -1;
}
// NOTE: the following code is necessary (e.g. for Germany)
//Check again
GUIObjectRoot->render(renderer, 0, 0, false);
if (propertiesPack->left - propertiesPack->gravityX < newPack->left + newPack->width ||
propertiesPack->left - propertiesPack->gravityX + propertiesPack->width > removePack->left - removePack->gravityX)
{
newPack->left = SCREEN_WIDTH*0.02;
newPack->top = SCREEN_HEIGHT - 140;
newPack->smallFont = false;
newPack->width = -1;
newPack->gravity = GUIGravityLeft;
propertiesPack->left = SCREEN_WIDTH*0.02;
propertiesPack->top = SCREEN_HEIGHT - 100;
propertiesPack->smallFont = false;
propertiesPack->width = -1;
propertiesPack->gravity = GUIGravityLeft;
removePack->left = SCREEN_WIDTH*0.02;
removePack->top = SCREEN_HEIGHT - 60;
removePack->smallFont = false;
removePack->width = -1;
removePack->gravity = GUIGravityLeft;
move->left = SCREEN_WIDTH*0.98;
move->top = SCREEN_HEIGHT - 140;
move->smallFont = false;
move->width = -1;
move->gravity = GUIGravityRight;
remove->left = SCREEN_WIDTH*0.98;
remove->top = SCREEN_HEIGHT - 100;
remove->smallFont = false;
remove->width = -1;
remove->gravity = GUIGravityRight;
edit->left = SCREEN_WIDTH*0.98;
edit->top = SCREEN_HEIGHT - 60;
edit->smallFont = false;
edit->width = -1;
edit->gravity = GUIGravityRight;
isVertical = true;
}
}
void LevelEditSelect::changePack(){
packPath = levelpacks->item[levelpacks->value].first;
packName = levelpacks->item[levelpacks->value].second;
if(packPath==CUSTOM_LEVELS_PATH){
//Disable some levelpack buttons.
propertiesPack->enabled=false;
removePack->enabled=false;
}else{
//Enable some levelpack buttons.
propertiesPack->enabled=true;
removePack->enabled=true;
}
//Set last levelpack.
getSettings()->setValue("lastlevelpack",levelpacks->getName());
//Now let levels point to the right pack.
levels=getLevelPackManager()->getLevelPack(levelpacks->getName());
//invalidate the tooltip
toolTip.number = -1;
}
void LevelEditSelect::packProperties(ImageManager& imageManager,SDL_Renderer& renderer, bool newPack){
//Open a message popup.
GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-390)/2,600,390,_("Properties"));
GUIObject* obj;
obj=new GUILabel(imageManager,renderer,40,50,240,36,_("Name:"));
root->addChild(obj);
obj=new GUITextBox(imageManager,renderer,60,80,480,36,packName.c_str());
if(newPack)
obj->caption="";
obj->name="LvlpackName";
root->addChild(obj);
obj=new GUILabel(imageManager,renderer,40,120,240,36,_("Description:"));
root->addChild(obj);
obj=new GUITextBox(imageManager,renderer,60,150,480,36,levels->levelpackDescription.c_str());
if(newPack)
obj->caption="";
obj->name="LvlpackDescription";
root->addChild(obj);
obj=new GUILabel(imageManager,renderer,40,190,240,36,_("Congratulation text:"));
root->addChild(obj);
obj=new GUITextBox(imageManager,renderer,60,220,480,36,levels->congratulationText.c_str());
if(newPack)
obj->caption="";
obj->name="LvlpackCongratulation";
root->addChild(obj);
obj = new GUILabel(imageManager, renderer, 40, 260, 240, 36, _("Music list:"));
root->addChild(obj);
obj = new GUITextBox(imageManager, renderer, 60, 290, 480, 36, levels->levelpackMusicList.c_str());
if (newPack)
obj->caption = "";
obj->name = "LvlpackMusic";
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.3,390-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
obj->name="cfgOK";
obj->eventCallback=this;
root->addChild(obj);
GUIButton *cancelButton = new GUIButton(imageManager, renderer, root->width*0.7, 390 - 44, -1, 36, _("Cancel"), 0, true, true, GUIGravityCenter);
cancelButton->name = "cfgCancel";
cancelButton->eventCallback = this;
root->addChild(cancelButton);
//Create the gui overlay.
//NOTE: We don't need to store a pointer since it will auto cleanup itself.
new AddonOverlay(renderer, root, cancelButton, NULL, UpDownFocus | TabFocus | ReturnControls | LeftRightControls);
if(newPack){
packPath.clear();
packName.clear();
}
}
void LevelEditSelect::addLevel(ImageManager& imageManager,SDL_Renderer& renderer){
//Open a message popup.
GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-200)/2,600,200,_("Add level"));
GUIObject* obj;
obj=new GUILabel(imageManager,renderer,40,80,240,36,_("File name:"));
root->addChild(obj);
char s[64];
SDL_snprintf(s,64,"map%02d.map",levels->getLevelCount()+1);
obj=new GUITextBox(imageManager,renderer,300,80,240,36,s);
obj->name="LvlFile";
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.3,200-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
obj->name="cfgAddOK";
obj->eventCallback=this;
root->addChild(obj);
GUIButton *cancelButton = new GUIButton(imageManager, renderer, root->width*0.7, 200 - 44, -1, 36, _("Cancel"), 0, true, true, GUIGravityCenter);
cancelButton->name = "cfgAddCancel";
cancelButton->eventCallback = this;
root->addChild(cancelButton);
//Dim the screen using the tempSurface.
//NOTE: We don't need to store a pointer since it will auto cleanup itself.
new AddonOverlay(renderer, root, cancelButton, NULL, UpDownFocus | TabFocus | ReturnControls | LeftRightControls);
}
void LevelEditSelect::moveLevel(ImageManager& imageManager,SDL_Renderer& renderer){
//Open a message popup.
GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-200)/2,600,200,_("Move level"));
GUIObject* obj;
obj=new GUILabel(imageManager,renderer,40,60,240,36,_("Level: "));
root->addChild(obj);
GUISpinBox *spinBox = new GUISpinBox(imageManager, renderer, 300, 60, 240, 36);
spinBox->caption = tfm::format("%d", selectedNumber->getNumber() + 1);
spinBox->format = "%1.0f";
spinBox->limitMin = 1.0f;
spinBox->limitMax = float(levels->getLevelCount());
spinBox->name = "MoveLevel";
root->addChild(spinBox);
obj=new GUISingleLineListBox(imageManager,renderer,root->width*0.5,110,240,36,true,true,GUIGravityCenter);
obj->name="lstPlacement";
vector<string> v;
v.push_back(_("Before"));
v.push_back(_("After"));
v.push_back(_("Swap"));
(dynamic_cast<GUISingleLineListBox*>(obj))->addItems(v);
obj->value=0;
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.3,200-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
obj->name="cfgMoveOK";
obj->eventCallback=this;
root->addChild(obj);
GUIButton *cancelButton = new GUIButton(imageManager, renderer, root->width*0.7, 200 - 44, -1, 36, _("Cancel"), 0, true, true, GUIGravityCenter);
cancelButton->name = "cfgMoveCancel";
cancelButton->eventCallback = this;
root->addChild(cancelButton);
//Create the gui overlay.
//NOTE: We don't need to store a pointer since it will auto cleanup itself.
new AddonOverlay(renderer, root, cancelButton, NULL, TabFocus | ReturnControls | LeftRightControls);
}
void LevelEditSelect::refresh(ImageManager& imageManager, SDL_Renderer& renderer, bool change){
int m=levels->getLevelCount();
if(change){
numbers.clear();
//clear the selected level
if(selectedNumber!=NULL){
selectedNumber=NULL;
}
//Disable the level specific buttons.
move->enabled=false;
remove->enabled=false;
edit->enabled=false;
for(int n=0;n<=m;n++){
numbers.emplace_back(imageManager, renderer);
}
}
for(int n=0;n<m;n++){
SDL_Rect box={(n%LEVELS_PER_ROW)*64+80,(n/LEVELS_PER_ROW)*64+184,0,0};
numbers[n].init(renderer,n,box);
}
SDL_Rect box={(m%LEVELS_PER_ROW)*64+80,(m/LEVELS_PER_ROW)*64+184,0,0};
numbers[m].init(renderer,"+",box,m);
m++; //including the "+" button
if(m>LEVELS_DISPLAYED_IN_SCREEN){
levelScrollBar->maxValue=(m-LEVELS_DISPLAYED_IN_SCREEN+LEVELS_PER_ROW-1)/LEVELS_PER_ROW;
levelScrollBar->visible=true;
}else{
levelScrollBar->maxValue=0;
levelScrollBar->visible=false;
}
if (levels->levelpackPath == LEVELS_PATH || levels->levelpackPath == CUSTOM_LEVELS_PATH)
levelpackDescription->caption = _("Individual levels which are not contained in any level packs");
else if (!levels->levelpackDescription.empty())
levelpackDescription->caption = _CC(levels->getDictionaryManager(), levels->levelpackDescription);
else
levelpackDescription->caption = "";
//invalidate the tooltip
toolTip.number = -1;
}
void LevelEditSelect::selectNumber(ImageManager& imageManager, SDL_Renderer& renderer, unsigned int number, bool selected){
if (selected) {
if (number >= 0 && number < levels->getLevelCount()) {
levels->setCurrentLevel(number);
setNextState(STATE_LEVEL_EDITOR);
} else {
addLevel(imageManager, renderer);
}
}else{
move->enabled = false;
remove->enabled = false;
edit->enabled = false;
selectedNumber = NULL;
if (number == numbers.size() - 1){
if (isKeyboardOnly) {
selectedNumber = &numbers[number];
} else {
addLevel(imageManager, renderer);
}
} else if (number >= 0 && number < levels->getLevelCount()) {
selectedNumber=&numbers[number];
//Enable the level specific buttons.
//NOTE: We check if 'remove levelpack' is enabled, if not then it's the Levels levelpack.
if(removePack->enabled)
move->enabled=true;
remove->enabled=true;
edit->enabled=true;
}
}
}
void LevelEditSelect::handleEvents(ImageManager& imageManager, SDL_Renderer& renderer){
//Call handleEvents() of base class.
LevelSelect::handleEvents(imageManager, renderer);
if (section == 3) {
//Check focus movement
if (inputMgr.isKeyDownEvent(INPUTMGR_RIGHT)){
isKeyboardOnly = true;
section2 += isVertical ? 3 : 1;
} else if (inputMgr.isKeyDownEvent(INPUTMGR_LEFT)){
isKeyboardOnly = true;
section2 -= isVertical ? 3 : 1;
} else if (inputMgr.isKeyDownEvent(INPUTMGR_UP)){
isKeyboardOnly = true;
section2 -= isVertical ? 1 : 3;
} else if (inputMgr.isKeyDownEvent(INPUTMGR_DOWN)){
isKeyboardOnly = true;
section2 += isVertical ? 1 : 3;
}
if (section2 > 6) section2 -= 6;
else if (section2 < 1) section2 += 6;
//Check if enter is pressed
if (isKeyboardOnly && inputMgr.isKeyDownEvent(INPUTMGR_SELECT) && section2 >= 1 && section2 <= 6) {
GUIButton *buttons[6] = {
newPack, propertiesPack, removePack, move, remove, edit
};
GUIEventCallback_OnEvent(imageManager, renderer, buttons[section2 - 1]->name, buttons[section2 - 1], GUIEventClick);
}
}
}
void LevelEditSelect::render(ImageManager& imageManager,SDL_Renderer &renderer){
//Let the levelselect render.
LevelSelect::render(imageManager,renderer);
//Draw highlight in keyboard only mode.
if (isKeyboardOnly) {
GUIButton *buttons[6] = {
newPack, propertiesPack, removePack, move, remove, edit
};
for (int i = 0; i < 6; i++) {
buttons[i]->state = (section == 3 && section2 - 1 == i) ? 1 : 0;
}
}
}
void LevelEditSelect::resize(ImageManager& imageManager, SDL_Renderer &renderer){
//Let the levelselect resize.
LevelSelect::resize(imageManager, renderer);
//Create the GUI.
createGUI(imageManager,renderer, false);
//NOTE: This is a workaround for buttons failing when resizing.
if(packPath==CUSTOM_LEVELS_PATH){
removePack->enabled=false;
propertiesPack->enabled=false;
}
if(selectedNumber)
selectNumber(imageManager, renderer, selectedNumber->getNumber(),false);
}
void LevelEditSelect::renderTooltip(SDL_Renderer& renderer,unsigned int number,int dy){
if (!toolTip.name || toolTip.number != number) {
SDL_Color fg = objThemes.getTextColor(true);
toolTip.number = number;
if (number < (unsigned int)levels->getLevelCount()){
//Render the name of the level.
toolTip.name = textureFromText(renderer, *fontText, _CC(levels->getDictionaryManager(), levels->getLevelName(number)), fg);
} else {
//Add level button
toolTip.name = textureFromText(renderer, *fontText, _("Add level"), fg);
}
}
//Check if name isn't null.
if(!toolTip.name)
return;
//Now draw a square the size of the three texts combined.
SDL_Rect r=numbers[number].box;
r.y-=dy*64;
const SDL_Rect nameSize = rectFromTexture(*toolTip.name);
r.w=nameSize.w;
r.h=nameSize.h;
//Make sure the tooltip doesn't go outside the window.
if(r.y>SCREEN_HEIGHT-200){
r.y-=nameSize.h+4;
}else{
r.y+=numbers[number].box.h+2;
}
if(r.x+r.w>SCREEN_WIDTH-50)
r.x=SCREEN_WIDTH-50-r.w;
//Draw a rectange
Uint32 color=0xFFFFFFFF;
drawGUIBox(r.x-5,r.y-5,r.w+10,r.h+10,renderer,color);
//Calc the position to draw.
SDL_Rect r2=r;
//Now we render the name if the surface isn't null.
if(toolTip.name){
//Draw the name.
applyTexture(r2.x, r2.y, toolTip.name, renderer);
}
}
//Escape invalid characters in a file name (mainly for Windows).
static std::string escapeFileName(const std::string& fileName) {
std::string ret;
for (int i = 0, m = fileName.size(); i < m; i++) {
bool escape = false;
char c = fileName[i];
switch (c) {
case '\"': case '*': case '/': case ':': case '<':
case '>': case '?': case '\\': case '|': case '%':
escape = true;
break;
}
if (c <= 0x1F || c >= 0x7F) escape = true;
if (i == 0 || i == m - 1) {
switch (c) {
case ' ': case '.':
escape = true;
break;
}
}
if (escape) {
ret += "%" + tfm::format("%02X", (int)(unsigned char)c);
} else {
ret.push_back(c);
}
}
return ret;
}
void LevelEditSelect::GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType){
//NOTE: We check for the levelpack change to enable/disable some levelpack buttons.
if(name=="cmdLvlPack"){
//We call changepack and return to prevent the LevelSelect to undo what we did.
changePack();
refresh(imageManager, renderer);
return;
}
//Let the level select handle his GUI events.
LevelSelect::GUIEventCallback_OnEvent(imageManager,renderer,name,obj,eventType);
//Check for the edit button.
if(name=="cmdNewLvlpack"){
//Create a new pack.
packProperties(imageManager,renderer, true);
}else if(name=="cmdLvlpackProp"){
//Show the pack properties.
packProperties(imageManager,renderer, false);
}else if(name=="cmdRmLvlpack"){
//Show an "are you sure" message.
if(msgBox(imageManager,renderer,tfm::format(_("Are you sure remove the level pack '%s'?"),packName),MsgBoxYesNo,_("Remove prompt"))==MsgBoxYes){
//Remove the directory.
if(!removeDirectory(levels->levelpackPath.c_str())){
cerr<<"ERROR: Unable to remove levelpack directory "<<levels->levelpackPath<<endl;
}
-
+
//Remove it from the vector (levelpack list).
- vector<pair<string,string> >::iterator it;
- for(it=levelpacks->item.begin();it!=levelpacks->item.end();++it){
- if (it->first == levels->levelpackPath)
+ for (auto it = levelpacks->item.begin(); it != levelpacks->item.end(); ++it){
+ if (it->first == levels->levelpackPath) {
levelpacks->item.erase(it);
+ break;
+ }
}
-
+
//Remove it from the levelpackManager.
getLevelPackManager()->removeLevelPack(levels->levelpackPath, true);
levels = NULL;
//And call changePack.
levelpacks->value=levelpacks->item.size()-1;
changePack();
refresh(imageManager, renderer);
}
}else if(name=="cmdMoveMap"){
if(selectedNumber!=NULL){
moveLevel(imageManager,renderer);
}
}else if(name=="cmdRmMap"){
if(selectedNumber!=NULL){
//Show an "are you sure" message.
if (msgBox(imageManager, renderer, tfm::format(_("Are you sure remove the map '%s'?"), levels->getLevel(selectedNumber->getNumber())->name), MsgBoxYesNo, _("Remove prompt")) != MsgBoxYes) {
return;
}
if(packPath!=CUSTOM_LEVELS_PATH){
if(!removeFile((levels->levelpackPath+"/"+levels->getLevel(selectedNumber->getNumber())->file).c_str())){
cerr<<"ERROR: Unable to remove level "<<(levels->levelpackPath+"/"+levels->getLevel(selectedNumber->getNumber())->file).c_str()<<endl;
}
levels->removeLevel(selectedNumber->getNumber());
levels->saveLevels(levels->levelpackPath+"/levels.lst");
}else{
//This is the levels levelpack so we just remove the file.
if(!removeFile(levels->getLevel(selectedNumber->getNumber())->file.c_str())){
cerr<<"ERROR: Unable to remove level "<<levels->getLevel(selectedNumber->getNumber())->file<<endl;
}
levels->removeLevel(selectedNumber->getNumber());
}
//And refresh the selection screen.
refresh(imageManager, renderer);
}
}else if(name=="cmdEdit"){
if(selectedNumber!=NULL){
levels->setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_LEVEL_EDITOR);
}
}
//Check for levelpack properties events.
if(name=="cfgOK"){
GUIObject *lvlpackName = GUIObjectRoot->getChild("LvlpackName");
GUIObject *lvlpackDescription = GUIObjectRoot->getChild("LvlpackDescription");
GUIObject *lvlpackCongratulation = GUIObjectRoot->getChild("LvlpackCongratulation");
GUIObject *lvlpackMusic = GUIObjectRoot->getChild("LvlpackMusic");
assert(lvlpackName && lvlpackDescription && lvlpackCongratulation && lvlpackMusic);
if (lvlpackName->caption.empty()) {
msgBox(imageManager, renderer, _("Levelpack name cannot be empty."), MsgBoxOKOnly, _("Error"));
} else {
//Check if the name changed.
if (packName != lvlpackName->caption) {
std::string newPackPathMinusSlash = getUserPath(USER_DATA) + "custom/levelpacks/" + escapeFileName(lvlpackName->caption);
//Delete the old one.
if (!packName.empty()){
std::string oldPackPathMinusSlash = levels->levelpackPath;
if (!oldPackPathMinusSlash.empty()) {
if (oldPackPathMinusSlash[oldPackPathMinusSlash.size() - 1] == '/'
|| oldPackPathMinusSlash[oldPackPathMinusSlash.size() - 1] == '\\')
{
oldPackPathMinusSlash.pop_back();
}
}
if (!renameDirectory(oldPackPathMinusSlash.c_str(), newPackPathMinusSlash.c_str())) {
- cerr << "ERROR: Unable to move levelpack directory " << oldPackPathMinusSlash << " to " << newPackPathMinusSlash << endl;
+ cerr << "ERROR: Unable to move levelpack directory '" << oldPackPathMinusSlash << "' to '"
+ << newPackPathMinusSlash << "'! The levelpack directory will be kept unchanged." << endl;
//If we failed to rename the directory, we just keep the old directory name.
newPackPathMinusSlash = oldPackPathMinusSlash;
}
//Remove the old one from the levelpack manager.
getLevelPackManager()->removeLevelPack(levels->levelpackPath, false);
//And the levelpack list.
for (auto it = levelpacks->item.begin(); it != levelpacks->item.end(); ++it){
if (it->first == levels->levelpackPath) {
levelpacks->item.erase(it);
break;
}
}
} else {
- //It's a new levelpack so we need to change the levels array.
- LevelPack* pack = new LevelPack;
- levels = pack;
-
- //Now create the dirs.
+ //It's a new levelpack. First we try to create the dirs and the levels.lst.
+ if (dirExists(newPackPathMinusSlash.c_str())) {
+ cerr << "ERROR: The levelpack directory " << newPackPathMinusSlash << " already exists!" << endl;
+ msgBox(imageManager, renderer, tfm::format(_("The levelpack directory '%s' already exists!"), newPackPathMinusSlash), MsgBoxOKOnly, _("Error"));
+ return;
+ }
if (!createDirectory(newPackPathMinusSlash.c_str())) {
cerr << "ERROR: Unable to create levelpack directory " << newPackPathMinusSlash << endl;
+ msgBox(imageManager, renderer, tfm::format(_("Unable to create levelpack directory '%s'!"), newPackPathMinusSlash), MsgBoxOKOnly, _("Error"));
+ return;
+ }
+ if (fileExists((newPackPathMinusSlash + "/levels.lst").c_str())) {
+ cerr << "ERROR: The levelpack file " << (newPackPathMinusSlash + "/levels.lst") << " already exists!" << endl;
+ msgBox(imageManager, renderer, tfm::format(_("The levelpack file '%s' already exists!"), newPackPathMinusSlash + "/levels.lst"), MsgBoxOKOnly, _("Error"));
+ return;
}
- if (!createFile((newPackPathMinusSlash + "/levels.lst").c_str())){
+ if (!createFile((newPackPathMinusSlash + "/levels.lst").c_str())) {
cerr << "ERROR: Unable to create levelpack file " << (newPackPathMinusSlash + "/levels.lst") << endl;
+ msgBox(imageManager, renderer, tfm::format(_("Unable to create levelpack file '%s'!"), newPackPathMinusSlash + "/levels.lst"), MsgBoxOKOnly, _("Error"));
+ return;
}
+
+ //If it's successful we create a new levelpack.
+ levels = new LevelPack;
}
//And set the new name.
packName = levels->levelpackName = lvlpackName->caption;
packPath = levels->levelpackPath = newPackPathMinusSlash + "/";
//Also add the levelpack location
getLevelPackManager()->addLevelPack(levels);
levelpacks->addItem(packPath, packName);
levelpacks->value = levelpacks->item.size() - 1;
//And call changePack.
changePack();
}
levels->levelpackDescription = lvlpackDescription->caption;
levels->congratulationText = lvlpackCongratulation->caption;
levels->levelpackMusicList = lvlpackMusic->caption;
//Refresh the leveleditselect to show the correct information.
refresh(imageManager, renderer);
//Save the configuration.
levels->saveLevels(levels->levelpackPath + "levels.lst");
getSettings()->setValue("lastlevelpack", levels->levelpackPath);
//Clear the gui.
if (GUIObjectRoot) {
delete GUIObjectRoot;
GUIObjectRoot = NULL;
}
}
}else if(name=="cfgCancel"){
//Check if packName is empty, if so it was a new levelpack and we need to revert to an existing one.
if(packName.empty()){
packPath = levelpacks->item[levelpacks->value].first;
packName = levelpacks->item[levelpacks->value].second;
changePack();
}
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for add level events.
if(name=="cfgAddOK"){
//Check if the file name isn't null.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="LvlFile"){
if(GUIObjectRoot->childControls[i]->caption.empty()){
msgBox(imageManager,renderer,_("No file name given for the new level."),MsgBoxOKOnly,_("Missing file name"));
return;
}else{
string tmp_caption = GUIObjectRoot->childControls[i]->caption;
//Replace all spaces with a underline.
size_t j;
for(;(j=tmp_caption.find(" "))!=string::npos;){
tmp_caption.replace(j,1,"_");
}
//If there isn't ".map" extension add it.
size_t found=tmp_caption.find_first_of(".");
if(found!=string::npos)
tmp_caption.replace(tmp_caption.begin()+found+1,tmp_caption.end(),"map");
else if (tmp_caption.substr(found+1)!="map")
tmp_caption.append(".map");
/* Create path and file in it */
string path=(levels->levelpackPath+"/"+tmp_caption);
if(packPath==CUSTOM_LEVELS_PATH){
path=(getUserPath(USER_DATA)+"/custom/levels/"+tmp_caption);
}
//First check if the file doesn't exist already.
FILE* f;
f=fopen(path.c_str(),"rb");
//Check if it exists.
if(f){
//Close the file.
fclose(f);
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render(imageManager,renderer);
levelEditGUIObjectRoot->render(renderer);
//Notify the user.
msgBox(imageManager, renderer, tfm::format(_("The file %s already exists."), tmp_caption), MsgBoxOKOnly, _("Error"));
return;
}
if(!createFile(path.c_str())){
cerr<<"ERROR: Unable to create level file "<<path<<endl;
}else{
//Update statistics.
statsMgr.newAchievement("create1");
if((++statsMgr.createdLevels)>=50) statsMgr.newAchievement("create50");
}
levels->addLevel(path);
//NOTE: Also add the level to the levels levelpack in case of custom levels.
if(packPath==CUSTOM_LEVELS_PATH){
LevelPack* levelsPack=getLevelPackManager()->getLevelPack(LEVELS_PATH);
if(levelsPack){
levelsPack->addLevel(path);
levelsPack->setLocked(levelsPack->getLevelCount()-1);
}else{
cerr<<"ERROR: Unable to add level to Levels levelpack"<<endl;
}
}
if(packPath!=CUSTOM_LEVELS_PATH)
levels->saveLevels(levels->levelpackPath+"levels.lst");
refresh(imageManager, renderer);
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
return;
}
}
}
}
}else if(name=="cfgAddCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for move level events.
if(name=="cfgMoveOK"){
//Check if the entered level number is valid.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
int level=0;
int placement=0;
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="MoveLevel"){
level=atoi(GUIObjectRoot->childControls[i]->caption.c_str());
if(level<=0 || level>levels->getLevelCount()){
msgBox(imageManager,renderer,_("The entered level number isn't valid!"),MsgBoxOKOnly,_("Illegal number"));
return;
}
}
if(GUIObjectRoot->childControls[i]->name=="lstPlacement"){
placement=GUIObjectRoot->childControls[i]->value;
}
}
//Now we execute the swap/move.
//Check for the place before.
if(placement==0){
//We place the selected level before the entered level.
levels->moveLevel(selectedNumber->getNumber(),level-1);
}else if(placement==1){
//We place the selected level after the entered level.
if(level<selectedNumber->getNumber())
levels->moveLevel(selectedNumber->getNumber(),level);
else
levels->moveLevel(selectedNumber->getNumber(),level+1);
}else if(placement==2){
//We swap the selected level with the entered level.
levels->swapLevel(selectedNumber->getNumber(),level-1);
}
//And save the change.
if(packPath!=CUSTOM_LEVELS_PATH)
levels->saveLevels(levels->levelpackPath+"/levels.lst");
refresh(imageManager, renderer);
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="cfgMoveCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, May 16, 8:16 PM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63488
Default Alt Text
(99 KB)
Attached To
Mode
R79 meandmyshadow
Attached
Detach File
Event Timeline