1 /*
2  *
3  *   Copyright (c) 1994, 2002, 2003 Johannes Prix
4  *   Copyright (c) 1994, 2002 Reinhard Prix
5  *   Copyright (c) 2004-2010 Arthur Huillet
6  *
7  *
8  *  This file is part of Freedroid
9  *
10  *  Freedroid is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  Freedroid is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with Freedroid; see the file COPYING. If not, write to the
22  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23  *  MA  02111-1307  USA
24  *
25  */
26 /**
27  * This file contains miscellaeous helpful functions for FreedroidRPG.
28  */
29 #define _misc_c
30 
31 #include "system.h"
32 
33 #include "defs.h"
34 #include "struct.h"
35 #include "global.h"
36 #include "proto.h"
37 #include "savestruct.h"
38 
39 #include "widgets/widgets.h"
40 #include "lvledit/lvledit.h"
41 
42 #include <stdlib.h>
43 #if HAVE_EXECINFO_H
44 #  include <execinfo.h>
45 #endif
46 #if HAVE_SIGNAL_H
47 #  include <signal.h>
48 #endif
49 
50 static int world_is_frozen = 0;
51 long oneframedelay = 0;
52 float FPSover1 = 10;
53 Uint32 Now_SDL_Ticks;
54 Uint32 One_Frame_SDL_Ticks;
55 Uint32 Ten_Frame_SDL_Ticks;
56 Uint32 Onehundred_Frame_SDL_Ticks;
57 int framenr = 0;
58 long Overall_Frames_Displayed = 0;
59 
60 char *our_homedir = NULL;
61 char *our_config_dir = NULL;
62 
63 struct data_dir data_dirs[] = {
64 	[GRAPHICS_DIR]= { "graphics",      "" },
65 	[FONT_DIR]=     { "graphics/font", "" },
66 	[SOUND_DIR]=    { "sound",         "" },
67 	[MUSIC_DIR]=    { "sound/music",   "" },
68 	[MAP_DIR]=      { "map",           "" },
69 	[TITLES_DIR]=   { "map/titles",    "" },
70 	[DIALOG_DIR]=   { "dialogs",       "" },
71 #ifdef ENABLE_NLS
72 	[LOCALE_DIR]=   { "locale",        "" },
73 #endif
74 	[LUA_MOD_DIR]=  { "lua_modules",   "" }
75 };
76 #define WELL_KNOWN_DATA_FILE "lua_modules/FDdialog.lua"
77 
78 mouse_press_button AllMousePressButtons[MAX_MOUSE_PRESS_BUTTONS] = {
79 	[UP_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/UpButton.png", {600, 94, 40, 40}, TRUE},
80 	[DOWN_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/DownButton.png", {600, 316, 40, 40}, TRUE},
81 
82 	[ITEM_BROWSER_LEFT_BUTTON] = {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {280, 44, 37, 37}, TRUE},
83 	[ITEM_BROWSER_RIGHT_BUTTON] = {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {536, 44, 37, 37}, TRUE},
84 	[ITEM_BROWSER_EXIT_BUTTON] = {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {201, 340, 47, 47}, TRUE},
85 
86 	[LEFT_SHOP_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/LeftButton.png", {23, 446, 23, 23}, TRUE},
87 	[RIGHT_SHOP_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/RightButton.png", {580, 447, 23, 23}, TRUE},
88 	[LEFT_TUX_SHOP_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/LeftShopButton.png", {6, 15, 23, 23}, TRUE},
89 	[RIGHT_TUX_SHOP_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/RightShopButton.png", {584, 15, 23, 23}, TRUE},
90 	[LEFT_LEVEL_EDITOR_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/LevelEditorObjectSelectorLeft.png", {3, 8, 15, 60}, FALSE},
91 	[LEFT_LEVEL_EDITOR_BUTTON_PUSHED] =
92 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorObjectSelectorLeftPushed.png", {2, 7, 15, 60}, FALSE},
93 	[RIGHT_LEVEL_EDITOR_BUTTON] =
94 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorObjectSelectorRight.png", {-16, 8, 15, 60}, FALSE},
95 	[RIGHT_LEVEL_EDITOR_BUTTON_PUSHED] =
96 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorObjectSelectorRightPushed.png", {-17, 7, 15, 60}, FALSE},
97 
98 	[NUMBER_SELECTOR_OK_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/number_selector_ok_button.png", {308, 288, 48, 48}, TRUE},
99 	[NUMBER_SELECTOR_LEFT_BUTTON] = {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {148, 244, 35, 35}, TRUE},
100 	[NUMBER_SELECTOR_RIGHT_BUTTON] = {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {404, 244, 35, 35}, TRUE},
101 
102 	[BUY_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/buy_button.png", {199, 98, 47, 47}, TRUE},
103 	[SELL_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/sell_button.png", {199, 153, 47, 47}, TRUE},
104 	[REPAIR_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/repair_button.png", {199, 225, 47, 47}, TRUE},
105 
106 	[OPEN_CLOSE_SKILL_EXPLANATION_BUTTON] = {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {0 + 17, 424, 33, 33}, FALSE},
107 
108 
109 /* lower right area next to the mini map*/
110 	[LEVEL_EDITOR_DELETE_OBSTACLE_BUTTON] =
111 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorDeleteObstacleButton.png", {-270, -30, 0, 0}, FALSE},
112 	[LEVEL_EDITOR_DELETE_OBSTACLE_BUTTON_PUSHED] =
113 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorDeleteObstacleButtonPushed.png", {-271, -31, 0, 0}, FALSE},
114 	[LEVEL_EDITOR_NEXT_OBJECT_BUTTON] =
115 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorNextObstacleButton.png", {-240, -30, 0, 0}, FALSE},
116 	[LEVEL_EDITOR_NEXT_OBJECT_BUTTON_PUSHED] =
117 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorNextObstacleButtonPushed.png", {-241, -31, 0, 0}, FALSE},
118 
119 
120 /* upper right are directly under the object selector*/
121 	[LEVEL_EDITOR_SAVE_SHIP_BUTTON] =
122 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorSaveShipButton.png", {-60, 80, 0, 0}, FALSE},
123 	[LEVEL_EDITOR_SAVE_SHIP_BUTTON_PUSHED] =
124 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorSaveShipButtonPushed.png", {-59, 79, 0, 0}, FALSE},
125 	[LEVEL_EDITOR_SAVE_SHIP_BUTTON_OFF] =
126 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorSaveShipButtonOff.png", {-60, 80, 0, 0}, FALSE},
127 	[LEVEL_EDITOR_SAVE_SHIP_BUTTON_OFF_PUSHED] =
128 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorSaveShipButtonOffPushed.png", {-59, 79, 0, 0}, FALSE},
129 	[LEVEL_EDITOR_QUIT_BUTTON] =
130 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorQuitButton.png", {-30, 80, 0, 0}, FALSE},
131 	[LEVEL_EDITOR_QUIT_BUTTON_PUSHED] =
132 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorQuitButtonPushed.png", {-29, 79, 0, 0}, FALSE},
133 
134 
135 /* above the upper row, the very upper row */
136 	[LEVEL_EDITOR_TOGGLE_MAP_LABELS_BUTTON] =
137 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleMapLabelsButton.png", {-30, -340, 0, 0}, FALSE},
138 	[LEVEL_EDITOR_TOGGLE_MAP_LABELS_BUTTON_PUSHED] =
139 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleMapLabelsButtonPushed.png", {-29, -339, 0, 0}, FALSE},
140 	[LEVEL_EDITOR_TOGGLE_MAP_LABELS_BUTTON_OFF] =
141 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleMapLabelsButtonOff.png", {-30, -340, 0, 0}, FALSE},
142 	[LEVEL_EDITOR_TOGGLE_MAP_LABELS_BUTTON_OFF_PUSHED] =
143 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleMapLabelsButtonOffPushed.png", {-29, -339, 0, 0}, FALSE},
144 
145 
146 /* above the obstacle selectors, upper row */
147 	[LEVEL_EDITOR_EDIT_CHEST_BUTTON] =
148 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorEditChestButton.png", {-150, -310, 0, 0}, FALSE},
149 	[LEVEL_EDITOR_EDIT_CHEST_BUTTON_PUSHED] =
150 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorEditChestButtonPushed.png", {-149, -309, 0, 0}, FALSE},
151 	[LEVEL_EDITOR_NEW_OBSTACLE_LABEL_BUTTON] =
152 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorNewObstacleLabelButton.png", {-120, -310, 0, 0}, FALSE},
153 	[LEVEL_EDITOR_NEW_OBSTACLE_LABEL_BUTTON_PUSHED] =
154 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorNewObstacleLabelButtonPushed.png", {-119, -309, 0, 0}, FALSE},
155 	[LEVEL_EDITOR_TOGGLE_WAYPOINT_CONNECTIONS_BUTTON] =
156 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleWaypointConnectionsButton.png", {-90, -310, 0, 0}, FALSE},
157 	[LEVEL_EDITOR_TOGGLE_WAYPOINT_CONNECTIONS_BUTTON_PUSHED] =
158 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleWaypointConnectionsButtonPushed.png", {-90, -310, 0, 0}, FALSE},
159 	[LEVEL_EDITOR_TOGGLE_WAYPOINT_CONNECTIONS_BUTTON_OFF] =
160 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleWaypointConnectionsButtonOff.png", {-90, -310, 0, 0}, FALSE},
161 	[LEVEL_EDITOR_TOGGLE_WAYPOINT_CONNECTIONS_BUTTON_OFF_PUSHED] =
162 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleWaypointConnectionsButtonOffPushed.png", {-90, -310, 0, 0}, FALSE},
163 	[LEVEL_EDITOR_ZOOM_IN_BUTTON] =
164 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorZoomInButton.png", {-60, -310, 0, 0}, FALSE},
165 	[LEVEL_EDITOR_ZOOM_IN_BUTTON_PUSHED] =
166 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorZoomInButtonPushed.png", {-59, -309, 0, 0}, FALSE},
167 	[LEVEL_EDITOR_ZOOM_OUT_BUTTON] =
168 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorZoomOutButton.png", {-60, -310, 0, 0}, FALSE},
169 	[LEVEL_EDITOR_ZOOM_OUT_BUTTON_PUSHED] =
170 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorZoomOutButtonPushed.png", {-59, -309, 0, 0}, FALSE},
171 	[LEVEL_EDITOR_BEAUTIFY_GRASS_BUTTON] =
172 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorBeautifyGrassButton.png", {-30, -310, 0, 0}, FALSE},
173 	[LEVEL_EDITOR_BEAUTIFY_GRASS_BUTTON_PUSHED] =
174 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorBeautifyGrassButtonPushed.png", {-29, -309, 0, 0}, FALSE},
175 	[LEVEL_EDITOR_ALL_FLOOR_LAYERS_BUTTON] =
176 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorAllFloorLayersButton.png", {-120, -310, 0, 0}, FALSE},
177 	[LEVEL_EDITOR_ALL_FLOOR_LAYERS_BUTTON_PUSHED] =
178 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorAllFloorLayersButtonPushed.png", {-120, -310, 0, 0}, FALSE},
179 	[LEVEL_EDITOR_SINGLE_FLOOR_LAYER_BUTTON] =
180 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorSingleFloorLayerButton.png", {-120, -310, 0, 0}, FALSE},
181 	[LEVEL_EDITOR_SINGLE_FLOOR_LAYER_BUTTON_PUSHED] =
182 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorSingleFloorLayerButtonPushed.png", {-120, -310, 0, 0}, FALSE},
183 
184 
185 /* above the obstacle selector, lower row*/
186 	[LEVEL_EDITOR_TOGGLE_GRID_BUTTON_OFF] =
187 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleGridButtonOff.png", {-150, -280, 0, 0}, FALSE},
188 	[LEVEL_EDITOR_TOGGLE_GRID_BUTTON_OFF_PUSHED] =
189 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleGridButtonOffPushed.png", {-149, -279, 0, 0}, FALSE},
190 	[LEVEL_EDITOR_TOGGLE_GRID_BUTTON] =
191 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleGridButton.png", {-150, -280, 0, 0}, FALSE},
192 	[LEVEL_EDITOR_TOGGLE_GRID_BUTTON_PUSHED] =
193 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleGridButtonPushed.png", {-149, -279, 0, 0}, FALSE},
194 	[LEVEL_EDITOR_TOGGLE_GRID_BUTTON_FULL] =
195 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleGridButtonFull.png", {-150, -280, 0, 0}, FALSE},
196 	[LEVEL_EDITOR_TOGGLE_GRID_BUTTON_FULL_PUSHED] =
197 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleGridButtonFullPushed.png", {-149, -279, 0, 0}, FALSE},
198 	[LEVEL_EDITOR_TOGGLE_ENEMIES_BUTTON] =
199 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleEnemiesButton.png", {-120, -280, 0, 0}, FALSE},
200 	[LEVEL_EDITOR_TOGGLE_ENEMIES_BUTTON_PUSHED] =
201 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleEnemiesButtonPushed.png", {-119, -279, 0, 0}, FALSE},
202 	[LEVEL_EDITOR_TOGGLE_ENEMIES_BUTTON_OFF] =
203 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleEnemiesButtonOff.png", {-120, -280, 0, 0}, FALSE},
204 	[LEVEL_EDITOR_TOGGLE_ENEMIES_BUTTON_OFF_PUSHED] =
205 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleEnemiesButtonOffPushed.png", {-119, -279, 0, 0}, FALSE},
206 	[LEVEL_EDITOR_TOGGLE_OBSTACLES_BUTTON] =
207 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleObstaclesButton.png", {-90, -280, 0, 0}, FALSE},
208 	[LEVEL_EDITOR_TOGGLE_OBSTACLES_BUTTON_PUSHED] =
209 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleObstaclesButtonPushed.png", {-89, -279, 0, 0}, FALSE},
210 	[LEVEL_EDITOR_TOGGLE_OBSTACLES_BUTTON_OFF] =
211 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleObstaclesButtonOff.png", {-90, -280, 0, 0}, FALSE},
212 	[LEVEL_EDITOR_TOGGLE_OBSTACLES_BUTTON_OFF_PUSHED] =
213 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleObstaclesButtonOffPushed.png", {-89, -279, 0, 0}, FALSE},
214 	[LEVEL_EDITOR_TOGGLE_TOOLTIPS_BUTTON] =
215 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleTooltipsButton.png", {-60, -280, 0, 0}, FALSE},
216 	[LEVEL_EDITOR_TOGGLE_TOOLTIPS_BUTTON_PUSHED] =
217 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleTooltipsButtonPushed.png", {-59, -279, 0, 0}, FALSE},
218 	[LEVEL_EDITOR_TOGGLE_TOOLTIPS_BUTTON_OFF] =
219 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleTooltipsButtonOff.png", {-60, -280, 0, 0}, FALSE},
220 	[LEVEL_EDITOR_TOGGLE_TOOLTIPS_BUTTON_OFF_PUSHED] =
221 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleTooltipsButtonOffPushed.png", {-59, -279, 0, 0}, FALSE},
222 	[LEVEL_EDITOR_TOGGLE_COLLISION_RECTS_BUTTON] =
223 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleCollisionRectsButton.png", {-30, -280, 0, 0}, FALSE},
224 	[LEVEL_EDITOR_TOGGLE_COLLISION_RECTS_BUTTON_PUSHED] =
225 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleCollisionRectsButtonPushed.png", {-29, -279, 0, 0}, FALSE},
226 	[LEVEL_EDITOR_TOGGLE_COLLISION_RECTS_BUTTON_OFF] =
227 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleCollisionRectsButtonOff.png", {-30, -280, 0, 0}, FALSE},
228 	[LEVEL_EDITOR_TOGGLE_COLLISION_RECTS_BUTTON_OFF_PUSHED] =
229 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorToggleCollisionRectsButtonOffPushed.png", {-29, -279, 0, 0}, FALSE},
230 
231 
232 	[LEVEL_EDITOR_NEXT_ITEM_GROUP_BUTTON] =
233 	    {EMPTY_IMAGE, "mouse_buttons/RightButton.png", {55 + 64 * 8, 32 + 5 * 66, 0, 0}, TRUE},
234 	[LEVEL_EDITOR_PREV_ITEM_GROUP_BUTTON] =
235 	    {EMPTY_IMAGE, "mouse_buttons/LeftButton.png", {55, 32 + 5 * 66, 0, 0}, TRUE},
236 
237 	[LEVEL_EDITOR_CANCEL_ITEM_DROP_BUTTON] =
238 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorCancelItemDrop.png", {55 + 80, 32 + 5 * 66, 0, 0}, TRUE},
239 	[LEVEL_EDITOR_UNDO_BUTTON] =
240 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorUndoButton.png", {-330, -30, 0, 0}, FALSE},
241 	[LEVEL_EDITOR_UNDO_BUTTON_PUSHED] =
242 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorUndoButtonPushed.png", {-331, -31, 0, 0}, FALSE},
243 
244 	[LEVEL_EDITOR_REDO_BUTTON] =
245 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorRedoButton.png", {-300, -30, 0, 0}, FALSE},
246 	[LEVEL_EDITOR_REDO_BUTTON_PUSHED] =
247 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorRedoButtonPushed.png", {-301, -31, 0, 0}, FALSE},
248 
249 	[LEVEL_EDITOR_TYPESELECT_OBSTACLE_BUTTON] =
250 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton.png", {-152, -250, 0, 0}, FALSE},
251 	[LEVEL_EDITOR_TYPESELECT_OBSTACLE_BUTTON_PUSHED] =
252 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButtonPushed.png", {-152, -250, 0, 0}, FALSE},
253 	[LEVEL_EDITOR_TYPESELECT_OBSTACLE_BUTTON_OFF] =
254 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButtonOff.png", {-152, -250, 0, 0}, FALSE},
255 	[LEVEL_EDITOR_TYPESELECT_OBSTACLE_BUTTON_OFF_PUSHED] =
256 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButtonOffPushed.png", {-152, -250, 0, 0}, FALSE},
257 
258 	[LEVEL_EDITOR_TYPESELECT_ENEMY_BUTTON] =
259 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton3.png", {-90, -174, 0, 0}, FALSE},
260 	[LEVEL_EDITOR_TYPESELECT_ENEMY_BUTTON_PUSHED] =
261 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton3Pushed.png", {-90, -174, 0, 0}, FALSE},
262 	[LEVEL_EDITOR_TYPESELECT_ENEMY_BUTTON_OFF] =
263 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton3Off.png", {-90, -174, 0, 0}, FALSE},
264 	[LEVEL_EDITOR_TYPESELECT_ENEMY_BUTTON_OFF_PUSHED] =
265 		{EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton3OffPushed.png", {-90, -174, 0, 0}, FALSE},
266 
267 	[LEVEL_EDITOR_TYPESELECT_FLOOR_BUTTON] =
268 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton.png", {-152, -212, 0, 0}, FALSE},
269 	[LEVEL_EDITOR_TYPESELECT_FLOOR_BUTTON_PUSHED] =
270 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButtonPushed.png", {-152, -212, 0, 0}, FALSE},
271 	[LEVEL_EDITOR_TYPESELECT_FLOOR_BUTTON_OFF] =
272 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButtonOff.png", {-152, -212, 0, 0}, FALSE},
273 	[LEVEL_EDITOR_TYPESELECT_FLOOR_BUTTON_OFF_PUSHED] =
274 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButtonOffPushed.png", {-152, -212, 0, 0}, FALSE},
275 
276 	[LEVEL_EDITOR_TYPESELECT_ITEM_BUTTON] =
277 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton2.png", {-152, -174, 0, 0}, FALSE},
278 	[LEVEL_EDITOR_TYPESELECT_ITEM_BUTTON_PUSHED] =
279 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton2Pushed.png", {-152, -174, 0, 0}, FALSE},
280 	[LEVEL_EDITOR_TYPESELECT_ITEM_BUTTON_OFF] =
281 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton2Off.png", {-152, -174, 0, 0}, FALSE},
282 	[LEVEL_EDITOR_TYPESELECT_ITEM_BUTTON_OFF_PUSHED] =
283 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton2OffPushed.png", {-152, -174, 0, 0}, FALSE},
284 
285 	[LEVEL_EDITOR_TYPESELECT_WAYPOINT_BUTTON] =
286 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton2.png", {-152, -136, 0, 0}, FALSE},
287 	[LEVEL_EDITOR_TYPESELECT_WAYPOINT_BUTTON_PUSHED] =
288 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton2Pushed.png", {-152, -136, 0, 0}, FALSE},
289 	[LEVEL_EDITOR_TYPESELECT_WAYPOINT_BUTTON_OFF] =
290 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton2Off.png", {-152, -136, 0, 0}, FALSE},
291 	[LEVEL_EDITOR_TYPESELECT_WAYPOINT_BUTTON_OFF_PUSHED] =
292 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton2OffPushed.png", {-152, -136, 0, 0}, FALSE},
293 
294 	[LEVEL_EDITOR_TYPESELECT_MAP_LABEL_BUTTON] =
295 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton3.png", {-90, -136, 0, 0}, FALSE},
296 	[LEVEL_EDITOR_TYPESELECT_MAP_LABEL_BUTTON_PUSHED] =
297 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton3Pushed.png", {-90, -136, 0, 0}, FALSE},
298 	[LEVEL_EDITOR_TYPESELECT_MAP_LABEL_BUTTON_OFF] =
299 		 {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton3Off.png", {-90, -136, 0, 0}, FALSE},
300 	[LEVEL_EDITOR_TYPESELECT_MAP_LABEL_BUTTON_OFF_PUSHED] =
301 	    {EMPTY_IMAGE, "mouse_buttons/LevelEditorTypeSelectorButton3OffPushed.png", {-90, -136, 0, 0}, FALSE},
302 
303 
304 	[WEAPON_RECT_BUTTON] =
305 	    {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {WEAPON_RECT_X, WEAPON_RECT_Y, WEAPON_RECT_WIDTH, WEAPON_RECT_HEIGHT}, FALSE},
306 	[DRIVE_RECT_BUTTON] =
307 	    {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {DRIVE_RECT_X, DRIVE_RECT_Y, DRIVE_RECT_WIDTH, DRIVE_RECT_HEIGHT}, FALSE},
308 	[SHIELD_RECT_BUTTON] =
309 	    {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {SHIELD_RECT_X, SHIELD_RECT_Y, SHIELD_RECT_WIDTH, SHIELD_RECT_HEIGHT}, FALSE},
310 	[HELMET_RECT_BUTTON] =
311 	    {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {HELMET_RECT_X, HELMET_RECT_Y, HELMET_RECT_WIDTH, HELMET_RECT_HEIGHT}, FALSE},
312 	[ARMOUR_RECT_BUTTON] =
313 	    {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {ARMOUR_RECT_X, ARMOUR_RECT_Y, ARMOUR_RECT_WIDTH, ARMOUR_RECT_HEIGHT}, FALSE},
314 
315 	[MORE_STR_BUTTON] =
316 	    {EMPTY_IMAGE, "mouse_buttons/AttributePlusButton.png", {0 + STR_X + 53, STR_Y - 5, 38, 22}, FALSE},
317 	[MORE_MAG_BUTTON] =
318 	    {EMPTY_IMAGE, "mouse_buttons/AttributePlusButton.png", {0 + STR_X + 53, MAG_Y - 5, 38, 22}, FALSE},
319 	[MORE_DEX_BUTTON] =
320 	    {EMPTY_IMAGE, "mouse_buttons/AttributePlusButton.png", {0 + STR_X + 53, DEX_Y - 5, 38, 22}, FALSE},
321 	[MORE_VIT_BUTTON] =
322 	    {EMPTY_IMAGE, "mouse_buttons/AttributePlusButton.png", {0 + STR_X + 53, VIT_Y - 5, 38, 22}, FALSE},
323 
324 	// These two buttons are for the scrolling text during the
325 	// title display, the credits menu and the level editor
326 	// keyboard explanation...
327 	//
328 	[SCROLL_TEXT_UP_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/arrow_up_for_scroll_text.png", {-65, 10, 73, 98}, FALSE},
329 	[SCROLL_TEXT_DOWN_BUTTON] =
330 	    {EMPTY_IMAGE, "mouse_buttons/arrow_down_for_scroll_text.png", {-65, -10 - 98, 73, 98}, FALSE},
331 
332 	[DESCRIPTION_WINDOW_UP_BUTTON] = {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {607, 99, 26, 26}, TRUE},
333 	[DESCRIPTION_WINDOW_DOWN_BUTTON] = {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {607, 347, 26, 26}, TRUE},
334 
335 	[DROID_SHOW_EXIT_BUTTON] = {EMPTY_IMAGE, "THIS_DOESNT_NEED_BLITTING", {202, 311, 47, 47}, TRUE},
336 
337 	[QUEST_BROWSER_ITEM_SHORT_BUTTON] =
338 	    {EMPTY_IMAGE, "mouse_buttons/quest_browser_item_short.png", {108, 86, 300, 26}, FALSE},
339 	[QUEST_BROWSER_ITEM_LONG_BUTTON] =
340 	    {EMPTY_IMAGE, "mouse_buttons/quest_browser_item_long.png", {108, 86, 300, 26}, FALSE},
341 
342 	[TAKEOVER_HELP_BUTTON] = {EMPTY_IMAGE, "mouse_buttons/takeover_help_button.png", {78, 23, 153, 38}, FALSE},
343 
344 	// Buttons of the item upgrade UI.
345 	[ITEM_UPGRADE_APPLY_BUTTON] = { EMPTY_IMAGE, "item_upgrade/button_apply.png",
346 	    { ITEM_UPGRADE_RECT_X + 250, ITEM_UPGRADE_RECT_Y + 389, 48, 48 }, FALSE},
347 	[ITEM_UPGRADE_APPLY_BUTTON_DISABLED] = { EMPTY_IMAGE, "item_upgrade/button_apply_disabled.png",
348 	    { ITEM_UPGRADE_RECT_X + 250, ITEM_UPGRADE_RECT_Y + 389, 48, 48 }, FALSE},
349 	[ITEM_UPGRADE_CLOSE_BUTTON] = { EMPTY_IMAGE, "item_upgrade/button_close.png",
350 	    { ITEM_UPGRADE_RECT_X + 215, ITEM_UPGRADE_RECT_Y + 406, 32, 32 }, FALSE},
351 
352 	// Buttons of the add-on crafting UI.
353 	[ADDON_CRAFTING_APPLY_BUTTON] = { EMPTY_IMAGE, "item_upgrade/button_apply.png",
354 	    { ADDON_CRAFTING_RECT_X + 250, ADDON_CRAFTING_RECT_Y + 389, 48, 48 }, FALSE},
355 	[ADDON_CRAFTING_APPLY_BUTTON_DISABLED] = { EMPTY_IMAGE, "item_upgrade/button_apply_disabled.png",
356 	    { ADDON_CRAFTING_RECT_X + 250, ADDON_CRAFTING_RECT_Y + 389, 48, 48 }, FALSE},
357 	[ADDON_CRAFTING_CLOSE_BUTTON] = { EMPTY_IMAGE, "item_upgrade/button_close.png",
358 	    { ADDON_CRAFTING_RECT_X + 215, ADDON_CRAFTING_RECT_Y + 406, 32, 32 }, FALSE},
359 	[ADDON_CRAFTING_SCROLL_UP_BUTTON] = { EMPTY_IMAGE, "mouse_buttons/crafting_scroll_up.png",
360 	    { ADDON_CRAFTING_RECT_X + 264, ADDON_CRAFTING_RECT_Y + 70, 32, 32 }, FALSE},
361 	[ADDON_CRAFTING_SCROLL_DOWN_BUTTON] = { EMPTY_IMAGE, "mouse_buttons/crafting_scroll_down.png",
362 	    { ADDON_CRAFTING_RECT_X + 264, ADDON_CRAFTING_RECT_Y + 198, 32, 32 }, FALSE},
363 	[ADDON_CRAFTING_SCROLL_DESC_UP_BUTTON] = { EMPTY_IMAGE, "mouse_buttons/crafting_scroll_up.png",
364 	    { ADDON_CRAFTING_RECT_X + 260, ADDON_CRAFTING_RECT_Y + 290, 32, 32 }, FALSE},
365 	[ADDON_CRAFTING_SCROLL_DESC_DOWN_BUTTON] = { EMPTY_IMAGE, "mouse_buttons/crafting_scroll_down.png",
366 	    { ADDON_CRAFTING_RECT_X + 260, ADDON_CRAFTING_RECT_Y + 350, 32, 32 }, FALSE},
367 
368 };				// mouse_press_button AllMousePressButtons[ MAX_MOUSE_PRESS_BUTTONS ]
369 
370 //--------------------
371 // We make these global variables here, as we might want to use
372 // this function inside a signal handler and maybe also it's better
373 // not to mess too much around with the stack while trying to read
374 // out the stack...
375 //
376 #define MAX_CALLS_IN_BACKTRACE 200
377 void *backtrace_array[MAX_CALLS_IN_BACKTRACE];
378 size_t backtrace_size;
379 char **backtrace_strings;
380 size_t backtrace_counter;
381 
382 /**
383  * Obtain a backtrace and print it to stdout.
384  * If signum != 0, call Terminate()
385  */
print_trace(int signum)386 void print_trace(int signum)
387 {
388 
389 #if (!defined __WIN32__) && (!defined __APPLE__) && (defined HAVE_BACKTRACE)
390 
391 	// fprintf ( stderr , "print_trace:  Now attempting backtrace from within the code!\n" );
392 	// fprintf ( stderr , "print_trace:  Allowing a maximum of %d function calls on the stack!\n" , MAX_CALLS_IN_BACKTRACE );
393 
394 	// We attempt to get a backtrace of all function calls so far, even
395 	// including the operating system (or rather libc) call to main() in
396 	// the beginning of execution.
397 	//
398 	backtrace_size = backtrace(backtrace_array, MAX_CALLS_IN_BACKTRACE);
399 
400 	fprintf(stderr, "print_trace:  Obtained %zu stack frames.\n", backtrace_size);
401 
402 	// Now we attempt to translate the trace information we've got to the
403 	// symbol names that might still reside in the binary.
404 	//
405 	// NOTE: that in order for this to work, the -rdynamic switch must have
406 	//       been passed as on option to the LINKER!
407 	//       Also there might be a problem with non-ELF binaries, but let's
408 	//       hope that it still works...
409 	//
410 	backtrace_strings = backtrace_symbols(backtrace_array, backtrace_size);
411 
412 	fprintf(stderr, "print_trace:  Obtaining symbols now done.\n");
413 
414 	for (backtrace_counter = 0; backtrace_counter < backtrace_size; backtrace_counter++)
415 		fprintf(stderr, "%s\n", backtrace_strings[backtrace_counter]);
416 
417 	// The strings generated in the backtrace_symbols function need to
418 	// get freed.  Well, this isn't terribly important, but clean.
419 	//
420 	free(backtrace_strings);
421 
422 #endif
423 
424 	switch (signum) {
425 	case 0:
426 		return;
427 		break;
428 	case SIGSEGV:
429 		fprintf(stderr, "\n%s():  received SIGSEGV!\n", __FUNCTION__);
430 		break;
431 	case SIGFPE:
432 		fprintf(stderr, "\n%s():  received SIGFPE!\n", __FUNCTION__);
433 		break;
434 	default:
435 		fprintf(stderr, "\n%s():  received UNKNOWN SIGNAL %d!  ERROR! \n", __FUNCTION__, signum);
436 	}
437 
438 	Terminate(EXIT_FAILURE);
439 
440 };				// void print_trace ( int sig_num )
441 
442 /**
443  * If we want the screen resolution to be a runtime option and not a
444  * compile time option any more, we must not use it as a constant.  That
445  * means we must adapt the button positions to the current screen
446  * resolution at runtime to, so we do it in this function, which will be
447  * involved at program startup.
448  */
adapt_button_positions_to_screen_resolution(void)449 void adapt_button_positions_to_screen_resolution(void)
450 {
451 	int i;
452 
453 	for (i = 0; i < MAX_MOUSE_PRESS_BUTTONS; i++) {
454 		if (AllMousePressButtons[i].button_rect.x < 0)
455 			AllMousePressButtons[i].button_rect.x += GameConfig.screen_width;
456 		if (AllMousePressButtons[i].button_rect.y < 0)
457 			AllMousePressButtons[i].button_rect.y += GameConfig.screen_height;
458 	}
459 
460 	AllMousePressButtons[OPEN_CLOSE_SKILL_EXPLANATION_BUTTON].button_rect.x += CHARACTERRECT_X;
461 
462 	AllMousePressButtons[MORE_STR_BUTTON].button_rect.x += CHARACTERRECT_X;
463 	AllMousePressButtons[MORE_MAG_BUTTON].button_rect.x += CHARACTERRECT_X;
464 	AllMousePressButtons[MORE_DEX_BUTTON].button_rect.x += CHARACTERRECT_X;
465 	AllMousePressButtons[MORE_VIT_BUTTON].button_rect.x += CHARACTERRECT_X;
466 
467 	Droid_Image_Window.x = 48 * GameConfig.screen_width / 640;
468 	Droid_Image_Window.y = 44 * GameConfig.screen_height / 480;
469 	Droid_Image_Window.w = 130 * GameConfig.screen_width / 640;
470 	Droid_Image_Window.h = 172 * GameConfig.screen_height / 480;
471 
472 	Full_User_Rect.x = 0;
473 	Full_User_Rect.y = 0;
474 	Full_User_Rect.w = GameConfig.screen_width;
475 	Full_User_Rect.h = GameConfig.screen_height;
476 
477 	Cons_Text_Rect.x = 175;
478 	Cons_Text_Rect.y = 180;
479 	Cons_Text_Rect.w = GameConfig.screen_width - 175;
480 	Cons_Text_Rect.h = 305;
481 
482 };				// void adapt_button_positions_to_screen_resolution( void )
483 
484 /**
485  * This is a useful utility sub-function for the checks whether the
486  * mouse cursor is on an enemy, a closed chest or a barrel, or any other object.
487  * The function detects if the current mouse cursor is over the graphics
488  * mentioned in that iso image, using the given position from the
489  * parameter list.
490  *
491  * TRUE or FALSE is returned, depending on whether the cursor IS or
492  * IS NOT on that particular iso_image, if positioned on that given spot.
493  */
mouse_cursor_is_on_that_image(float pos_x,float pos_y,struct image * our_image)494 int mouse_cursor_is_on_that_image(float pos_x, float pos_y, struct image *our_image)
495 {
496 	// our_iso_image = & ( enemy_iso_images [ RotationModel ] [ RotationIndex ] [ (int) this_bot -> animation_phase ] ) ;
497 	SDL_Rect screen_rectangle;
498 
499 	screen_rectangle.x = translate_map_point_to_screen_pixel_x(pos_x, pos_y) + our_image->offset_x;
500 	screen_rectangle.y = translate_map_point_to_screen_pixel_y(pos_x, pos_y) + our_image->offset_y;
501 	screen_rectangle.w = our_image->w;
502 	screen_rectangle.h = our_image->h;
503 
504 	if (MouseCursorIsInRect(&(screen_rectangle),
505 				input_axis.x + User_Rect.w / 2 + User_Rect.x, input_axis.y + User_Rect.h / 2 + User_Rect.y)) {
506 		return (TRUE);
507 	}
508 
509 	return (FALSE);
510 }
511 
512 /**
513  * This function checks if a given screen position lies within the
514  * inventory screen toggle button or not.
515  */
MouseCursorIsInRect(const SDL_Rect * our_rect,int x,int y)516 int MouseCursorIsInRect(const SDL_Rect *our_rect, int x, int y)
517 {
518 	// Now we can start to check if the mouse cursor really is on that
519 	// rectangle or not.
520 	//
521 	if (x > our_rect->x + our_rect->w)
522 		return (FALSE);
523 	if (x < our_rect->x)
524 		return (FALSE);
525 	if (y > our_rect->y + our_rect->h)
526 		return (FALSE);
527 	if (y < our_rect->y)
528 		return (FALSE);
529 
530 	// So since the cursor is not outside of this rectangle, it must
531 	// we inside, and so we'll return this answer.
532 	//
533 	return (TRUE);
534 
535 };				// int MouseCursorIsInRect( SDL_rect* our_rect , int x , int y )
536 
537 /**
538  * This function checks if a given screen position lies within the
539  * inventory screen toggle button or not.
540  */
MouseCursorIsOnButton(int ButtonIndex,int x,int y)541 int MouseCursorIsOnButton(int ButtonIndex, int x, int y)
542 {
543 	SDL_Rect temp_rect;
544 
545 	// First a sanity check if the button index given does make
546 	// some sense.
547 	//
548 	if ((ButtonIndex >= MAX_MOUSE_PRESS_BUTTONS) || (ButtonIndex < 0)) {
549 		error_message(__FUNCTION__, "\
550 A Button that should be checked for mouse contact was requested, but the\n\
551 button index given exceeds the number of buttons defined in FreedroidRPG.", PLEASE_INFORM | IS_FATAL);
552 	}
553 
554 	Copy_Rect(AllMousePressButtons[ButtonIndex].button_rect, temp_rect);
555 	// If this button needs scaling still, then we do it now...
556 	//
557 	if (AllMousePressButtons[ButtonIndex].scale_this_button) {
558 		temp_rect.x *= ((float)GameConfig.screen_width) / 640.0;
559 		temp_rect.w *= ((float)GameConfig.screen_width) / 640.0;
560 		temp_rect.y *= ((float)GameConfig.screen_height) / 480.0;
561 		temp_rect.h *= ((float)GameConfig.screen_height) / 480.0;
562 	}
563 
564 	if (y < AllMousePressButtons[ButtonIndex].button_rect.y)
565 		return (FALSE);
566 
567 	// So since the cursor is not outside of this rectangle, it must
568 	// we inside, and so we'll return this answer.
569 	//
570 	return (MouseCursorIsInRect(&(temp_rect), x, y));
571 
572 };				// int MouseCursorIsOnButton( int ButtonIndex , int x , int y )
573 
574 /**
575  * Draw a button on the screen.
576  */
ShowGenericButtonFromList(int ButtonIndex)577 void ShowGenericButtonFromList(int ButtonIndex)
578 {
579 	struct mouse_press_button *btn;
580 
581 	// Safety check
582 	if ((ButtonIndex >= MAX_MOUSE_PRESS_BUTTONS) || (ButtonIndex < 0)) {
583 		error_message(__FUNCTION__, "Request to display button index %d could not be fulfilled: the\n\
584 				button index given exceeds the number of buttons defined in FreedroidRPG.", PLEASE_INFORM, ButtonIndex);
585 		return;
586 	}
587 
588 	btn = &AllMousePressButtons[ButtonIndex];
589 
590 	// Some buttons have no graphics, in this case there is nothing to do.
591 	if (!strcmp(AllMousePressButtons[ButtonIndex].button_image_file_name, "THIS_DOESNT_NEED_BLITTING")) {
592 		return;
593 	}
594 
595 	// Compute scaling factors for button
596 	float scale_x = 1.0, scale_y = 1.0;
597 	if (btn->scale_this_button) {
598 		scale_x = ((float)GameConfig.screen_width) / 640.0;
599 		scale_y = ((float)GameConfig.screen_height) / 480.0;
600 	}
601 
602 	// Load button image if required
603 	struct image *img = &btn->button_image;
604 	if (!image_loaded(img)) {
605 		load_image(img, btn->button_image_file_name, NO_MOD);
606 
607 		// Maybe we had '0' entries for the height or width of this button in the list.
608 		// This means that we will take the real width and the real height from the image
609 		// and overwrite the 0 entries with this.
610 		//
611 		if (!btn->button_rect.w) {
612 			btn->button_rect.w = img->w;
613 		}
614 
615 		if (!btn->button_rect.h) {
616 			btn->button_rect.h = img->h;
617 		}
618 	}
619 
620 	display_image_on_screen(img, AllMousePressButtons[ButtonIndex].button_rect.x * scale_x, AllMousePressButtons[ButtonIndex].button_rect.y * scale_y, set_image_transformation(scale_x, scale_y, 1.0, 1.0, 1.0, 1.0, 0));
621 }
622 
init_data_dirs_path()623 int init_data_dirs_path()
624 {
625 	int i, j;
626 	FILE *f;
627 	char file_path[PATH_MAX];
628 
629 	// Reset the data dirs paths
630 	for (i = 0; i < LAST_DATA_DIR; i++) {
631 		data_dirs[i].path[0] = '\0';
632 	}
633 
634 	// Directories to look for the data dirs
635 	char *top_data_dir[]   = { ".", "..", "../..", FD_DATADIR };
636 #ifdef ENABLE_NLS
637 	char *top_locale_dir[] = { ".", "..", "../..", LOCALEDIR };
638 #endif
639 	int slen = sizeof(top_data_dir)/sizeof(top_data_dir[0]);
640 
641 	// To find the root of the data dirs, we search a well known file that is
642 	// always needed for the game to work.
643 	for (i = 0; i < slen ; i++) {
644 		sprintf(file_path, "%s/" WELL_KNOWN_DATA_FILE, top_data_dir[i]);
645 
646 		if ((f = fopen(file_path, "r")) != NULL) {
647 			// File found, so now fill the data dir paths
648 			for (j = 0; j < LAST_DATA_DIR; j++) {
649 				char *dir = top_data_dir[i];
650 				const char *subdir = data_dirs[j].name;
651 #ifdef ENABLE_NLS
652 				if (j == LOCALE_DIR) {
653 					dir = top_locale_dir[i];
654 					// LOCALEDIR envvar already points to the locale subdir
655 					if (!strcmp(dir, LOCALEDIR))
656 						subdir = "";
657 				}
658 #endif
659 				int nb = snprintf(data_dirs[j].path, PATH_MAX, "%s/%s", dir, subdir);
660 				if (nb >= PATH_MAX) {
661 					error_message(__FUNCTION__, "data_dirs[].path is not big enough to store the following path: %s/%s",
662 					             PLEASE_INFORM | IS_FATAL, dir, data_dirs[j].name);
663 				}
664 			}
665 			fclose(f);
666 			return 1;
667 		}
668 	}
669 
670 	// The searched file was not found. Complain.
671 	if (getcwd(file_path, PATH_MAX)) {
672 		error_message(__FUNCTION__, "Data dirs not found ! (current directory: %s)",
673 	                 PLEASE_INFORM | IS_FATAL, file_path);
674 	} else {
675 		error_message(__FUNCTION__, "Data dirs not found ! (and cannot find the current working directory)",
676 	                 PLEASE_INFORM | IS_FATAL);
677 	}
678 
679 	return 0;
680 }
681 
682 /* -----------------------------------------------------------------
683  * check if a given filename exists in subdir.
684  *
685  * fills in the (ALLOC'd) string and returns 1 if okay, 0 on error.
686  * file_path's length HAS to be PATH_MAX.
687  * ----------------------------------------------------------------- */
_file_exists(const char * fname,const char * subdir,char * file_path)688 static int _file_exists(const char *fname, const char *subdir, char *file_path)
689 {
690 	int nb = snprintf(file_path, PATH_MAX, "%s/%s", subdir, fname);
691 	if (nb >= PATH_MAX) {
692 		*file_path = 0;
693 		error_message(__FUNCTION__, "Pathname too long (max is %d): %s/%s",
694 					 NO_REPORT, PATH_MAX, subdir, fname);
695 		return 0;
696 	}
697 
698 	FILE *fp = fopen(file_path, "r");
699 	if (!fp) {
700 		/* not found */
701 		*file_path = 0;
702 		return 0;
703 	}
704 
705 	fclose(fp);
706 	return 1;
707 }
708 
709 /* -----------------------------------------------------------------
710  * Find a filename in subdir (using a data_dir handle).
711  *
712  * fills in the (ALLOC'd) string and returns 1 if okay, 0 on error.
713  * file_path's length HAS to be PATH_MAX.
714  * ----------------------------------------------------------------- */
find_file(const char * fname,int subdir_handle,char * file_path,int error_report)715 int find_file(const char *fname, int subdir_handle, char *file_path, int error_report)
716 {
717 	if (subdir_handle < 0 || subdir_handle >= LAST_DATA_DIR) {
718 		error_message(__FUNCTION__, "Called with a wrong subdir handle (%d)",
719 		             error_report | PLEASE_INFORM, subdir_handle);
720 		return 0;
721 	}
722 
723 	if (!_file_exists(fname, data_dirs[subdir_handle].path, file_path)) {
724 		error_message(__FUNCTION__, "File %s not found in %s",
725 		             error_report, fname, data_dirs[subdir_handle].name);
726 		return 0;
727 	}
728 
729 	return 1;
730 }
731 
732 /* -----------------------------------------------------------------
733  * Find a suffixed filename in subdir (using a data_dir handle).
734  *
735  * The 'suffix' is added before the filename extension.
736  *
737  * fills in the (ALLOC'd) string and returns 1 if okay, 0 on error.
738  * file_path's length HAS to be PATH_MAX.
739  * ----------------------------------------------------------------- */
find_suffixed_file(const char * fname,const char * suffix,int subdir_handle,char * file_path,int error_report)740 int find_suffixed_file(const char *fname, const char *suffix, int subdir_handle, char *file_path, int error_report)
741 {
742 	char suffixed_fname[PATH_MAX];
743 	char *actual_fname = (char *)fname;
744 
745 	if (suffix) {
746 		int fname_length = strlen(fname);
747 		int suffix_length = strlen(suffix);
748 		if ((fname_length + suffix_length + 1) >= PATH_MAX) {
749 			*file_path = 0;
750 			error_message(__FUNCTION__, "Filename + suffix too long (max is %d): %s, with suffix: %s",
751 			              PLEASE_INFORM, PATH_MAX, fname, suffix);
752 			return 0;
753 		}
754 
755 		int pos = strrchr(fname, '.') - fname;
756 
757 		memcpy(suffixed_fname, fname, pos);
758 		memcpy(suffixed_fname + pos, suffix, suffix_length);
759 		memcpy(suffixed_fname + pos + suffix_length, fname + pos, fname_length - pos + 1);
760 		suffixed_fname[fname_length + suffix_length] = '\0';
761 
762 		actual_fname = suffixed_fname;
763 	}
764 
765 	return find_file(actual_fname, subdir_handle, file_path, error_report);
766 }
767 
768 /* -----------------------------------------------------------------
769  * Find a localized version of a filename in subdir (using a data_dir handle).
770  *
771  * The localized versions are to be put in subdirs, using locale names.
772  * For instances, map/titles/fr ou map/titles/de.
773  *
774  * As with gettext(), generalizations of the locale name are tried
775  * in turn. So if the locale is 'fr_FR', but the 'fr_FR' subdir does
776  * not exists, then the 'fr' subdir is checked.
777  *
778  * fills in the (ALLOC'd) string and returns 1 if okay, 0 on error.
779  * file_path's length HAS to be PATH_MAX.
780  * ----------------------------------------------------------------- */
find_localized_file(const char * fname,int subdir_handle,char * file_path,int error_report)781 int find_localized_file(const char *fname, int subdir_handle, char *file_path, int error_report)
782 {
783 #ifdef ENABLE_NLS
784 	if (subdir_handle < 0 || subdir_handle >= LAST_DATA_DIR) {
785 		error_message(__FUNCTION__, "Called with a wrong subdir handle (%d)",
786 		              error_report | PLEASE_INFORM, subdir_handle);
787 		return 0;
788 	}
789 
790 	char *used_locale = lang_get();
791 
792 	if (!used_locale || strlen(used_locale) == 0) {
793 		return find_file(fname, subdir_handle, file_path, error_report);
794 	}
795 
796 	// A locale name is typically of the form language[_territory][.codeset][@modifier]
797 	// We try each possible locale name in turn from the whole one to 'language' only.
798 	char *locale = strdup(used_locale);
799 	char *sep = "@._";
800 	int i;
801 
802 	for (i = -1; i < (int)strlen(sep); i++) {
803 		// 'i == -1' is a special case, to use the full locale name
804 		if (i != -1) {
805 			char *ptr = strchr(locale, sep[i]);
806 			if (!ptr) {
807 				continue;
808 			}
809 			*ptr = '\0';
810 		}
811 
812 		char l10ndir[PATH_MAX];
813 		int nb = snprintf(l10ndir, PATH_MAX, "%s/%s", data_dirs[subdir_handle].path, locale);
814 		if (nb >= PATH_MAX) {
815 			error_message(__FUNCTION__, "Dirname too long (max is %d): %s/%s - Using untranslated version of %s",
816 			             error_report, PATH_MAX, data_dirs[subdir_handle].path, locale, fname);
817 			break;
818 		}
819 		if (_file_exists(fname, l10ndir, file_path)) {
820 			free(locale);
821 			return 1;
822 		}
823 	}
824 
825 	free(locale);
826 #endif
827 
828 	// Localized version not found. Use untranslated version.
829 	return find_file(fname, subdir_handle, file_path, error_report);
830 }
831 
find_encoded_file(const char * fname,int subdir_handle,char * file_path,int error_report)832 int find_encoded_file(const char *fname, int subdir_handle, char *file_path, int error_report)
833 {
834 #ifdef ENABLE_NLS
835 	if (subdir_handle < 0 || subdir_handle >= LAST_DATA_DIR) {
836 		error_message(__FUNCTION__, "Called with a wrong subdir handle (%d)",
837 		              error_report | PLEASE_INFORM, subdir_handle);
838 		return 0;
839 	}
840 
841 	char *used_encoding = lang_get_encoding();
842 
843 	if (!used_encoding || !strlen(used_encoding) || !strcmp(used_encoding, "ASCII")) {
844 		return find_file(fname, subdir_handle, file_path, error_report);
845 	}
846 
847 	char encoded_dir[PATH_MAX];
848 	int nb = snprintf(encoded_dir, PATH_MAX, "%s/%s", data_dirs[subdir_handle].path, used_encoding);
849 	if (nb >= PATH_MAX) {
850 		error_message(__FUNCTION__, "Dirname too long (max is %d): %s/%s - Using default encoding version of %s",
851 		             error_report, PATH_MAX, data_dirs[subdir_handle].path, used_encoding, fname);
852 		return find_file(fname, subdir_handle, file_path, error_report);
853 	}
854 	if (_file_exists(fname, encoded_dir, file_path))
855 		return TRUE;
856 #endif
857 
858 	// Encoded version not found. Use default encoding version.
859 	return find_file(fname, subdir_handle, file_path, error_report);
860 }
861 
862 /**
863  * This function realizes the Pause-Mode: the game process is halted,
864  * while the graphics and animations are not.  This mode
865  * can further be toggled from PAUSE to CHEESE, which is
866  * a feature from the original program that should probably
867  * allow for better screenshots.
868  */
Pause(void)869 void Pause(void)
870 {
871 	int Pause = TRUE;
872 	int cheese = FALSE;	/* cheese mode: do not display GAME PAUSED  - nicer screenshots */
873 	SDL_Event event;
874 	SDLKey key;
875 
876 	Activate_Conservative_Frame_Computation();
877 
878 	AssembleCombatPicture(DO_SCREEN_UPDATE | SHOW_ITEMS);
879 
880 	input_get_keybind("pause", &key, NULL);
881 
882 	while (Pause) {
883 		SDL_WaitEvent(&event);
884 
885 		if (event.type == SDL_QUIT) {
886 			Terminate(EXIT_SUCCESS);
887 		}
888 
889 		AssembleCombatPicture(SHOW_ITEMS);
890 		if (!cheese) {
891 			put_string_centered(Menu_Font, 200, _("GAME PAUSED"));
892 			put_string_centered(Menu_Font, 230, _("press p to resume"));
893 		}
894 		our_SDL_flip_wrapper();
895 
896 		if (event.type == SDL_KEYDOWN) {
897 			if (event.key.keysym.sym == key) {
898 				Pause = FALSE;
899 			} else if (event.key.keysym.sym == SDLK_c) {
900 				cheese = !cheese;
901 			}
902 		}
903 
904 		SDL_Delay(10);
905 	}
906 
907 	return;
908 }
909 
910 /**
911  * This function prevents any action from taking place in the game world.
912  *
913  * Interaction with the user interface is unaffected.
914  */
freeze_world()915 void freeze_world()
916 {
917 	// Because different UI elements may try to freeze the game world,
918 	// it's necessary to count how many times this function has been
919 	// called.
920 	world_is_frozen++;
921 }
922 
923 /**
924  * This function unfreezes the game world.
925  *
926  * NOTE: unfreeze_world() must be called for each call to freeze_world().
927  */
unfreeze_world()928 void unfreeze_world()
929 {
930 	if (world_is_frozen > 0)
931 		world_is_frozen--;
932 }
933 
934 /**
935  * This function returns the current state of the game world.
936  * @return TRUE if the game world is currently frozen.
937  */
world_frozen()938 int world_frozen()
939 {
940 	return (world_is_frozen > 0);
941 }
942 
943 /**
944  * This function starts the time-taking process.  Later the results
945  * of this function will be used to calculate the current framerate
946  */
StartTakingTimeForFPSCalculation(void)947 void StartTakingTimeForFPSCalculation(void)
948 {
949 	/* This ensures, that 0 is never an encountered framenr,
950 	 * therefore count to 100 here
951 	 * Take the time now for calculating the frame rate
952 	 * (DO NOT MOVE THIS COMMAND PLEASE!) */
953 	framenr++;
954 
955 	One_Frame_SDL_Ticks = SDL_GetTicks();
956 	if (framenr % 10 == 1)
957 		Ten_Frame_SDL_Ticks = SDL_GetTicks();
958 	if (framenr % 100 == 1) {
959 		Onehundred_Frame_SDL_Ticks = SDL_GetTicks();
960 	}
961 };				// void StartTakingTimeForFPSCalculation(void)
962 
963 /**
964  * This function computes the framerate that has been experienced
965  * in this frame.  It will be used to correctly calibrate all
966  * movements of game objects.
967  *
968  * NOTE:  To query the actual framerate a DIFFERENT function must
969  *        be used, namely Frame_Time().
970  */
ComputeFPSForThisFrame(void)971 void ComputeFPSForThisFrame(void)
972 {
973 
974 	Now_SDL_Ticks = SDL_GetTicks();
975 	oneframedelay = Now_SDL_Ticks - One_Frame_SDL_Ticks;
976 
977 	if (!oneframedelay)
978 		FPSover1 = 1000 * 1 / 0.5;
979 	else
980 		FPSover1 = 1000 * 1 / (float)oneframedelay;
981 }
982 
983 /**
984  *
985  * This function is the key to independence of the framerate for various game elements.
986  * It returns the average time needed to draw one frame.
987  * Other functions use this to calculate new positions of moving objects, etc..
988  *
989  * Also there is of course a serious problem when some interruption occurs, like e.g.
990  * the options menu is called or the debug menu is called or the console or the elevator
991  * is entered or a takeover game takes place.  This might cause HUGE framerates, that could
992  * box the influencer out of the ship if used to calculate the new position.
993  *
994  * To counter unwanted effects after such events we have the SkipAFewFramerates counter,
995  * which instructs Rate_To_Be_Returned to return only the overall default framerate since
996  * no better substitute exists at this moment.  But on the other hand, this seems to
997  * work REALLY well this way.
998  *
999  * This counter is most conveniently set via the function Activate_Conservative_Frame_Computation,
1000  * which can be conveniently called from eveywhere.
1001  *
1002  */
Frame_Time(void)1003 float Frame_Time(void)
1004 {
1005 	float Rate_To_Be_Returned;
1006 
1007 	if (SkipAFewFrames) {
1008 		Rate_To_Be_Returned = Overall_Average;
1009 		return Rate_To_Be_Returned;
1010 	}
1011 
1012 	Rate_To_Be_Returned = (1.0 / FPSover1);
1013 
1014 	return Rate_To_Be_Returned;
1015 }
1016 
1017 /**
1018  *
1019  * With framerate computation, there is a problem when some interruption occurs, like e.g.
1020  * the options menu is called or the debug menu is called or the console or the elevator
1021  * is entered or a takeover game takes place.  This might cause HUGE framerates, that could
1022  * box the influencer out of the ship if used to calculate the new position.
1023  *
1024  * To counter unwanted effects after such events we have the SkipAFewFramerates counter,
1025  * which instructs Rate_To_Be_Returned to return only the overall default framerate since
1026  * no better substitute exists at this moment.
1027  *
1028  * This counter is most conveniently set via the function Activate_Conservative_Frame_Computation,
1029  * which can be conveniently called from everywhere.
1030  *
1031  */
Activate_Conservative_Frame_Computation(void)1032 void Activate_Conservative_Frame_Computation(void)
1033 {
1034 	// SkipAFewFrames=212;
1035 	// SkipAFewFrames=22;
1036 	SkipAFewFrames = 3;
1037 
1038 	DebugPrintf(1, "\nConservative_Frame_Computation activated!");
1039 
1040 };				// void Activate_Conservative_Frame_Computation(void)
1041 
1042 /*
1043  * Should be called in every frame when counting FPS
1044  */
update_frames_displayed(void)1045 void update_frames_displayed(void)
1046 {
1047 	// The next couter counts the frames displayed by FreedroidRPG during this
1048 	// whole run!!  DO NOT RESET THIS COUNTER WHEN THE GAME RESTARTS!!
1049 	Overall_Frames_Displayed++;
1050 	Overall_Average = (Overall_Average * (Overall_Frames_Displayed - 1)
1051 			   + Frame_Time()) / Overall_Frames_Displayed;
1052 
1053 	if (SkipAFewFrames)
1054 		SkipAFewFrames--;
1055 }
1056 
1057 /**
1058  * This function is used to generate an integer in range of all
1059  * numbers from 0 to UpperBound.
1060  */
MyRandom(int UpperBound)1061 int MyRandom(int UpperBound)
1062 {
1063 
1064 	if (!UpperBound)
1065 		return 0;
1066 
1067 	float tmp;
1068 	int PureRandom;
1069 	int dice_val;		/* the result in [0, UpperBound] */
1070 
1071 	PureRandom = rand();
1072 	tmp = 1.0 * PureRandom / RAND_MAX;	/* random number in [0;1] */
1073 
1074 	/*
1075 	 * we always round OFF for the resulting int, therefore
1076 	 * we first add 0.99999 to make sure that UpperBound has
1077 	 * roughly the same probablity as the other numbers
1078 	 */
1079 	dice_val = (int)(tmp * (1.0 * UpperBound + 0.99999));
1080 
1081 	return (dice_val);
1082 
1083 };				// int MyRandom ( int UpperBound )
1084 
1085 /**
1086  * This function teleports the influencer to a new position on the
1087  * ship.  THIS CAN BE A POSITION ON A DIFFERENT LEVEL.
1088  */
Teleport(int LNum,float X,float Y,int with_sound_and_fading,int with_animation_reset)1089 void Teleport(int LNum, float X, float Y, int with_sound_and_fading, int with_animation_reset)
1090 {
1091 	int old_lvl = Me.pos.z;
1092 
1093 	// Check if we are in editor and not in game test mode then we store the level number
1094 	//
1095 	if(game_root_mode == ROOT_IS_LVLEDIT && game_status != INSIDE_GAME)
1096 		GameConfig.last_edited_level = LNum;
1097 	// Maybe the 'teleport' really comes from a teleportation device or
1098 	// teleport spell or maybe even from accessing some sewer access way.
1099 	// In that case we'll fade out the screen a bit using the gamma ramp
1100 	// and then later back in again.  (Note that this is a blocking function
1101 	// call, i.e. it will take a second or so each.)
1102 	//
1103 	if (with_sound_and_fading) {
1104 		fade_out_screen();
1105 	}
1106 
1107 	if (LNum != Me.pos.z) {
1108 
1109 		// In case a real level change has happened,
1110 		// we need to do a lot of work.  Therefore we start by activating
1111 		// the conservative frame time computation to avoid a 'jump'.
1112 		//
1113 		Activate_Conservative_Frame_Computation();
1114 
1115 		Me.pos.x = X;
1116 		Me.pos.y = Y;
1117 		Me.pos.z = LNum;
1118 
1119 		item_held_in_hand = NULL;
1120 
1121 		// We add some sanity check against teleporting to non-allowed
1122 		// locations (like outside of map that is)
1123 		//
1124 		if (!level_exists(LNum) || !pos_inside_level(Me.pos.x, Me.pos.y, curShip.AllLevels[LNum])) {
1125 			fprintf(stderr, "\n\ntarget location was: lev=%d x=%f y=%f.\n", LNum, X, Y);
1126 			fprintf(stderr, "source location was: lev=%d x=%f y=%f.", Me.pos.z, Me.pos.x, Me.pos.y);
1127 			error_message(__FUNCTION__, "\
1128 A Teleport was requested, but the location to teleport to lies beyond\n\
1129 the bounds of this 'ship' which means the current collection of levels.\n\
1130 This indicates an error in the map system of FreedroidRPG.", PLEASE_INFORM | IS_FATAL);
1131 		}
1132 
1133 		// Refresh some speed-up data structures
1134 		get_visible_levels();
1135 
1136 	} else {
1137 		// If no real level change has occurred, everything
1138 		// is simple and we just need to set the new coordinates, haha
1139 		//
1140 		Me.pos.x = X;
1141 		Me.pos.y = Y;
1142 
1143 		// Teleport could have been called by the leveleditor, due to
1144 		// some changes in the current level (light values, for example),
1145 		// so we refresh the speed-up data structures
1146 		get_visible_levels();
1147 	}
1148 
1149 	// After the teleport, the mouse move target might be
1150 	// completely out of date.  Therefore we simply delete it.  In cases
1151 	// where the jump came from crossing a jump threshold (levels glued
1152 	// together) we can still restore the move target in that (the calling!)
1153 	// function.
1154 	//
1155 	Me.mouse_move_target.x = Me.pos.x;
1156 	Me.mouse_move_target.y = Me.pos.y;
1157 	Me.mouse_move_target.z = Me.pos.z;
1158 
1159 	Me.mouse_move_target_combo_action_type = NO_COMBO_ACTION_SET;
1160 
1161 	// Animate Tux as standing still
1162 	if (with_animation_reset) {
1163 		Me.walk_cycle_phase = 0.0;
1164 		Me.phase = tux_anim.standing_keyframe;
1165 	}
1166 
1167 	if (with_sound_and_fading) {
1168 		teleport_arrival_sound();
1169 	}
1170 	// Perhaps the player is visiting this level for the first time.  Then, the
1171 	// tux should make it's initial statement about the location, if there is one.
1172 	//
1173 	if (!Me.HaveBeenToLevel[Me.pos.z]) {
1174 		Me.HaveBeenToLevel[Me.pos.z] = TRUE;
1175 	}
1176 
1177 	switch_background_music(CURLEVEL()->Background_Song_Name);
1178 
1179 	// Since we've mightily changed position now, we should clear the
1180 	// position history, so that no one gets confused...
1181 	//
1182 	InitInfluPositionHistory();
1183 
1184 	if (with_sound_and_fading) {
1185 		append_new_game_message(_("Arrived at %s."), D_(curShip.AllLevels[Me.pos.z]->Levelname));
1186 		fade_in_screen();
1187 	}
1188 
1189 	if (game_status == INSIDE_GAME) {
1190 		// Notify level change events on this level.
1191 		if (LNum != old_lvl)
1192 			event_level_changed(old_lvl, LNum);
1193 
1194 		// Notify position changed.
1195 		event_position_changed(Me.pos, TRUE);
1196 	}
1197 }
1198 
1199 /**
1200  * Teleport the influencer to the center of a level on the ship
1201  * \param level_num The number of the level where we want to be teleported
1202  */
teleport_to_level_center(int level_num)1203 void teleport_to_level_center(int level_num)
1204 {
1205 	// Calculate the center of the level
1206 	float x = curShip.AllLevels[level_num]->xlen / 2;
1207 	float y = curShip.AllLevels[level_num]->ylen / 2;
1208 
1209 	// Teleporting to the center of the level
1210 	Teleport(level_num, x, y, FALSE, TRUE);
1211 }
1212 
1213 /**
1214  * Check if a level exists.
1215  * \param level_num The number of the level.
1216  * \return TRUE if the level exists.
1217 */
level_exists(int level_num)1218 int level_exists(int level_num)
1219 {
1220 	if (level_num < 0 || level_num >= curShip.num_levels) {
1221 		return FALSE;
1222 	}
1223 
1224 	if (curShip.AllLevels[level_num] == NULL) {
1225 		return FALSE;
1226 	}
1227 
1228 	return TRUE;
1229 }
1230 
1231 /*----------------------------------------------------------------------
1232  * LoadGameConfig(): load saved options from config-file
1233  *
1234  * this should be the first of all load/save functions called
1235  * as here we read the $HOME-dir and create the config-subdir if necessary
1236  *
1237  *----------------------------------------------------------------------*/
LoadGameConfig(void)1238 int LoadGameConfig(void)
1239 {
1240 	char fname[5000];
1241 	FILE *configfile;
1242 
1243 	if (!our_config_dir) {
1244 		DebugPrintf(1, "No useble config-dir. No config-loading possible\n");
1245 		return (OK);
1246 	}
1247 
1248 	sprintf(fname, "%s/fdrpg.cfg", our_config_dir);
1249 	if ((configfile = fopen(fname, "rb")) == NULL) {
1250 		fprintf(stderr, "\nUnable to open configuration file %s\n", fname);
1251 		lang_set(GameConfig.locale, NULL);
1252 		return (ERR);
1253 	}
1254 
1255 	char *stuff = (char *)malloc(FS_filelength(configfile) + 1);
1256 	if (fread(stuff, FS_filelength(configfile), 1, configfile) != 1) {
1257 		error_message(__FUNCTION__, "\nFailed to read config file: %s.", NO_REPORT, fname);
1258 		fclose(configfile);
1259 		free(stuff);
1260 		return ERR;
1261 	}
1262 	stuff[FS_filelength(configfile)] = 0;
1263 	fclose(configfile);
1264 
1265 	if (setjmp(saveload_jmpbuf)) {
1266 		error_message(__FUNCTION__, "Failed to read config file: %s.", NO_REPORT, fname);
1267 		configfile = NULL;
1268 		free(stuff);
1269 		ResetGameConfigToDefaultValues();
1270 		return ERR;
1271 	}
1272 
1273 	load_freedroid_configuration(stuff);
1274 	lang_set(GameConfig.locale, NULL);
1275 
1276 	configfile = NULL;
1277 	free(stuff);
1278 
1279 	if (!GameConfig.freedroid_version_string || strcmp(GameConfig.freedroid_version_string, VERSION)) {
1280 		error_message(__FUNCTION__, "\
1281 Settings file found in your ~/.freedroid_rpg dir does not\n\
1282 seem to be from the same version as this installation of FreedroidRPG.\n\
1283 This is perfectly normal if you have just upgraded your version of\n\
1284 FreedroidRPG.  However, the loading of your settings will be canceled now,\n\
1285 because the format of the settings file is no longer supported.\n\
1286 No need to panic.  The default settings will be used instead and a new\n\
1287 settings file will be generated.", NO_REPORT);
1288 		ResetGameConfigToDefaultValues();
1289 		return (ERR);
1290 	};
1291 
1292 	// Now we will turn off the skills and inventory screen and that, cause
1293 	// this should be off when the game starts...
1294 	//
1295 	GameConfig.CharacterScreen_Visible = FALSE;
1296 	GameConfig.Inventory_Visible = FALSE;
1297 	GameConfig.SkillScreen_Visible = FALSE;
1298 	GameConfig.skill_explanation_screen_visible = FALSE;
1299 	GameConfig.Automap_Visible = TRUE;
1300 
1301 	return (OK);
1302 }
1303 
1304 /*----------------------------------------------------------------------
1305  * SaveGameConfig: do just that
1306  * Return: -1 on error, -2 if immediate exit is needed, 0 otherwise
1307  *----------------------------------------------------------------------*/
SaveGameConfig(void)1308 int SaveGameConfig(void)
1309 {
1310 	char fname[5000];
1311 	int current_width;
1312 	int current_height;
1313 	FILE *config_file;
1314 
1315 	// Maybe the Terminate function was invoked BEFORE the startup process
1316 	// was complete at all (like e.g. some illegal command line parameter).
1317 	// Then the config dir is not initialized.  We catch this case and return
1318 	// control to the operating system immediately if that happens...
1319 
1320 	if (our_config_dir == NULL || our_config_dir[0] == '\0') {
1321 		DebugPrintf(-4, "It seems that the game couldn't start up at all... therefore we need not save any configuration information.\n");
1322 		return -2;
1323 	}
1324 
1325 	// Now we know, that the config dir has been initialized already.
1326 	// That indicates, that the game did start up already.
1327 	// Therefore we can do the normal save config stuff...
1328 
1329 	sprintf(fname, "%s/fdrpg.cfg", our_config_dir);
1330 	if ((config_file = fopen(fname, "wb")) == NULL) {
1331 		DebugPrintf(-4, "Unable to open configuration file %s for writing\n", fname);
1332 		return -1;
1333 	}
1334 
1335 	// We put the current version number of FreedroidRPG into the
1336 	// version number string.  This will be useful so that later
1337 	// versions of FreedroidRPG can identify old config files and decide
1338 	// not to use them in some cases.
1339 	//
1340 	if (GameConfig.freedroid_version_string) {
1341 		free(GameConfig.freedroid_version_string);
1342 	}
1343 	GameConfig.freedroid_version_string = strdup(VERSION);
1344 
1345 	// We preserve the current resolution, modify it a bit, such that
1346 	// the preselected resolution will come to effect next time, save
1347 	// it and then we restore the current settings again.
1348 	//
1349 	current_width = GameConfig.screen_width;
1350 	current_height = GameConfig.screen_height;
1351 	GameConfig.screen_width = GameConfig.next_time_width_of_screen;
1352 	GameConfig.screen_height = GameConfig.next_time_height_of_screen;
1353 
1354 	// Now write the actual data
1355 	savestruct_autostr = alloc_autostr(4096);
1356 	save_freedroid_configuration(savestruct_autostr);
1357 	if (fwrite(savestruct_autostr->value, savestruct_autostr->length, 1, config_file) != 1) {
1358 		error_message(__FUNCTION__, "Failed to write configuration file: %s", NO_REPORT, fname);
1359 		free_autostr(savestruct_autostr);
1360 		fclose(config_file);
1361 		return -1;
1362 	}
1363 
1364 	free_autostr(savestruct_autostr);
1365 
1366 	GameConfig.screen_width = current_width;
1367 	GameConfig.screen_height = current_height;
1368 	fclose(config_file);
1369 
1370 	return 0;
1371 }
1372 
free_memory_before_exit(void)1373 static void free_memory_before_exit(void)
1374 {
1375 	// free the entities
1376 	clear_volatile_obstacles();
1377 	clear_enemies();
1378 	clear_npcs();
1379 	free_tux();
1380 
1381 	// free the widgets
1382 	free_game_ui();
1383 	free_lvledit_ui();
1384 	free_chat_widgets();
1385 	widget_free_image_resources();
1386 
1387 	// free animations lists and visible levels
1388 	reset_visible_levels();
1389 	clear_animated_floor_tile_list();
1390 
1391 	// other stuff
1392 	delete_events();
1393 	free_current_ship();
1394 	leveleditor_cleanup();
1395 	free_error_msg_store();
1396 }
1397 
1398 /**
1399  * This function is used for terminating freedroid.  It will close
1400  * the SDL submodules and exit.
1401  */
Terminate(int exit_code)1402 void Terminate(int exit_code)
1403 {
1404 	if (!do_benchmark) {
1405 		printf("\n---------------------------------------------------------------------------------");
1406 		printf("\nTermination of freedroidRPG initiated... ");
1407 	}
1408 
1409 	// Save the config file only in case of success.
1410 
1411 	if (exit_code == EXIT_SUCCESS) {
1412 		if (SaveGameConfig() == -2) {
1413 			exit_code = -1;
1414 			goto IMMEDIATE_EXIT;
1415 		}
1416 	}
1417 
1418 	// Close active lua states, to force a call to garbage collector, in order
1419 	// to call Lua binding 'destructors', and clean all the stuff
1420 
1421 	close_lua();
1422 	close_audio();
1423 	free_memory_before_exit();
1424 
1425 	if (!do_benchmark) {
1426 		printf("Thank you for playing freedroidRPG.\n\n");
1427 	}
1428 
1429 IMMEDIATE_EXIT:
1430 
1431 	if (SDL_WasInit(SDL_INIT_EVERYTHING))
1432 		SDL_Quit();
1433 
1434 	// If the game was not run from the command line, we should open an editor with
1435 	// the last debug output, since people in general won't know how and where
1436 	// to find the material for proper reporting of bugs.
1437 
1438 	if (!run_from_term) {
1439 		if (exit_code == EXIT_FAILURE) {
1440 			int rtn;
1441 			fflush(stdout);
1442 			fflush(stderr);
1443 			char *cmd = MyMalloc(strlen(our_config_dir) + strlen(OPENTXT_CMD) + 20);
1444 			sprintf(cmd, "%s %s/fdrpg_out.txt", OPENTXT_CMD, our_config_dir);
1445 			rtn = system(cmd);
1446 			if (rtn == -1) // We use the return value mainly to avoid a compilation warning
1447 				DebugPrintf(-1, "system call failed: \"%s\" returned %d", cmd, rtn);
1448 			free(cmd);
1449 		}
1450 	}
1451 
1452 	// Now we drop control back to the operating system.  The FreedroidRPG
1453 	// program has finished.
1454 
1455 	exit(exit_code);
1456 }
1457 
1458 /**
1459  * Return a pointer towards the obstacle designated by the given (unique) label.
1460  * \param level_number If not NULL, this is set to the levelnumber where the obstacle was found.
1461  */
give_pointer_to_obstacle_with_label(const char * obstacle_label,int * level_number)1462 obstacle *give_pointer_to_obstacle_with_label(const char *obstacle_label, int *level_number)
1463 {
1464 	int i, j;
1465 
1466 	// On each level, browse the obstacle extensions until we find the label we are looking for
1467 	for (i = 0; i < curShip.num_levels; i++) {
1468 		level *l = curShip.AllLevels[i];
1469 
1470 		if (l == NULL)
1471 			continue;
1472 
1473 		for (j = 0; j < l->obstacle_extensions.size; j++) {
1474 			struct obstacle_extension *ext = &ACCESS_OBSTACLE_EXTENSION(l->obstacle_extensions, j);
1475 
1476 			if (ext->type == OBSTACLE_EXTENSION_LABEL) {
1477 				if (!strcmp(ext->data, obstacle_label)) {
1478 					if (level_number) {
1479 						*level_number = l->levelnum;
1480 					}
1481 					return ext->obs;
1482 				}
1483 			}
1484 
1485 		}
1486 	}
1487 
1488 	error_message(__FUNCTION__, "Obstacle label \"%s\" was not found on the map.", PLEASE_INFORM | IS_FATAL, obstacle_label);
1489 	return NULL;
1490 }
1491 
1492 /*----------------------------------------------------------------------
1493  * Get the power of 2 greater than of equal to the argument
1494  * http://www-graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
1495  *----------------------------------------------------------------------*/
pot_gte(uint32_t v)1496 uint32_t pot_gte(uint32_t v)
1497 {
1498 	uint32_t pot = v;
1499 	--pot;
1500 	pot |= pot >> 1;
1501 	pot |= pot >> 2;
1502 	pot |= pot >> 4;
1503 	pot |= pot >> 8;
1504 	pot |= pot >> 16;
1505 	return (++pot);
1506 
1507 }				// uint32_t pot_gte( uint32_t v)
1508 
1509 /*
1510  * Some systems do not have setenv().
1511  * On those systems, we use putenv().
1512  */
fd_setenv(const char * var,const char * val,int overwrite)1513 int fd_setenv(const char *var, const char *val, int overwrite)
1514 {
1515 	int ret;
1516 
1517 #ifdef HAVE_SETENV
1518 	ret = setenv(var, val, overwrite);
1519 #else
1520 	// putenv does not make a copy of its argument, and inserts the pointer to
1521 	// the argument into the environment array.
1522 	// The following not trivial code is needed to avoid memleak.
1523 	// (see POS34-C on https://www.securecoding.cert.org)
1524 	static char *oldenv = NULL;
1525 	const size_t len = strlen(var) + 1 + strlen(val) + 2;
1526 	char *env = (char *)MyMalloc(len);
1527 	snprintf(env, len, "%s=%s", var, val);
1528 	ret = putenv(env);
1529 	if (ret != 0) {
1530 		free(env);
1531 		error_message(__FUNCTION__, "Error when calling putenv() to set %s to %s\n", PLEASE_INFORM, var, val);
1532 		return ret;
1533 	}
1534 	if (oldenv != NULL) {
1535 		free(oldenv);
1536 	}
1537 	oldenv = env;
1538 #endif
1539 	return ret;
1540 }
1541 
1542 /*
1543  * Some systems do not have unsetenv().
1544  * On those systems, we use putenv().
1545  */
fd_unsetenv(const char * var)1546 int fd_unsetenv(const char *var)
1547 {
1548 	int ret;
1549 
1550 #ifdef HAVE_UNSETENV
1551 	ret = unsetenv(var);
1552 #else
1553 	// See fdrpg_setenv() comment
1554 
1555 	static char *oldenv = NULL;
1556 	const size_t len = strlen(var) + 2;
1557 	char *env = (char *)MyMalloc(len);
1558 	snprintf(env, len, "%s=", var);
1559 	ret = putenv(env);
1560 	if (ret != 0) {
1561 		free(env);
1562 		error_message(__FUNCTION__, "Error when calling putenv() to unset %s\n", PLEASE_INFORM, var);
1563 		return ret;
1564 	}
1565 	if (oldenv != NULL) {
1566 		free(oldenv);
1567 	}
1568 	oldenv = env;
1569 #endif
1570 	return ret;
1571 }
1572 
1573 #undef _misc_c
1574