• R/O
  • SSH
  • HTTPS

marathon: Commit


Commit MetaInfo

Revision523 (tree)
Zeit2012-06-02 13:50:26
Autorookawa_mi

Log Message

1.0.2対応
改行コード変換

Ändern Zusammenfassung

Diff

--- marathon/trunk/Source_Files/Files/find_files.h (revision 522)
+++ marathon/trunk/Source_Files/Files/find_files.h (revision 523)
@@ -1,129 +1,129 @@
1-#ifndef __FIND_FILES_H
2-#define __FIND_FILES_H
3-
4-/*
5-
6- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
7- and the "Aleph One" developers.
8-
9- This program is free software; you can redistribute it and/or modify
10- it under the terms of the GNU General Public License as published by
11- the Free Software Foundation; either version 3 of the License, or
12- (at your option) any later version.
13-
14- This program is distributed in the hope that it will be useful,
15- but WITHOUT ANY WARRANTY; without even the implied warranty of
16- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17- GNU General Public License for more details.
18-
19- This license is contained in the file "COPYING",
20- which is included with this source code; it is available online at
21- http://www.gnu.org/licenses/gpl.html
22-
23-Aug 25, 2000 (Loren Petrich):
24- Abstracting the file handling
25-
26-Aug 26, 2000 (Loren Petrich):
27- Creating an object-oriented file finder
28-
29-*/
30-
31-#include "FileHandler.h"
32-
33-// Finds every type of file
34-const Typecode WILDCARD_TYPE = _typecode_unknown;
35-
36-#if defined(mac)
37-
38-/* Find all files of a given type.. */
39-
40-enum {
41- _fill_buffer, /* Fill the buffer with matches */
42- _callback_only /* Ignore the buffer, and call the callback for each. */
43-};
44-
45-enum {
46- // Filespec buffers are always created from outside,
47- // and no alphabetical sorting is ever done
48- // _ff_create_buffer= 0x0001, /* Create the destination buffer, free later w/ Dispose */
49- // _ff_alphabetical= 0x0002, /* Matches are returned in alphabetical order */
50- _ff_recurse= 0x0004, /* Recurse when I find a subdirectory */
51- _ff_callback_with_catinfo= 0x0008 /* Callback with CInfoPBRec as second paramter */
52-};
53-
54-// File-finder object
55-class FileFinder
56-{
57- // Temporary stuff:
58- FileSpecifier TempFile;
59-
60- bool Enumerate(DirectorySpecifier& Dir);
61-
62-public:
63- short version; /* Version Control (Set to 0) <- */
64- short flags; /* Search flags <- */
65- short search_type; /* Search type <- */
66-
67- DirectorySpecifier BaseDir;
68- Typecode Type; /* OSType to find <- */
69-
70- FileSpecifier *buffer; /* Destination <-> */
71- short max; /* Maximum matches to return <- */
72- short count; /* Count of matches found -> */
73-
74- /* Callback functions, if returns true, you add it. If */
75- /* callback==NULL, adds all found. <- */
76- // ZZZ semantics change: if _callback_only and callback returns false, terminate the enumeration
77- bool (*callback)(FileSpecifier& File, void *data);
78- void *user_data; /* Passed to callback above. <- */
79- // ZZZ addition: will be called when entering and leaving a subdirectory
80- bool (*directory_change_callback)(FileSpecifier& Directory, bool EnteringDirectory, void* data);
81-
82- // Clears out the memory contents
83- void Clear();
84-
85- // Does the finding
86- bool Find();
87-
88- // Platform-specific members
89- short GetError() {return Err;}
90-
91-private:
92- CInfoPBRec pb; /* static to prevent stack overflow.. */
93- OSType type_to_find;
94- OSErr Err;
95-};
96-
97-#elif defined(SDL)
98-
99-#include <vector>
100-
101-// File-finder base class
102-class FileFinder {
103-public:
104- FileFinder() {}
105- virtual ~FileFinder() {}
106-
107- bool Find(DirectorySpecifier &dir, Typecode type, bool recursive = true) {
108- return _Find(dir, type, recursive, 0);
109- }
110-
111-protected:
112- bool _Find(DirectorySpecifier& dir, Typecode type, bool recursive, int depth);
113- // Gets called for each found file, returns true if search is to be aborted
114- virtual bool found(FileSpecifier &file) = 0;
115-};
116-
117-// Find all files of given type and append them to a vector
118-class FindAllFiles : public FileFinder {
119-public:
120- FindAllFiles(vector<FileSpecifier> &v) : dest_vector(v) {dest_vector.clear();}
121-
122-private:
123- bool found(FileSpecifier &file);
124- vector<FileSpecifier> &dest_vector;
125-};
126-
127-#endif
128-
129-#endif
1+#ifndef __FIND_FILES_H
2+#define __FIND_FILES_H
3+
4+/*
5+
6+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
7+ and the "Aleph One" developers.
8+
9+ This program is free software; you can redistribute it and/or modify
10+ it under the terms of the GNU General Public License as published by
11+ the Free Software Foundation; either version 3 of the License, or
12+ (at your option) any later version.
13+
14+ This program is distributed in the hope that it will be useful,
15+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+ GNU General Public License for more details.
18+
19+ This license is contained in the file "COPYING",
20+ which is included with this source code; it is available online at
21+ http://www.gnu.org/licenses/gpl.html
22+
23+Aug 25, 2000 (Loren Petrich):
24+ Abstracting the file handling
25+
26+Aug 26, 2000 (Loren Petrich):
27+ Creating an object-oriented file finder
28+
29+*/
30+
31+#include "FileHandler.h"
32+
33+// Finds every type of file
34+const Typecode WILDCARD_TYPE = _typecode_unknown;
35+
36+#if defined(mac)
37+
38+/* Find all files of a given type.. */
39+
40+enum {
41+ _fill_buffer, /* Fill the buffer with matches */
42+ _callback_only /* Ignore the buffer, and call the callback for each. */
43+};
44+
45+enum {
46+ // Filespec buffers are always created from outside,
47+ // and no alphabetical sorting is ever done
48+ // _ff_create_buffer= 0x0001, /* Create the destination buffer, free later w/ Dispose */
49+ // _ff_alphabetical= 0x0002, /* Matches are returned in alphabetical order */
50+ _ff_recurse= 0x0004, /* Recurse when I find a subdirectory */
51+ _ff_callback_with_catinfo= 0x0008 /* Callback with CInfoPBRec as second paramter */
52+};
53+
54+// File-finder object
55+class FileFinder
56+{
57+ // Temporary stuff:
58+ FileSpecifier TempFile;
59+
60+ bool Enumerate(DirectorySpecifier& Dir);
61+
62+public:
63+ short version; /* Version Control (Set to 0) <- */
64+ short flags; /* Search flags <- */
65+ short search_type; /* Search type <- */
66+
67+ DirectorySpecifier BaseDir;
68+ Typecode Type; /* OSType to find <- */
69+
70+ FileSpecifier *buffer; /* Destination <-> */
71+ short max; /* Maximum matches to return <- */
72+ short count; /* Count of matches found -> */
73+
74+ /* Callback functions, if returns true, you add it. If */
75+ /* callback==NULL, adds all found. <- */
76+ // ZZZ semantics change: if _callback_only and callback returns false, terminate the enumeration
77+ bool (*callback)(FileSpecifier& File, void *data);
78+ void *user_data; /* Passed to callback above. <- */
79+ // ZZZ addition: will be called when entering and leaving a subdirectory
80+ bool (*directory_change_callback)(FileSpecifier& Directory, bool EnteringDirectory, void* data);
81+
82+ // Clears out the memory contents
83+ void Clear();
84+
85+ // Does the finding
86+ bool Find();
87+
88+ // Platform-specific members
89+ short GetError() {return Err;}
90+
91+private:
92+ CInfoPBRec pb; /* static to prevent stack overflow.. */
93+ OSType type_to_find;
94+ OSErr Err;
95+};
96+
97+#elif defined(SDL)
98+
99+#include <vector>
100+
101+// File-finder base class
102+class FileFinder {
103+public:
104+ FileFinder() {}
105+ virtual ~FileFinder() {}
106+
107+ bool Find(DirectorySpecifier &dir, Typecode type, bool recursive = true) {
108+ return _Find(dir, type, recursive, 0);
109+ }
110+
111+protected:
112+ bool _Find(DirectorySpecifier& dir, Typecode type, bool recursive, int depth);
113+ // Gets called for each found file, returns true if search is to be aborted
114+ virtual bool found(FileSpecifier &file) = 0;
115+};
116+
117+// Find all files of given type and append them to a vector
118+class FindAllFiles : public FileFinder {
119+public:
120+ FindAllFiles(vector<FileSpecifier> &v) : dest_vector(v) {dest_vector.clear();}
121+
122+private:
123+ bool found(FileSpecifier &file);
124+ vector<FileSpecifier> &dest_vector;
125+};
126+
127+#endif
128+
129+#endif
--- marathon/trunk/Source_Files/Files/tags.h (revision 522)
+++ marathon/trunk/Source_Files/Files/tags.h (revision 523)
@@ -1,176 +1,176 @@
1-#ifndef __TAGS_H
2-#define __TAGS_H
3-
4-#include "cstypes.h"
5-
6-/*
7- TAGS.H
8-
9- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
10- and the "Aleph One" developers.
11-
12- This program is free software; you can redistribute it and/or modify
13- it under the terms of the GNU General Public License as published by
14- the Free Software Foundation; either version 3 of the License, or
15- (at your option) any later version.
16-
17- This program is distributed in the hope that it will be useful,
18- but WITHOUT ANY WARRANTY; without even the implied warranty of
19- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20- GNU General Public License for more details.
21-
22- This license is contained in the file "COPYING",
23- which is included with this source code; it is available online at
24- http://www.gnu.org/licenses/gpl.html
25-
26- Sunday, July 3, 1994 5:33:15 PM
27-
28- This is a list of all of the tags used by code that uses the wad file format.
29- One tag, KEY_TAG, has special meaning, and KEY_TAG_SIZE must be set to the
30- size of an index entry. Each wad can only have one index entry. You can get the
31- index entry from a wad, or from all of the wads in the file easily.
32-
33- Marathon uses the KEY_TAG as the name of the level.
34-
35-Feb 2, 2000 (Loren Petrich):
36- Changed application creator to 26.A "Aleph One"
37- Changed soundfile type to 'sndー' to be Marathon-Infinity compatible
38-
39-Feb 3, 2000 (Loren Petrich):
40- Changed shapes-file type to 'shpー' to be Marathon-Infinity compatible
41-
42-Feb 4, 2000 (Loren Petrich):
43- Changed most of the other 2's to ー's to be Marathon-Infinity compatible,
44- except for the map file type.
45-
46-Feb 6, 2000 (Loren Petrich):
47- Added loading of typecodes from the resource fork
48-
49-Aug 21, 2000 (Loren Petrich):
50- Added a preferences filetype
51-
52-Aug 22, 2000 (Loren Petrich):
53- Added an images filetype
54-
55-Aug 28, 2000 (Loren Petrich):
56- get_typecode() now defaults to '????' for unrecognized typecodes
57-
58-Mar 14, 2001 (Loren Petrich):
59- Added a music filetype
60-
61-Jul 4, 2002 (Loren Petrich):
62- Added a "set" function for the typecode
63-*/
64-
65-#include <vector>
66-
67-#define MAXIMUM_LEVEL_NAME_SIZE 64
68-
69-/* OSTypes.. */
70-// LP change: moved values to filetypes_macintosh.c
71-enum Typecode {
72- _typecode_unknown= NONE,
73- _typecode_creator= 0,
74- _typecode_scenario,
75- _typecode_savegame,
76- _typecode_film,
77- _typecode_physics,
78- _typecode_shapes,
79- _typecode_sounds,
80- _typecode_patch,
81- _typecode_images,
82- _typecode_preferences,
83- _typecode_music,
84- _typecode_theme, // pseudo type code
85- _typecode_netscript, // ZZZ pseudo typecode
86- _typecode_shapespatch,
87- NUMBER_OF_TYPECODES
88-};
89-
90-// LP addition: typecode handling
91-// Initializer: loads from resource fork
92-void initialize_typecodes();
93-
94-// Accessors
95-uint32 get_typecode(Typecode which);
96-void set_typecode(Typecode which, uint32 _type);
97-#ifdef mac
98-Typecode get_typecode_for_file_type(OSType inType);
99-const std::vector<OSType> get_all_file_types_for_typecode (Typecode which);
100-#endif
101-
102-// These are no longer constants, which will cause trouble for switch/case constructions
103-// These have been eliminated in favor of using the above enum of abstracted filetypes
104-// as much as possible
105-/*
106-#define APPLICATION_CREATOR (get_typecode(_typecode_creator))
107-#define SCENARIO_FILE_TYPE (get_typecode(_typecode_scenario))
108-#define SAVE_GAME_TYPE (get_typecode(_typecode_savegame))
109-#define FILM_FILE_TYPE (get_typecode(_typecode_film))
110-#define PHYSICS_FILE_TYPE (get_typecode(_typecode_physics))
111-#define SHAPES_FILE_TYPE (get_typecode(_typecode_shapes))
112-#define SOUNDS_FILE_TYPE (get_typecode(_typecode_sounds))
113-#define PATCH_FILE_TYPE (get_typecode(_typecode_patch))
114-#define IMAGES_FILE_TYPE (get_typecode(_typcode_images))
115-#define PREFERENCES_FILE_TYPE (get_typecode(_typecode_prefs))
116-*/
117-
118-/* Other tags- */
119-#define POINT_TAG FOUR_CHARS_TO_INT('P','N','T','S')
120-#define LINE_TAG FOUR_CHARS_TO_INT('L','I','N','S')
121-#define SIDE_TAG FOUR_CHARS_TO_INT('S','I','D','S')
122-#define POLYGON_TAG FOUR_CHARS_TO_INT('P','O','L','Y')
123-#define LIGHTSOURCE_TAG FOUR_CHARS_TO_INT('L','I','T','E')
124-#define ANNOTATION_TAG FOUR_CHARS_TO_INT('N','O','T','E')
125-#define OBJECT_TAG FOUR_CHARS_TO_INT('O','B','J','S')
126-#define GUARDPATH_TAG FOUR_CHARS_TO_INT('p','\x8c','t','h')
127-#define MAP_INFO_TAG FOUR_CHARS_TO_INT('M','i','n','f')
128-#define ITEM_PLACEMENT_STRUCTURE_TAG FOUR_CHARS_TO_INT('p','l','a','c')
129-#define DOOR_EXTRA_DATA_TAG FOUR_CHARS_TO_INT('d','o','o','r')
130-#define PLATFORM_STATIC_DATA_TAG FOUR_CHARS_TO_INT('p','l','a','t')
131-#define ENDPOINT_DATA_TAG FOUR_CHARS_TO_INT('E','P','N','T')
132-#define MEDIA_TAG FOUR_CHARS_TO_INT('m','e','d','i')
133-#define AMBIENT_SOUND_TAG FOUR_CHARS_TO_INT('a','m','b','i')
134-#define RANDOM_SOUND_TAG FOUR_CHARS_TO_INT('b','o','n','k')
135-#define TERMINAL_DATA_TAG FOUR_CHARS_TO_INT('t','e','r','m')
136-
137-/* Save/Load game tags. */
138-#define PLAYER_STRUCTURE_TAG FOUR_CHARS_TO_INT('p','l','y','r')
139-#define DYNAMIC_STRUCTURE_TAG FOUR_CHARS_TO_INT('d','w','o','l')
140-#define OBJECT_STRUCTURE_TAG FOUR_CHARS_TO_INT('m','o','b','j')
141-#define DOOR_STRUCTURE_TAG FOUR_CHARS_TO_INT('d','o','o','r')
142-#define MAP_INDEXES_TAG FOUR_CHARS_TO_INT('i','i','d','x')
143-#define AUTOMAP_LINES FOUR_CHARS_TO_INT('a','l','i','n')
144-#define AUTOMAP_POLYGONS FOUR_CHARS_TO_INT('a','p','o','l')
145-#define MONSTERS_STRUCTURE_TAG FOUR_CHARS_TO_INT('m','O','n','s')
146-#define EFFECTS_STRUCTURE_TAG FOUR_CHARS_TO_INT('f','x',' ',' ')
147-#define PROJECTILES_STRUCTURE_TAG FOUR_CHARS_TO_INT('b','a','n','g')
148-#define PLATFORM_STRUCTURE_TAG FOUR_CHARS_TO_INT('P','L','A','T')
149-#define WEAPON_STATE_TAG FOUR_CHARS_TO_INT('w','e','a','p')
150-#define TERMINAL_STATE_TAG FOUR_CHARS_TO_INT('c','i','n','t')
151-#define LUA_STATE_TAG FOUR_CHARS_TO_INT('s','l','u','a')
152-
153-/* Physix model tags */
154-#define MONSTER_PHYSICS_TAG FOUR_CHARS_TO_INT('M','N','p','x')
155-#define EFFECTS_PHYSICS_TAG FOUR_CHARS_TO_INT('F','X','p','x')
156-#define PROJECTILE_PHYSICS_TAG FOUR_CHARS_TO_INT('P','R','p','x')
157-#define PHYSICS_PHYSICS_TAG FOUR_CHARS_TO_INT('P','X','p','x')
158-#define WEAPONS_PHYSICS_TAG FOUR_CHARS_TO_INT('W','P','p','x')
159-
160-/* Embedded shapes */
161-#define SHAPE_PATCH_TAG FOUR_CHARS_TO_INT('S','h','P','a')
162-
163-/* Embedded scripts */
164-#define MMLS_TAG FOUR_CHARS_TO_INT('M','M','L','S')
165-#define LUAS_TAG FOUR_CHARS_TO_INT('L','U','A','S')
166-
167-/* Preferences Tags.. */
168-#define prefGRAPHICS_TAG FOUR_CHARS_TO_INT('g','r','a','f')
169-#define prefSERIAL_TAG FOUR_CHARS_TO_INT('s','e','r','l')
170-#define prefNETWORK_TAG FOUR_CHARS_TO_INT('n','e','t','w')
171-#define prefPLAYER_TAG FOUR_CHARS_TO_INT('p','l','y','r')
172-#define prefINPUT_TAG FOUR_CHARS_TO_INT('i','n','p','u')
173-#define prefSOUND_TAG FOUR_CHARS_TO_INT('s','n','d',' ')
174-#define prefENVIRONMENT_TAG FOUR_CHARS_TO_INT('e','n','v','r')
175-
176-#endif
1+#ifndef __TAGS_H
2+#define __TAGS_H
3+
4+#include "cstypes.h"
5+
6+/*
7+ TAGS.H
8+
9+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
10+ and the "Aleph One" developers.
11+
12+ This program is free software; you can redistribute it and/or modify
13+ it under the terms of the GNU General Public License as published by
14+ the Free Software Foundation; either version 3 of the License, or
15+ (at your option) any later version.
16+
17+ This program is distributed in the hope that it will be useful,
18+ but WITHOUT ANY WARRANTY; without even the implied warranty of
19+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+ GNU General Public License for more details.
21+
22+ This license is contained in the file "COPYING",
23+ which is included with this source code; it is available online at
24+ http://www.gnu.org/licenses/gpl.html
25+
26+ Sunday, July 3, 1994 5:33:15 PM
27+
28+ This is a list of all of the tags used by code that uses the wad file format.
29+ One tag, KEY_TAG, has special meaning, and KEY_TAG_SIZE must be set to the
30+ size of an index entry. Each wad can only have one index entry. You can get the
31+ index entry from a wad, or from all of the wads in the file easily.
32+
33+ Marathon uses the KEY_TAG as the name of the level.
34+
35+Feb 2, 2000 (Loren Petrich):
36+ Changed application creator to 26.A "Aleph One"
37+ Changed soundfile type to 'sndー' to be Marathon-Infinity compatible
38+
39+Feb 3, 2000 (Loren Petrich):
40+ Changed shapes-file type to 'shpー' to be Marathon-Infinity compatible
41+
42+Feb 4, 2000 (Loren Petrich):
43+ Changed most of the other 2's to ー's to be Marathon-Infinity compatible,
44+ except for the map file type.
45+
46+Feb 6, 2000 (Loren Petrich):
47+ Added loading of typecodes from the resource fork
48+
49+Aug 21, 2000 (Loren Petrich):
50+ Added a preferences filetype
51+
52+Aug 22, 2000 (Loren Petrich):
53+ Added an images filetype
54+
55+Aug 28, 2000 (Loren Petrich):
56+ get_typecode() now defaults to '????' for unrecognized typecodes
57+
58+Mar 14, 2001 (Loren Petrich):
59+ Added a music filetype
60+
61+Jul 4, 2002 (Loren Petrich):
62+ Added a "set" function for the typecode
63+*/
64+
65+#include <vector>
66+
67+#define MAXIMUM_LEVEL_NAME_SIZE 64
68+
69+/* OSTypes.. */
70+// LP change: moved values to filetypes_macintosh.c
71+enum Typecode {
72+ _typecode_unknown= NONE,
73+ _typecode_creator= 0,
74+ _typecode_scenario,
75+ _typecode_savegame,
76+ _typecode_film,
77+ _typecode_physics,
78+ _typecode_shapes,
79+ _typecode_sounds,
80+ _typecode_patch,
81+ _typecode_images,
82+ _typecode_preferences,
83+ _typecode_music,
84+ _typecode_theme, // pseudo type code
85+ _typecode_netscript, // ZZZ pseudo typecode
86+ _typecode_shapespatch,
87+ NUMBER_OF_TYPECODES
88+};
89+
90+// LP addition: typecode handling
91+// Initializer: loads from resource fork
92+void initialize_typecodes();
93+
94+// Accessors
95+uint32 get_typecode(Typecode which);
96+void set_typecode(Typecode which, uint32 _type);
97+#ifdef mac
98+Typecode get_typecode_for_file_type(OSType inType);
99+const std::vector<OSType> get_all_file_types_for_typecode (Typecode which);
100+#endif
101+
102+// These are no longer constants, which will cause trouble for switch/case constructions
103+// These have been eliminated in favor of using the above enum of abstracted filetypes
104+// as much as possible
105+/*
106+#define APPLICATION_CREATOR (get_typecode(_typecode_creator))
107+#define SCENARIO_FILE_TYPE (get_typecode(_typecode_scenario))
108+#define SAVE_GAME_TYPE (get_typecode(_typecode_savegame))
109+#define FILM_FILE_TYPE (get_typecode(_typecode_film))
110+#define PHYSICS_FILE_TYPE (get_typecode(_typecode_physics))
111+#define SHAPES_FILE_TYPE (get_typecode(_typecode_shapes))
112+#define SOUNDS_FILE_TYPE (get_typecode(_typecode_sounds))
113+#define PATCH_FILE_TYPE (get_typecode(_typecode_patch))
114+#define IMAGES_FILE_TYPE (get_typecode(_typcode_images))
115+#define PREFERENCES_FILE_TYPE (get_typecode(_typecode_prefs))
116+*/
117+
118+/* Other tags- */
119+#define POINT_TAG FOUR_CHARS_TO_INT('P','N','T','S')
120+#define LINE_TAG FOUR_CHARS_TO_INT('L','I','N','S')
121+#define SIDE_TAG FOUR_CHARS_TO_INT('S','I','D','S')
122+#define POLYGON_TAG FOUR_CHARS_TO_INT('P','O','L','Y')
123+#define LIGHTSOURCE_TAG FOUR_CHARS_TO_INT('L','I','T','E')
124+#define ANNOTATION_TAG FOUR_CHARS_TO_INT('N','O','T','E')
125+#define OBJECT_TAG FOUR_CHARS_TO_INT('O','B','J','S')
126+#define GUARDPATH_TAG FOUR_CHARS_TO_INT('p','\x8c','t','h')
127+#define MAP_INFO_TAG FOUR_CHARS_TO_INT('M','i','n','f')
128+#define ITEM_PLACEMENT_STRUCTURE_TAG FOUR_CHARS_TO_INT('p','l','a','c')
129+#define DOOR_EXTRA_DATA_TAG FOUR_CHARS_TO_INT('d','o','o','r')
130+#define PLATFORM_STATIC_DATA_TAG FOUR_CHARS_TO_INT('p','l','a','t')
131+#define ENDPOINT_DATA_TAG FOUR_CHARS_TO_INT('E','P','N','T')
132+#define MEDIA_TAG FOUR_CHARS_TO_INT('m','e','d','i')
133+#define AMBIENT_SOUND_TAG FOUR_CHARS_TO_INT('a','m','b','i')
134+#define RANDOM_SOUND_TAG FOUR_CHARS_TO_INT('b','o','n','k')
135+#define TERMINAL_DATA_TAG FOUR_CHARS_TO_INT('t','e','r','m')
136+
137+/* Save/Load game tags. */
138+#define PLAYER_STRUCTURE_TAG FOUR_CHARS_TO_INT('p','l','y','r')
139+#define DYNAMIC_STRUCTURE_TAG FOUR_CHARS_TO_INT('d','w','o','l')
140+#define OBJECT_STRUCTURE_TAG FOUR_CHARS_TO_INT('m','o','b','j')
141+#define DOOR_STRUCTURE_TAG FOUR_CHARS_TO_INT('d','o','o','r')
142+#define MAP_INDEXES_TAG FOUR_CHARS_TO_INT('i','i','d','x')
143+#define AUTOMAP_LINES FOUR_CHARS_TO_INT('a','l','i','n')
144+#define AUTOMAP_POLYGONS FOUR_CHARS_TO_INT('a','p','o','l')
145+#define MONSTERS_STRUCTURE_TAG FOUR_CHARS_TO_INT('m','O','n','s')
146+#define EFFECTS_STRUCTURE_TAG FOUR_CHARS_TO_INT('f','x',' ',' ')
147+#define PROJECTILES_STRUCTURE_TAG FOUR_CHARS_TO_INT('b','a','n','g')
148+#define PLATFORM_STRUCTURE_TAG FOUR_CHARS_TO_INT('P','L','A','T')
149+#define WEAPON_STATE_TAG FOUR_CHARS_TO_INT('w','e','a','p')
150+#define TERMINAL_STATE_TAG FOUR_CHARS_TO_INT('c','i','n','t')
151+#define LUA_STATE_TAG FOUR_CHARS_TO_INT('s','l','u','a')
152+
153+/* Physix model tags */
154+#define MONSTER_PHYSICS_TAG FOUR_CHARS_TO_INT('M','N','p','x')
155+#define EFFECTS_PHYSICS_TAG FOUR_CHARS_TO_INT('F','X','p','x')
156+#define PROJECTILE_PHYSICS_TAG FOUR_CHARS_TO_INT('P','R','p','x')
157+#define PHYSICS_PHYSICS_TAG FOUR_CHARS_TO_INT('P','X','p','x')
158+#define WEAPONS_PHYSICS_TAG FOUR_CHARS_TO_INT('W','P','p','x')
159+
160+/* Embedded shapes */
161+#define SHAPE_PATCH_TAG FOUR_CHARS_TO_INT('S','h','P','a')
162+
163+/* Embedded scripts */
164+#define MMLS_TAG FOUR_CHARS_TO_INT('M','M','L','S')
165+#define LUAS_TAG FOUR_CHARS_TO_INT('L','U','A','S')
166+
167+/* Preferences Tags.. */
168+#define prefGRAPHICS_TAG FOUR_CHARS_TO_INT('g','r','a','f')
169+#define prefSERIAL_TAG FOUR_CHARS_TO_INT('s','e','r','l')
170+#define prefNETWORK_TAG FOUR_CHARS_TO_INT('n','e','t','w')
171+#define prefPLAYER_TAG FOUR_CHARS_TO_INT('p','l','y','r')
172+#define prefINPUT_TAG FOUR_CHARS_TO_INT('i','n','p','u')
173+#define prefSOUND_TAG FOUR_CHARS_TO_INT('s','n','d',' ')
174+#define prefENVIRONMENT_TAG FOUR_CHARS_TO_INT('e','n','v','r')
175+
176+#endif
--- marathon/trunk/Source_Files/Files/find_files_sdl.cpp (revision 522)
+++ marathon/trunk/Source_Files/Files/find_files_sdl.cpp (revision 523)
@@ -1,88 +1,88 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20-*/
21-
22-/*
23- * find_files_sdl.cpp - Routines for finding files, SDL implementation
24- *
25- * Written in 2000 by Christian Bauer
26- */
27-
28-#ifndef SDL_RFORK_HACK
29-#include "cseries.h"
30-#include "FileHandler.h"
31-#include "find_files.h"
32-
33-#include <vector>
34-#include <algorithm>
35-
36-
37-/*
38- * File finder base class
39- */
40-
41-bool FileFinder::_Find(DirectorySpecifier &dir, Typecode type, bool recursive, int depth)
42-{
43- // Get list of entries in directory
44- vector<dir_entry> entries;
45- if (!dir.ReadDirectory(entries))
46- return false;
47- sort(entries.begin(), entries.end());
48-
49- // Iterate through entries
50- vector<dir_entry>::const_iterator i, end = entries.end();
51- for (i = entries.begin(); i != end; i++) {
52-
53- // Construct full specifier of file/dir
54- FileSpecifier file = dir + i->name;
55-
56- if (i->is_directory) {
57-
58- if (depth == 0 && i->name == "Plugins")
59- continue;
60-
61- // Recurse into directory
62- if (recursive)
63- if (_Find(file, type, recursive, depth + 1))
64- return true;
65-
66- } else {
67-
68- // Check file type and call found() function
69- if (type == WILDCARD_TYPE || type == file.GetType())
70- if (found(file))
71- return true;
72- }
73- }
74- return false;
75-}
76-
77-
78-/*
79- * Find all files of given type
80- */
81-
82-bool FindAllFiles::found(FileSpecifier &file)
83-{
84- dest_vector.push_back(file);
85- return false;
86-}
87-
88-#endif
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 3 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+*/
21+
22+/*
23+ * find_files_sdl.cpp - Routines for finding files, SDL implementation
24+ *
25+ * Written in 2000 by Christian Bauer
26+ */
27+
28+#ifndef SDL_RFORK_HACK
29+#include "cseries.h"
30+#include "FileHandler.h"
31+#include "find_files.h"
32+
33+#include <vector>
34+#include <algorithm>
35+
36+
37+/*
38+ * File finder base class
39+ */
40+
41+bool FileFinder::_Find(DirectorySpecifier &dir, Typecode type, bool recursive, int depth)
42+{
43+ // Get list of entries in directory
44+ vector<dir_entry> entries;
45+ if (!dir.ReadDirectory(entries))
46+ return false;
47+ sort(entries.begin(), entries.end());
48+
49+ // Iterate through entries
50+ vector<dir_entry>::const_iterator i, end = entries.end();
51+ for (i = entries.begin(); i != end; i++) {
52+
53+ // Construct full specifier of file/dir
54+ FileSpecifier file = dir + i->name;
55+
56+ if (i->is_directory) {
57+
58+ if (depth == 0 && i->name == "Plugins")
59+ continue;
60+
61+ // Recurse into directory
62+ if (recursive)
63+ if (_Find(file, type, recursive, depth + 1))
64+ return true;
65+
66+ } else {
67+
68+ // Check file type and call found() function
69+ if (type == WILDCARD_TYPE || type == file.GetType())
70+ if (found(file))
71+ return true;
72+ }
73+ }
74+ return false;
75+}
76+
77+
78+/*
79+ * Find all files of given type
80+ */
81+
82+bool FindAllFiles::found(FileSpecifier &file)
83+{
84+ dest_vector.push_back(file);
85+ return false;
86+}
87+
88+#endif
--- marathon/trunk/Source_Files/Files/game_wad.cpp (revision 522)
+++ marathon/trunk/Source_Files/Files/game_wad.cpp (revision 523)
@@ -1,2601 +1,2600 @@
1-/*
2-GAME_WAD.C
3-
4- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5- and the "Aleph One" developers.
6-
7- This program is free software; you can redistribute it and/or modify
8- it under the terms of the GNU General Public License as published by
9- the Free Software Foundation; either version 3 of the License, or
10- (at your option) any later version.
11-
12- This program is distributed in the hope that it will be useful,
13- but WITHOUT ANY WARRANTY; without even the implied warranty of
14- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15- GNU General Public License for more details.
16-
17- This license is contained in the file "COPYING",
18- which is included with this source code; it is available online at
19- http://www.gnu.org/licenses/gpl.html
20-
21-Sunday, July 3, 1994 10:45:17 PM
22-
23-Routines for loading an entire game.
24-
25-Sunday, September 25, 1994 5:03:54 PM (alain)
26- call recalculate_redundant_endpoint_data() upon restoring saved game since
27- the redundant data isn't saved.
28-Sunday, November 6, 1994 5:35:34 PM
29- added support for the unified platforms/doors, cleaned up some old code of mine...
30-Saturday, August 26, 1995 2:28:56 PM
31- made portable.
32-
33-Jan 30, 2000 (Loren Petrich):
34- Added some typecasts
35- Removed some "static" declarations that conflict with "extern"
36-
37-Feb 4, 2000 (Loren Petrich):
38- Changed halt() to assert(false) for better debugging
39-
40-Feb 6, 2000 (Loren Petrich):
41- Added loading and saving of physics models in savegames and from map files
42-
43-Feb 12, 2000 (Loren Petrich):
44- Added MARATHON_INFINITY_DATA_VERSION where appropriate
45-
46-Feb 14, 2000 (Loren Petrich):
47- Added more Pfhorte-friendly error checking to reading in of
48- map-info ('Minf') chunk; allowing it to be 2 bytes shorter.
49-
50-Feb 17, 2000 (Loren Petrich):
51- Hides cursor after warning user about loading non-Bungie map files
52- (strERRORS, warningExternalMapsFile)
53-
54-Feb 19, 2000 (Loren Petrich):
55- Fixed off-by-one asserts in load_***() routines;
56-
57-Feb 26, 2000 (Loren Petrich):
58- Added chase-cam initialization
59-
60-June 15, 2000 (Loren Petrich):
61- Added supprt for Chris Pruett's Pfhortran
62-
63-Aug 12, 2000 (Loren Petrich):
64- Using object-oriented file handler
65-
66-Aug 25, 2000 (Loren Petrich):
67- Cleared errors (game_errors.c/h) produced by Pfhortran
68- and by checking on a scenario's image files
69-
70-Aug 28, 2000 (Loren Petrich):
71- Started on using new pack/unpack routines
72-
73-Nov 26, 2000 (Loren Petrich):
74- Movied a RunLevelScript() before some other stuff, such as entering_map(),
75- so that textures to be loaded can be specified before they actually get loaded.
76-
77-Feb 15, 2002 (Br'fin (Jeremy Parsons)):
78- Additional save data is now applied to the Temporary file instead of the original
79- (Old level preview info is now saved under Macintosh again)
80-*/
81-
82-// This needs to do the right thing on save game, which is storing the precalculated crap.
83-
84-#include "cseries.h"
85-
86-#include <string.h>
87-#include <stdlib.h>
88-
89-#include "map.h"
90-#include "monsters.h"
91-#include "network.h"
92-#include "projectiles.h"
93-#include "effects.h"
94-#include "player.h"
95-#include "platforms.h"
96-#include "flood_map.h"
97-#include "scenery.h"
98-#include "lightsource.h"
99-#include "media.h"
100-#include "weapons.h"
101-#include "shell.h"
102-#include "preferences.h"
103-#include "FileHandler.h"
104-
105-#include "editor.h"
106-#include "tags.h"
107-#include "wad.h"
108-#include "game_wad.h"
109-#include "interface.h"
110-#include "game_window.h"
111-#include "game_errors.h"
112-#include "computer_interface.h" // for loading/saving terminal state.
113-#include "images.h"
114-#include "shell.h"
115-#include "preferences.h"
116-#include "SoundManager.h"
117-#include "Plugins.h"
118-
119-// LP change: added chase-cam init and render allocation
120-#include "ChaseCam.h"
121-#include "render.h"
122-
123-#include "XML_LevelScript.h"
124-
125-// For packing and unpacking some of the stuff
126-#include "Packing.h"
127-
128-#include "motion_sensor.h" // ZZZ for reset_motion_sensor()
129-
130-#include "Music.h"
131-
132-#ifdef env68k
133-#pragma segment file_io
134-#endif
135-
136-// unify the save game code into one structure.
137-
138-/* -------- local globals */
139-FileSpecifier MapFileSpec;
140-static bool file_is_set= false;
141-
142-// LP addition: was a physics model loaded from the previous level loaded?
143-static bool PhysicsModelLoadedEarlier = false;
144-
145-// The following local globals are for handling games that need to be restored.
146-struct revert_game_info
147-{
148- bool game_is_from_disk;
149- struct game_data game_information;
150- struct player_start_data player_start;
151- struct entry_point entry_point;
152- FileSpecifier SavedGame;
153-};
154-static struct revert_game_info revert_game_data;
155-
156-#if 0
157-/* Borrowed from the old lightsource.h, to allow Marathon II to open/use Marathon I maps */
158-struct old_light_data {
159- uint16 flags;
160-
161- int16 type;
162- int16 mode; /* on, off, etc. */
163- int16 phase;
164-
165- fixed minimum_intensity, maximum_intensity;
166- int16 period; /* on, in ticks (turning on and off periods are always the same for a given light type,
167- or else are some function of this period) */
168-
169- fixed intensity; /* current intensity */
170-
171- int16 unused[5];
172-};
173-
174-enum /* old light types */
175-{
176- _light_is_normal,
177- _light_is_rheostat,
178- _light_is_flourescent,
179- _light_is_strobe,
180- _light_flickers,
181- _light_pulsates,
182- _light_is_annoying,
183- _light_is_energy_efficient
184-};
185-#endif
186-
187-/* -------- static functions */
188-static void scan_and_add_scenery(void);
189-static void complete_restoring_level(struct wad_data *wad);
190-static void load_redundant_map_data(short *redundant_data, size_t count);
191-static void allocate_map_structure_for_map(struct wad_data *wad);
192-static wad_data *build_export_wad(wad_header *header, int32 *length);
193-static struct wad_data *build_save_game_wad(struct wad_header *header, int32 *length);
194-
195-static void allocate_map_for_counts(size_t polygon_count, size_t side_count,
196- size_t endpoint_count, size_t line_count);
197-static void load_points(uint8 *points, size_t count);
198-static void load_lines(uint8 *lines, size_t count);
199-static void load_sides(uint8 *sides, size_t count, short version);
200-static void load_polygons(uint8 *polys, size_t count, short version);
201-static void load_lights(uint8 *_lights, size_t count, short version);
202-static void load_annotations(uint8 *annotations, size_t count);
203-static void load_objects(uint8 *map_objects, size_t count);
204-static void load_media(uint8 *_medias, size_t count);
205-static void load_map_info(uint8 *map_info);
206-static void load_ambient_sound_images(uint8 *data, size_t count);
207-static void load_random_sound_images(uint8 *data, size_t count);
208-static void load_terminal_data(uint8 *data, size_t length);
209-
210-/* Used _ONLY_ by game_wad.c internally and precalculate.c. */
211-// ZZZ: hmm, no longer true, now using when resuming a network saved-game... hope that's ok?...
212-//static bool process_map_wad(struct wad_data *wad, bool restoring_game, short version);
213-
214-/* Final three calls, must be in this order! */
215-static void recalculate_redundant_map(void);
216-static void scan_and_add_platforms(uint8 *platform_static_data, size_t count, short version);
217-static void complete_loading_level(short *_map_indexes, size_t map_index_count,
218- uint8 *_platform_data, size_t platform_data_count,
219- uint8 *actual_platform_data, size_t actual_platform_data_count, short version);
220-
221-static uint8 *unpack_directory_data(uint8 *Stream, directory_data *Objects, size_t Count);
222-//static uint8 *pack_directory_data(uint8 *Stream, directory_data *Objects, int Count);
223-
224-/* ------------------------ Net functions */
225-int32 get_net_map_data_length(
226- void *data)
227-{
228- return get_flat_data_length(data);
229-}
230-
231-/* Note that this frees it as well */
232-bool process_net_map_data(
233- void *data)
234-{
235- struct wad_header header;
236- struct wad_data *wad;
237- bool success= false;
238-
239- wad= inflate_flat_data(data, &header);
240- if(wad)
241- {
242- success= process_map_wad(wad, false, header.data_version);
243- free_wad(wad); /* Note that the flat data points into the wad. */
244- }
245-
246- return success;
247-}
248-
249-/* This will have to do some interesting voodoo with union wads, methinks */
250-void *get_map_for_net_transfer(
251- struct entry_point *entry)
252-{
253- assert(file_is_set);
254-
255- /* false means don't use union maps.. */
256- return get_flat_data(MapFileSpec, false, entry->level_number);
257-}
258-
259-/* ---------------------- End Net Functions ----------- */
260-
261-/* This takes a cstring */
262-void set_map_file(FileSpecifier& File)
263-{
264- // Do whatever parameter restoration is specified before changing the file
265- if (file_is_set) RunRestorationScript();
266-
267- MapFileSpec = File;
268- set_scenario_images_file(File);
269- // Only need to do this here
270- LoadLevelScripts(File);
271-
272- // Don't care whether there was an error when checking on the file's scenario images
273- clear_game_error();
274-
275- file_is_set= true;
276-}
277-
278-/* Set to the default map.. (Only if no map doubleclicked upon on startup.. */
279-void set_to_default_map(
280- void)
281-{
282- FileSpecifier NewMapFile;
283-
284- get_default_map_spec(NewMapFile);
285- set_map_file(NewMapFile);
286-}
287-
288-/* Return true if it finds the file, and it sets the mapfile to that file. */
289-/* Otherwise it returns false, meaning that we need have the file sent to us. */
290-bool use_map_file(
291- uint32 checksum)
292-{
293- FileSpecifier File;
294- bool success= false;
295-
296- if(find_wad_file_that_has_checksum(File, _typecode_scenario, strPATHS, checksum))
297- {
298- set_map_file(File);
299- success= true;
300- }
301-
302- return success;
303-}
304-
305-bool load_level_from_map(
306- short level_index)
307-{
308- OpenedFile OFile;
309- struct wad_header header;
310- struct wad_data *wad;
311- short index_to_load;
312- bool restoring_game= false;
313-
314- if(file_is_set)
315- {
316- /* Determine what we are trying to do.. */
317- if(level_index==NONE)
318- {
319- restoring_game= true;
320- index_to_load= 0; /* Saved games are always index 0 */
321- } else {
322- index_to_load= level_index;
323- }
324-
325- OpenedFile MapFile;
326- if (open_wad_file_for_reading(MapFileSpec,MapFile))
327- {
328- /* Read the file */
329- if(read_wad_header(MapFile, &header))
330- {
331- if(index_to_load>=0 && index_to_load<header.wad_count)
332- {
333-
334- wad= read_indexed_wad_from_file(MapFile, &header, index_to_load, true);
335- if (wad)
336- {
337- /* Process everything... */
338- process_map_wad(wad, restoring_game, header.data_version);
339-
340- /* Nuke our memory... */
341- free_wad(wad);
342- } else {
343- // error code has been set...
344- }
345- } else {
346- set_game_error(gameError, errWadIndexOutOfRange);
347- }
348- } else {
349- // error code has been set...
350- }
351-
352- /* Close the file.. */
353- close_wad_file(MapFile);
354- } else {
355- // error code has been set..
356- }
357- } else {
358- set_game_error(gameError, errMapFileNotSet);
359- }
360-
361- /* ... and bail */
362- return (!error_pending());
363-}
364-
365-// keep these around for level export
366-static std::vector<static_platform_data> static_platforms;
367-
368-extern bool ok_to_reset_scenery_solidity;
369-
370-/* Hopefully this is in the correct order of initialization... */
371-/* This sucks, beavis. */
372-void complete_loading_level(
373- short *_map_indexes,
374- size_t map_index_count,
375- uint8 *_platform_data,
376- size_t platform_data_count,
377- uint8 *actual_platform_data,
378- size_t actual_platform_data_count,
379- short version)
380-{
381- /* Scan, add the doors, recalculate, and generally tie up all loose ends */
382- /* Recalculate the redundant data.. */
383- load_redundant_map_data(_map_indexes, map_index_count);
384-
385- static_platforms.clear();
386-
387- /* Add the platforms. */
388- if(_platform_data || (_platform_data==NULL && actual_platform_data==NULL))
389- {
390- scan_and_add_platforms(_platform_data, platform_data_count, version);
391- } else {
392- assert(actual_platform_data);
393- PlatformList.resize(actual_platform_data_count);
394- unpack_platform_data(actual_platform_data,platforms,actual_platform_data_count);
395- assert(actual_platform_data_count == static_cast<size_t>(static_cast<int16>(actual_platform_data_count)));
396- assert(0 <= static_cast<int16>(actual_platform_data_count));
397- dynamic_world->platform_count= static_cast<int16>(actual_platform_data_count);
398- }
399-
400- scan_and_add_scenery();
401- ok_to_reset_scenery_solidity = true;
402-
403- /* Gotta do this after recalculate redundant.. */
404- if(version==MARATHON_ONE_DATA_VERSION)
405- {
406- short loop;
407-
408- for(loop= 0; loop<dynamic_world->side_count; ++loop)
409- {
410- guess_side_lightsource_indexes(loop);
411- }
412- }
413-}
414-
415-/* Call with location of NULL to get the number of start locations for a */
416-/* given team or player */
417-short get_player_starting_location_and_facing(
418- short team,
419- short index,
420- struct object_location *location)
421-{
422-#if 1
423- short ii;
424- struct map_object *saved_object;
425- short count= 0;
426- bool done= false;
427-
428- saved_object= saved_objects;
429- for(ii=0; !done && ii<dynamic_world->initial_objects_count; ++ii)
430- {
431- if(saved_object->type==_saved_player)
432- {
433- /* index=NONE means use any starting location */
434- if(saved_object->index==team || team==NONE)
435- {
436- if(location && count==index)
437- {
438- location->p= saved_object->location;
439- location->polygon_index= saved_object->polygon_index;
440- location->yaw= saved_object->facing;
441- location->pitch= 0;
442- location->flags= saved_object->flags;
443- done= true;
444- }
445- count++;
446- }
447- }
448- ++saved_object;
449- }
450-
451- /* If they asked for a valid location, make sure that we gave them one */
452- if(location) vassert(done, csprintf(temporary, "Tried to place: %d only %d starting pts.", index, count));
453-
454- return count;
455-#else
456- location->x= 0x14e9;
457- location->y= 0x1ba0;
458- *facing= 0x00;
459- *polygon_index= 0x6f;
460-
461- return 1;
462-#endif
463-}
464-
465-uint32 get_current_map_checksum(
466- void)
467-{
468- // fileref file_handle;
469- struct wad_header header;
470-
471- assert(file_is_set);
472- OpenedFile MapFile;
473- open_wad_file_for_reading(MapFileSpec, MapFile);
474- assert(MapFile.IsOpen());
475-
476- /* Read the file */
477- read_wad_header(MapFile, &header);
478-
479- /* Close the file.. */
480- close_wad_file(MapFile);
481-
482- return header.checksum;
483-}
484-
485-// ZZZ: split this out from new_game for sharing
486-void set_saved_game_name_to_default()
487-{
488-#if defined(mac) || defined(SDL_RFORK_HACK)
489- revert_game_data.SavedGame.SetToApp();
490- revert_game_data.SavedGame.SetName(getcstr(temporary, strFILENAMES, filenameDEFAULT_SAVE_GAME),_typecode_savegame);
491-#endif
492-#if defined(SDL) && !defined(SDL_RFORK_HACK)
493- revert_game_data.SavedGame.SetToSavedGamesDir();
494- revert_game_data.SavedGame += getcstr(temporary, strFILENAMES, filenameDEFAULT_SAVE_GAME);
495-#endif
496-}
497-
498-extern void ResetPassedLua();
499-
500-bool new_game(
501- short number_of_players,
502- bool network,
503- struct game_data *game_information,
504- struct player_start_data *player_start_information,
505- struct entry_point *entry_point)
506-{
507- short player_index, i;
508- bool success= true;
509-
510- ResetPassedLua();
511-
512- /* Make sure our code is synchronized.. */
513- assert(MAXIMUM_PLAYER_START_NAME_LENGTH==MAXIMUM_PLAYER_NAME_LENGTH);
514-
515- /* Initialize the global network going flag... */
516- game_is_networked= network;
517-
518- /* If we want to save it, this is an untitled map.. */
519- set_saved_game_name_to_default();
520-
521- /* Set the random seed. */
522- set_random_seed(game_information->initial_random_seed);
523-
524- /* Initialize the players to a known state. This must be done before goto_level */
525- /* because it sets dynamic_world->player_count to 0, which is crucial for when */
526- /* I try to recreate the players... */
527- initialize_map_for_new_game(); // memsets dynamic_world to 0
528-
529- /* Copy the game data into the dynamic_world */
530- /* ajr-this used to be done only when we successfully loaded the map. however, goto_level
531- * will place the initial monsters on a level, which calls new_monster, which relies
532- * on this information being setup properly, so we do it here instead. */
533- obj_copy(dynamic_world->game_information, *game_information);
534-
535- /* Load the level */
536- assert(file_is_set);
537- success= goto_level(entry_point, true);
538- /* If we were able to load the map... */
539- if(success)
540- {
541- /* Initialize the players-> note there may be more than one player in a */
542- /* non-network game, for playback.. */
543- for (i=0;i<number_of_players;++i)
544- {
545- player_index= new_player(player_start_information[i].team,
546- player_start_information[i].color, player_start_information[i].identifier);
547- assert(player_index==i);
548-
549- /* Now copy in the name of the player.. */
550- assert(strlen(player_start_information[i].name)<=MAXIMUM_PLAYER_NAME_LENGTH);
551- strcpy(players[i].name, player_start_information[i].name);
552- }
553-
554-#if !defined(DISABLE_NETWORKING)
555- if(game_is_networked)
556- {
557- /* Make sure we can count. */
558- assert(number_of_players==NetGetNumberOfPlayers());
559-
560- set_local_player_index(NetGetLocalPlayerIndex());
561- set_current_player_index(NetGetLocalPlayerIndex());
562- }
563- else
564-#endif // !defined(DISABLE_NETWORKING)
565- {
566- set_local_player_index(0);
567- set_current_player_index(0);
568- }
569-
570- /* we need to alert the function that reverts the game of the game setup so that
571- * new game can be called if the user wants to revert later.
572- */
573- setup_revert_game_info(game_information, player_start_information, entry_point);
574-
575- // Reset the player queues (done here and in load_game)
576- reset_action_queues();
577-
578- /* Load the collections */
579- /* entering map might fail if NetSync() fails.. */
580- success= entering_map(false);
581-
582- // ZZZ: set motion sensor to sane state - needs to come after entering_map() (which calls load_collections())
583- reset_motion_sensor(current_player_index);
584- }
585-
586- // LP change: adding chase-cam initialization
587- ChaseCam_Initialize();
588-
589- return success;
590-}
591-
592-bool get_indexed_entry_point(
593- struct entry_point *entry_point,
594- short *index,
595- int32 type)
596-{
597- short actual_index;
598-
599- // Open map file
600- assert(file_is_set);
601- OpenedFile MapFile;
602- if (!open_wad_file_for_reading(MapFileSpec,MapFile))
603- return false;
604-
605- // Read header
606- wad_header header;
607- if (!read_wad_header(MapFile, &header)) {
608- close_wad_file(MapFile);
609- return false;
610- }
611-
612- bool success = false;
613- if (header.application_specific_directory_data_size == SIZEOF_directory_data)
614- {
615-
616- // New style wad
617- void *total_directory_data= read_directory_data(MapFile, &header);
618-
619- assert(total_directory_data);
620- for(actual_index= *index; actual_index<header.wad_count; ++actual_index)
621- {
622- uint8 *p = (uint8 *)get_indexed_directory_data(&header, actual_index, total_directory_data);
623- directory_data directory;
624- unpack_directory_data(p, &directory, 1);
625-
626- /* Find the flags that match.. */
627- if(directory.entry_point_flags & type)
628- {
629- /* This one is valid! */
630- entry_point->level_number= actual_index;
631- strcpy(entry_point->level_name, directory.level_name);
632-
633- *index= actual_index+1;
634- success= true;
635- break; /* Out of the for loop */
636- }
637- }
638- free(total_directory_data);
639-
640- } else {
641-
642- // Old style wad, find the index
643- for(actual_index= *index; !success && actual_index<header.wad_count; ++actual_index)
644- {
645- struct wad_data *wad;
646-
647- /* Read the file */
648- wad= read_indexed_wad_from_file(MapFile, &header, actual_index, true);
649- if (wad)
650- {
651- /* IF this has the proper type.. */
652- size_t length;
653- uint8 *p = (uint8 *)extract_type_from_wad(wad, MAP_INFO_TAG, &length);
654- assert(length == SIZEOF_static_data);
655- static_data map_info;
656- unpack_static_data(p, &map_info, 1);
657-
658- if(map_info.entry_point_flags & type)
659- {
660- /* This one is valid! */
661- entry_point->level_number= actual_index;
662- assert(strlen(map_info.level_name)<LEVEL_NAME_LENGTH);
663- strcpy(entry_point->level_name, map_info.level_name);
664-
665- *index= actual_index+1;
666- success= true;
667- }
668-
669- free_wad(wad);
670- }
671- }
672- }
673-
674- return success;
675-}
676-
677-// Get vector of map entry points matching given type
678-bool get_entry_points(vector<entry_point> &vec, int32 type)
679-{
680- vec.clear();
681-
682- // Open map file
683- assert(file_is_set);
684- OpenedFile MapFile;
685- if (!open_wad_file_for_reading(MapFileSpec,MapFile))
686- return false;
687-
688- // Read header
689- wad_header header;
690- if (!read_wad_header(MapFile, &header)) {
691- close_wad_file(MapFile);
692- return false;
693- }
694-
695- bool success = false;
696- if (header.application_specific_directory_data_size == SIZEOF_directory_data) {
697-
698- // New style wad, read directory data
699- void *total_directory_data = read_directory_data(MapFile, &header);
700- assert(total_directory_data);
701-
702- // Push matching directory entries into vector
703- for (int i=0; i<header.wad_count; i++) {
704- uint8 *p = (uint8 *)get_indexed_directory_data(&header, i, total_directory_data);
705- directory_data directory;
706- unpack_directory_data(p, &directory, 1);
707-
708- if (directory.entry_point_flags & type) {
709-
710- // This one is valid
711- entry_point point;
712- point.level_number = i;
713- strcpy(point.level_name, directory.level_name);
714- vec.push_back(point);
715- success = true;
716- }
717- }
718- free(total_directory_data);
719-
720- } else {
721-
722- // Old style wad
723- for (int i=0; i<header.wad_count; i++) {
724-
725- wad_data *wad = read_indexed_wad_from_file(MapFile, &header, i, true);
726- if (!wad)
727- continue;
728-
729- // Read map_info data
730- size_t length;
731- uint8 *p = (uint8 *)extract_type_from_wad(wad, MAP_INFO_TAG, &length);
732- assert(length == SIZEOF_static_data);
733- static_data map_info;
734- unpack_static_data(p, &map_info, 1);
735-
736- if (map_info.entry_point_flags & type) {
737-
738- // This one is valid
739- entry_point point;
740- point.level_number = i;
741- assert(strlen(map_info.level_name) < LEVEL_NAME_LENGTH);
742- strcpy(point.level_name, map_info.level_name);
743- vec.push_back(point);
744- success = true;
745- }
746-
747- free_wad(wad);
748- }
749- }
750-
751- return success;
752-}
753-
754-extern void LoadSoloLua();
755-extern void RunLuaScript();
756-
757-/* This is called when the game level is changed somehow */
758-/* The only thing that has to be valid in the entry point is the level_index */
759-
760-/* Returns a short that is an OSErr... */
761-bool goto_level(
762- struct entry_point *entry,
763- bool new_game)
764-{
765- bool success= true;
766-
767- if(!new_game)
768- {
769- /* Clear the current map */
770- leaving_map();
771-
772- // ghs: hack to get new MML-specified sounds loaded
773- SoundManager::instance()->UnloadAllSounds();
774- }
775-
776-#if !defined(DISABLE_NETWORKING)
777- /* If the game is networked, then I must call the network code to do the right */
778- /* thing with the map.. */
779- if(game_is_networked)
780- {
781- /* This function, if it is a server, calls get_map_for_net_transfer, and */
782- /* then calls process_map_wad on it. Non-server receives the map and then */
783- /* calls process_map_wad on it. */
784- success= NetChangeMap(entry);
785- }
786- else
787-#endif // !defined(DISABLE_NETWORKING)
788- {
789- /* Load it and then rock.. */
790- load_level_from_map(entry->level_number);
791- if(error_pending()) success= false;
792- }
793-
794- if (success)
795- {
796- // LP: doing this here because level-specific MML may specify which level-specific
797- // textures to load.
798- // Being careful to carry over errors so that Pfhortran errors can be ignored
799- short SavedType, SavedError = get_game_error(&SavedType);
800- if (!game_is_networked || use_map_file(((game_info *) NetGetGameData())->parent_checksum))
801- {
802- RunLevelScript(entry->level_number);
803- }
804- else
805- {
806- ResetLevelScript();
807- }
808- RunScriptChunks();
809- if (!game_is_networked)
810- {
811- Plugins::instance()->load_solo_mml();
812- LoadSoloLua();
813- }
814- Music::instance()->PreloadLevelMusic();
815- set_game_error(SavedType,SavedError);
816-
817- if (!new_game)
818- {
819- recreate_players_for_new_level();
820- }
821-
822- /* Load the collections */
823- dynamic_world->current_level_number= entry->level_number;
824-
825- // ghs: this runs very early now
826- // we want to be before place_initial_objects, and
827- // before MarkLuaCollections
828- RunLuaScript();
829-
830- if (film_profile.early_object_initialization)
831- {
832- place_initial_objects();
833- initialize_control_panels_for_level();
834- }
835-
836- if (!new_game)
837- {
838-
839- /* entering_map might fail if netsync fails, but we will have already displayed */
840- /* the error.. */
841- success= entering_map(false);
842- }
843-
844- if (!film_profile.early_object_initialization && success)
845- {
846- place_initial_objects();
847- initialize_control_panels_for_level();
848- }
849-
850- }
851-
852-// if(!success) alert_user(fatalError, strERRORS, badReadMap, -1);
853-
854- /* We be done.. */
855- return success;
856-}
857-
858-/* -------------------- Private or map editor functions */
859-void allocate_map_for_counts(
860- size_t polygon_count,
861- size_t side_count,
862- size_t endpoint_count,
863- size_t line_count)
864-{
865- //long cumulative_length= 0;
866- size_t automap_line_count, automap_polygon_count, map_index_count;
867- // long automap_line_length, automap_polygon_length, map_index_length;
868-
869- /* Give the map indexes a whole bunch of memory (cause we can't calculate it) */
870- // map_index_length= (polygon_count*32+1024)*sizeof(int16);
871- map_index_count= (polygon_count*32+1024);
872-
873- /* Automap lines. */
874- // automap_line_length= (line_count/8+((line_count%8)?1:0))*sizeof(byte);
875- automap_line_count= (line_count/8+((line_count%8)?1:0));
876-
877- /* Automap Polygons */
878- // automap_polygon_length= (polygon_count/8+((polygon_count%8)?1:0))*sizeof(byte);
879- automap_polygon_count= (polygon_count/8+((polygon_count%8)?1:0));
880-
881- // cumulative_length+= polygon_count*sizeof(struct polygon_data);
882- // cumulative_length+= side_count*sizeof(struct side_data);
883- // cumulative_length+= endpoint_count*sizeof(struct endpoint_data);
884- // cumulative_length+= line_count*sizeof(struct line_data);
885- // cumulative_length+= map_index_length;
886- // cumulative_length+= automap_line_length;
887- // cumulative_length+= automap_polygon_length;
888-
889- /* Okay, we now have the length. Allocate our block.. */
890- // reallocate_map_structure_memory(cumulative_length);
891-
892- /* Tell the recalculation data how big it is.. */
893- // set_map_index_buffer_size(map_index_length);
894-
895- /* Setup our pointers. */
896- // map_polygons= (struct polygon_data *) get_map_structure_chunk(polygon_count*sizeof(struct polygon_data));
897- // map_sides= (struct side_data *) get_map_structure_chunk(side_count*sizeof(struct side_data));
898- // map_endpoints= (struct endpoint_data *) get_map_structure_chunk(endpoint_count*sizeof(struct endpoint_data));
899- // map_lines= (struct line_data *) get_map_structure_chunk(line_count*sizeof(struct line_data));
900- // map_indexes= (short *) get_map_structure_chunk(map_index_length);
901- // automap_lines= (uint8 *) get_map_structure_chunk(automap_line_length);
902- // automap_polygons= (uint8 *) get_map_structure_chunk(automap_polygon_length);
903-
904- // Most of the other stuff: reallocate here
905- EndpointList.resize(endpoint_count);
906- LineList.resize(line_count);
907- SideList.resize(side_count);
908- PolygonList.resize(polygon_count);
909- AutomapLineList.resize(automap_line_count);
910- AutomapPolygonList.resize(automap_polygon_count);
911-
912- // Map indexes: start off with none of them (of course),
913- // but reserve a size equal to the map index length
914- MapIndexList.clear();
915- MapIndexList.reserve(map_index_count);
916- dynamic_world->map_index_count= 0;
917-
918- // Stuff that needs the max number of polygons
919- allocate_render_memory();
920- allocate_flood_map_memory();
921-}
922-
923-void load_points(
924- uint8 *points,
925- size_t count)
926-{
927- size_t loop;
928-
929- // OK to modify input-data pointer since it's called by value
930- for(loop=0; loop<count; ++loop)
931- {
932- world_point2d& vertex = map_endpoints[loop].vertex;
933- StreamToValue(points,vertex.x);
934- StreamToValue(points,vertex.y);
935- }
936- assert(count == static_cast<size_t>(static_cast<int16>(count)));
937- assert(0 <= static_cast<int16>(count));
938- dynamic_world->endpoint_count= static_cast<int16>(count);
939-}
940-
941-void load_lines(
942- uint8 *lines,
943- size_t count)
944-{
945- // assert(count>=0 && count<=MAXIMUM_LINES_PER_MAP);
946- unpack_line_data(lines,map_lines,count);
947- assert(count == static_cast<size_t>(static_cast<int16>(count)));
948- assert(0 <= static_cast<int16>(count));
949- dynamic_world->line_count= static_cast<int16>(count);
950-}
951-
952-void load_sides(
953- uint8 *sides,
954- size_t count,
955- short version)
956-{
957- size_t loop;
958-
959- // assert(count>=0 && count<=MAXIMUM_SIDES_PER_MAP);
960-
961- unpack_side_data(sides,map_sides,count);
962-
963- for(loop=0; loop<count; ++loop)
964- {
965- if(version==MARATHON_ONE_DATA_VERSION)
966- {
967- map_sides[loop].transparent_texture.texture= UNONE;
968- map_sides[loop].ambient_delta= 0;
969- }
970- ++sides;
971- }
972-
973- assert(count == static_cast<size_t>(static_cast<int16>(count)));
974- assert(0 <= static_cast<int16>(count));
975- dynamic_world->side_count= static_cast<int16>(count);
976-}
977-
978-void load_polygons(
979- uint8 *polys,
980- size_t count,
981- short version)
982-{
983- size_t loop;
984-
985- // assert(count>=0 && count<=MAXIMUM_POLYGONS_PER_MAP);
986-
987- unpack_polygon_data(polys,map_polygons,count);
988- assert(count == static_cast<size_t>(static_cast<int16>(count)));
989- assert(0 <= static_cast<int16>(count));
990- dynamic_world->polygon_count= static_cast<int16>(count);
991-
992- /* Allow for backward compatibility! */
993- switch(version)
994- {
995- case MARATHON_ONE_DATA_VERSION:
996- for(loop= 0; loop<count; ++loop)
997- {
998- map_polygons[loop].media_index= NONE;
999- map_polygons[loop].floor_origin.x= map_polygons[loop].floor_origin.y= 0;
1000- map_polygons[loop].ceiling_origin.x= map_polygons[loop].ceiling_origin.y= 0;
1001- }
1002- break;
1003-
1004- case MARATHON_TWO_DATA_VERSION:
1005- // LP addition:
1006- case MARATHON_INFINITY_DATA_VERSION:
1007- break;
1008-
1009- default:
1010- assert(false);
1011- break;
1012- }
1013-}
1014-
1015-void load_lights(
1016- uint8 *_lights,
1017- size_t count,
1018- short version)
1019-{
1020- unsigned short loop, new_index;
1021-
1022- LightList.resize(count);
1023- objlist_clear(lights,count);
1024- // vassert(count>=0 && count<=MAXIMUM_LIGHTS_PER_MAP, csprintf(temporary, "Light count: %d vers: %d",
1025- // count, version));
1026-
1027- old_light_data *OldLights;
1028-
1029- switch(version)
1030- {
1031- case MARATHON_ONE_DATA_VERSION: {
1032-
1033- // Unpack the old lights into a temporary array
1034- OldLights = new old_light_data[count];
1035- unpack_old_light_data(_lights,OldLights,count);
1036-
1037- old_light_data *OldLtPtr = OldLights;
1038- for(loop= 0; loop<count; ++loop, OldLtPtr++)
1039- {
1040- static_light_data TempLight;
1041- convert_old_light_data_to_new(&TempLight, OldLtPtr, 1);
1042-
1043- new_index = new_light(&TempLight);
1044- assert(new_index==loop);
1045- }
1046- delete []OldLights;
1047- break;
1048- }
1049-
1050- case MARATHON_TWO_DATA_VERSION:
1051- case MARATHON_INFINITY_DATA_VERSION:
1052- // OK to modify the data pointer since it was passed by value
1053- for(loop= 0; loop<count; ++loop)
1054- {
1055- static_light_data TempLight;
1056- _lights = unpack_static_light_data(_lights, &TempLight, 1);
1057-
1058- new_index = new_light(&TempLight);
1059- assert(new_index==loop);
1060- }
1061- break;
1062-
1063- default:
1064- assert(false);
1065- break;
1066- }
1067-}
1068-
1069-void load_annotations(
1070- uint8 *annotations,
1071- size_t count)
1072-{
1073- // assert(count>=0 && count<=MAXIMUM_ANNOTATIONS_PER_MAP);
1074- MapAnnotationList.resize(count);
1075- unpack_map_annotation(annotations,map_annotations,count);
1076- assert(count == static_cast<size_t>(static_cast<int16>(count)));
1077- assert(0 <= static_cast<int16>(count));
1078- dynamic_world->default_annotation_count= static_cast<int16>(count);
1079-}
1080-
1081-void load_objects(uint8 *map_objects, size_t count)
1082-{
1083- // assert(count>=0 && count<=MAXIMUM_SAVED_OBJECTS);
1084- SavedObjectList.resize(count);
1085- unpack_map_object(map_objects,saved_objects,count);
1086- assert(count == static_cast<size_t>(static_cast<int16>(count)));
1087- assert(0 <= static_cast<int16>(count));
1088- dynamic_world->initial_objects_count= static_cast<int16>(count);
1089-}
1090-
1091-void load_map_info(
1092- uint8 *map_info)
1093-{
1094- unpack_static_data(map_info,static_world,1);
1095- static_world->ball_in_play = false;
1096-}
1097-
1098-void load_media(
1099- uint8 *_medias,
1100- size_t count)
1101-{
1102- // struct media_data *media= _medias;
1103- size_t ii;
1104-
1105- MediaList.resize(count);
1106- objlist_clear(medias,count);
1107- // assert(count>=0 && count<=MAXIMUM_MEDIAS_PER_MAP);
1108-
1109- for(ii= 0; ii<count; ++ii)
1110- {
1111- media_data TempMedia;
1112- _medias = unpack_media_data(_medias,&TempMedia,1);
1113-
1114- size_t new_index = new_media(&TempMedia);
1115- assert(new_index==ii);
1116- }
1117-}
1118-
1119-void load_ambient_sound_images(
1120- uint8 *data,
1121- size_t count)
1122-{
1123- // assert(count>=0 &&count<=MAXIMUM_AMBIENT_SOUND_IMAGES_PER_MAP);
1124- AmbientSoundImageList.resize(count);
1125- unpack_ambient_sound_image_data(data,ambient_sound_images,count);
1126- assert(count == static_cast<size_t>(static_cast<int16>(count)));
1127- assert(0 <= static_cast<int16>(count));
1128- dynamic_world->ambient_sound_image_count= static_cast<int16>(count);
1129-}
1130-
1131-void load_random_sound_images(
1132- uint8 *data,
1133- size_t count)
1134-{
1135- // assert(count>=0 &&count<=MAXIMUM_RANDOM_SOUND_IMAGES_PER_MAP);
1136- RandomSoundImageList.resize(count);
1137- unpack_random_sound_image_data(data,random_sound_images,count);
1138- assert(count == static_cast<size_t>(static_cast<int16>(count)));
1139- assert(0 <= static_cast<int16>(count));
1140- dynamic_world->random_sound_image_count= static_cast<int16>(count);
1141-}
1142-
1143-/* Recalculate all the redundant crap- must be done before platforms/doors/etc.. */
1144-void recalculate_redundant_map(
1145- void)
1146-{
1147- short loop;
1148-
1149- for(loop=0;loop<dynamic_world->polygon_count;++loop) recalculate_redundant_polygon_data(loop);
1150- for(loop=0;loop<dynamic_world->line_count;++loop) recalculate_redundant_line_data(loop);
1151- for(loop=0;loop<dynamic_world->endpoint_count;++loop) recalculate_redundant_endpoint_data(loop);
1152-}
1153-
1154-extern bool load_game_from_file(FileSpecifier& File);
1155-
1156-bool load_game_from_file(FileSpecifier& File)
1157-{
1158- bool success= false;
1159-
1160- ResetPassedLua();
1161-
1162- /* Setup for a revert.. */
1163- revert_game_data.game_is_from_disk = true;
1164- revert_game_data.SavedGame = File;
1165-
1166- /* Use the save game file.. */
1167- set_map_file(File);
1168-
1169- /* Load the level from the map */
1170- success= load_level_from_map(NONE); /* Save games are ALWAYS index NONE */
1171- if (success)
1172- {
1173- uint32 parent_checksum;
1174-
1175- /* Find the original scenario this saved game was a part of.. */
1176- parent_checksum= read_wad_file_parent_checksum(File);
1177- if(use_map_file(parent_checksum))
1178- {
1179- // LP: getting the level scripting off of the map file
1180- // Being careful to carry over errors so that Pfhortran errors can be ignored
1181- short SavedType, SavedError = get_game_error(&SavedType);
1182- RunLevelScript(dynamic_world->current_level_number);
1183- RunScriptChunks();
1184- if (!game_is_networked)
1185- {
1186- Plugins::instance()->load_solo_mml();
1187- LoadSoloLua();
1188- }
1189- set_game_error(SavedType,SavedError);
1190- }
1191- else
1192- {
1193- /* Tell the user theyÕre screwed when they try to leave this level. */
1194- alert_user(infoError, strERRORS, cantFindMap, 0);
1195-
1196- // LP addition: makes the game look normal
1197- hide_cursor();
1198-
1199- /* Set to the default map. */
1200- set_to_default_map();
1201-
1202- ResetLevelScript();
1203- RunScriptChunks();
1204- }
1205- }
1206-
1207- return success;
1208-}
1209-
1210-void setup_revert_game_info(
1211- struct game_data *game_info,
1212- struct player_start_data *start,
1213- struct entry_point *entry)
1214-{
1215- revert_game_data.game_is_from_disk = false;
1216- obj_copy(revert_game_data.game_information, *game_info);
1217- obj_copy(revert_game_data.player_start, *start);
1218- obj_copy(revert_game_data.entry_point, *entry);
1219-}
1220-
1221-extern void reset_messages();
1222-
1223-bool revert_game(
1224- void)
1225-{
1226- bool successful;
1227-
1228- assert(dynamic_world->player_count==1);
1229-
1230- leaving_map();
1231-
1232- if (revert_game_data.game_is_from_disk)
1233- {
1234- /* Reload their last saved game.. */
1235- successful= load_game_from_file(revert_game_data.SavedGame);
1236- if (successful)
1237- {
1238- Music::instance()->PreloadLevelMusic();
1239- RunLuaScript();
1240-
1241- // LP: added for loading the textures if one had died on another level;
1242- // this gets around WZ's moving of this line into make_restored_game_relevant()
1243- successful = entering_map(true /*restoring game*/);
1244- }
1245-
1246- /* And they don't get to continue. */
1247- stop_recording();
1248- }
1249- else
1250- {
1251- /* This was the totally evil line discussed above. */
1252- successful= new_game(1, false, &revert_game_data.game_information, &revert_game_data.player_start,
1253- &revert_game_data.entry_point);
1254-
1255- /* And rewind so that the last player is used. */
1256- rewind_recording();
1257- }
1258-
1259- if(successful)
1260- {
1261- update_interface(NONE);
1262- ChaseCam_Reset();
1263- ResetFieldOfView();
1264- reset_messages();
1265- ReloadViewContext();
1266- }
1267-
1268- return successful;
1269-}
1270-
1271-bool export_level(FileSpecifier& File)
1272-{
1273- struct wad_header header;
1274- short err = 0;
1275- bool success = false;
1276- int32 offset, wad_length;
1277- struct directory_entry entry;
1278- struct wad_data *wad;
1279-
1280- FileSpecifier TempFile;
1281- DirectorySpecifier TempFileDir;
1282- File.ToDirectory(TempFileDir);
1283- TempFile.FromDirectory(TempFileDir);
1284- TempFile.AddPart("savetemp.dat");
1285-
1286- /* Fill in the default wad header (we are using File instead of TempFile to get the name right in the header) */
1287- fill_default_wad_header(File, CURRENT_WADFILE_VERSION, MARATHON_TWO_DATA_VERSION, 1, 0, &header);
1288-
1289- if (create_wadfile(TempFile, _typecode_scenario))
1290- {
1291- OpenedFile SaveFile;
1292- if (open_wad_file_for_writing(TempFile, SaveFile))
1293- {
1294- /* Write out the new header */
1295- if (write_wad_header(SaveFile, &header))
1296- {
1297- offset = SIZEOF_wad_header;
1298-
1299- wad = build_export_wad(&header, &wad_length);
1300- if (wad)
1301- {
1302- set_indexed_directory_offset_and_length(&header, &entry, 0, offset, wad_length, 0);
1303-
1304- if (write_wad(SaveFile, &header, wad, offset))
1305- {
1306- /* Update the new header */
1307- offset+= wad_length;
1308- header.directory_offset= offset;
1309- if (write_wad_header(SaveFile, &header) && write_directorys(SaveFile, &header, &entry))
1310- {
1311- /* We win. */
1312- success= true;
1313- }
1314- }
1315-
1316- free_wad(wad);
1317- }
1318- }
1319-
1320- err = SaveFile.GetError();
1321- calculate_and_store_wadfile_checksum(SaveFile);
1322- close_wad_file(SaveFile);
1323- }
1324-
1325- if (!err)
1326- {
1327- if (!File.Exists())
1328- create_wadfile(File,_typecode_savegame);
1329-
1330- TempFile.Exchange(File);
1331- err = TempFile.GetError();
1332- TempFile.Delete(); // it's not an error if this fails
1333- }
1334- }
1335-
1336- if (err || error_pending())
1337- {
1338- success = false;
1339- }
1340-
1341-
1342- return success;
1343-
1344-}
1345-
1346-void get_current_saved_game_name(FileSpecifier& File)
1347-{
1348- File = revert_game_data.SavedGame;
1349-}
1350-
1351-/* The current mapfile should be set to the save game file... */
1352-bool save_game_file(FileSpecifier& File)
1353-{
1354- struct wad_header header;
1355- short err = 0;
1356- bool success= false;
1357- int32 offset, wad_length;
1358- struct directory_entry entry;
1359- struct wad_data *wad;
1360-
1361- /* Save off the random seed. */
1362- dynamic_world->random_seed= get_random_seed();
1363-
1364- /* Setup to revert the game properly */
1365- revert_game_data.game_is_from_disk= true;
1366- revert_game_data.SavedGame = File;
1367-
1368- // LP: add a file here; use temporary file for a safe save.
1369- // Write into the temporary file first
1370- FileSpecifier TempFile;
1371- DirectorySpecifier TempFileDir;
1372- File.ToDirectory(TempFileDir);
1373- TempFile.FromDirectory(TempFileDir);
1374-#if defined(mac) || defined(SDL_RFORK_HACK)
1375- TempFile.SetName("savetemp.dat",NONE);
1376-#else
1377- TempFile.AddPart("savetemp.dat");
1378-#endif
1379-
1380- /* Fill in the default wad header (we are using File instead of TempFile to get the name right in the header) */
1381- fill_default_wad_header(File, CURRENT_WADFILE_VERSION, EDITOR_MAP_VERSION, 1, 0, &header);
1382-
1383- /* Assume that we confirmed on save as... */
1384- if (create_wadfile(TempFile,_typecode_savegame))
1385- {
1386- OpenedFile SaveFile;
1387- if(open_wad_file_for_writing(TempFile,SaveFile))
1388- {
1389- /* Write out the new header */
1390- if (write_wad_header(SaveFile, &header))
1391- {
1392- offset= SIZEOF_wad_header;
1393-
1394- wad= build_save_game_wad(&header, &wad_length);
1395- if (wad)
1396- {
1397- /* Set the entry data.. */
1398- set_indexed_directory_offset_and_length(&header,
1399- &entry, 0, offset, wad_length, 0);
1400-
1401- /* Save it.. */
1402- if (write_wad(SaveFile, &header, wad, offset))
1403- {
1404- /* Update the new header */
1405- offset+= wad_length;
1406- header.directory_offset= offset;
1407- header.parent_checksum= read_wad_file_checksum(MapFileSpec);
1408- if (write_wad_header(SaveFile, &header) && write_directorys(SaveFile, &header, &entry))
1409- {
1410- /* This function saves the overhead map as a thumbnail, as well */
1411- /* as adding the string resource that tells you what it is when */
1412- /* it is clicked on & Marathon2 isn't installed. Obviously, both */
1413- /* of these are superfluous for a dos environment. */
1414- add_finishing_touches_to_save_file(TempFile);
1415-
1416- /* We win. */
1417- success= true;
1418- }
1419- }
1420-
1421- free_wad(wad);
1422- }
1423- }
1424-
1425- err = SaveFile.GetError();
1426- close_wad_file(SaveFile);
1427- }
1428-
1429- // LP addition: exchange with temporary file;
1430- // create target file if necessary
1431- if (!err)
1432- {
1433- if (!File.Exists())
1434- create_wadfile(File,_typecode_savegame);
1435-
1436- TempFile.Exchange(File);
1437- err = TempFile.GetError();
1438- TempFile.Delete(); // it's not an error if this fails
1439- }
1440- }
1441-
1442- if(err || error_pending())
1443- {
1444- if(!err) err= get_game_error(NULL);
1445- alert_user(infoError, strERRORS, fileError, err);
1446- clear_game_error();
1447- success= false;
1448- }
1449-
1450- return success;
1451-}
1452-
1453-/* -------- static functions */
1454-static void scan_and_add_platforms(
1455- uint8 *platform_static_data,
1456- size_t count,
1457- short version)
1458-{
1459- struct polygon_data *polygon;
1460- short loop;
1461-
1462- PlatformList.resize(count);
1463- objlist_clear(platforms,count);
1464-
1465- static_platforms.resize(count);
1466- unpack_static_platform_data(platform_static_data, &static_platforms[0], count);
1467-
1468- polygon= map_polygons;
1469- for(loop=0; loop<dynamic_world->polygon_count; ++loop)
1470- {
1471- if (polygon->type==_polygon_is_platform)
1472- {
1473- /* Search and find the extra data. If it is not there, use the permutation for */
1474- /* backwards compatibility! */
1475-
1476- size_t platform_static_data_index;
1477- for(platform_static_data_index = 0; platform_static_data_index<count; ++platform_static_data_index)
1478- {
1479- if (static_platforms[platform_static_data_index].polygon_index == loop)
1480- {
1481- new_platform(&static_platforms[platform_static_data_index], loop, version);
1482- break;
1483- }
1484- }
1485-
1486- /* DIdn't find it- use a standard platform */
1487- if(platform_static_data_index==count)
1488- {
1489- polygon->permutation= 1;
1490- new_platform(get_defaults_for_platform_type(polygon->permutation), loop, version);
1491- }
1492- }
1493- ++polygon;
1494- }
1495-}
1496-
1497-
1498-extern void unpack_lua_states(uint8*, size_t);
1499-
1500-/* Load a level from a wad-> mainly used by the net stuff. */
1501-bool process_map_wad(
1502- struct wad_data *wad,
1503- bool restoring_game,
1504- short version)
1505-{
1506- size_t data_length;
1507- uint8 *data;
1508- size_t count;
1509- bool is_preprocessed_map= false;
1510-
1511- assert(version==MARATHON_INFINITY_DATA_VERSION || version==MARATHON_TWO_DATA_VERSION || version==MARATHON_ONE_DATA_VERSION);
1512-
1513- /* zero everything so no slots are used */
1514- initialize_map_for_new_level();
1515-
1516- /* Calculate the length (for reallocate map) */
1517- allocate_map_structure_for_map(wad);
1518-
1519- /* Extract points */
1520- data= (uint8 *)extract_type_from_wad(wad, POINT_TAG, &data_length);
1521- count= data_length/SIZEOF_world_point2d;
1522- assert(data_length == count*SIZEOF_world_point2d);
1523-
1524- if(count)
1525- {
1526- load_points(data, count);
1527- } else {
1528-
1529- data= (uint8 *)extract_type_from_wad(wad, ENDPOINT_DATA_TAG, &data_length);
1530- count= data_length/SIZEOF_endpoint_data;
1531- assert(data_length == count*SIZEOF_endpoint_data);
1532- // assert(count>=0 && count<MAXIMUM_ENDPOINTS_PER_MAP);
1533-
1534- /* Slam! */
1535- unpack_endpoint_data(data,map_endpoints,count);
1536- assert(count == static_cast<size_t>(static_cast<int16>(count)));
1537- assert(0 <= static_cast<int16>(count));
1538- dynamic_world->endpoint_count= static_cast<int16>(count);
1539-
1540- if (version > MARATHON_ONE_DATA_VERSION)
1541- is_preprocessed_map= true;
1542- }
1543-
1544- /* Extract lines */
1545- data= (uint8 *)extract_type_from_wad(wad, LINE_TAG, &data_length);
1546- count = data_length/SIZEOF_line_data;
1547- assert(data_length == count*SIZEOF_line_data);
1548- load_lines(data, count);
1549-
1550- /* Order is important! */
1551- data= (uint8 *)extract_type_from_wad(wad, SIDE_TAG, &data_length);
1552- count = data_length/SIZEOF_side_data;
1553- assert(data_length == count*SIZEOF_side_data);
1554- load_sides(data, count, version);
1555-
1556- /* Extract polygons */
1557- data= (uint8 *)extract_type_from_wad(wad, POLYGON_TAG, &data_length);
1558- count = data_length/SIZEOF_polygon_data;
1559- assert(data_length == count*SIZEOF_polygon_data);
1560- load_polygons(data, count, version);
1561-
1562- /* Extract the lightsources */
1563- if(restoring_game)
1564- {
1565- // Slurp them in
1566- data= (uint8 *)extract_type_from_wad(wad, LIGHTSOURCE_TAG, &data_length);
1567- count = data_length/SIZEOF_light_data;
1568- assert(data_length == count*SIZEOF_light_data);
1569- LightList.resize(count);
1570- unpack_light_data(data,lights,count);
1571- }
1572- else
1573- {
1574- /* When you are restoring a game, the actual light structure is set. */
1575- data= (uint8 *)extract_type_from_wad(wad, LIGHTSOURCE_TAG, &data_length);
1576- if(version==MARATHON_ONE_DATA_VERSION)
1577- {
1578- /* We have an old style light */
1579- count= data_length/SIZEOF_old_light_data;
1580- assert(count*SIZEOF_old_light_data==data_length);
1581- load_lights(data, count, version);
1582- } else {
1583- count= data_length/SIZEOF_static_light_data;
1584- assert(count*SIZEOF_static_light_data==data_length);
1585- load_lights(data, count, version);
1586- }
1587-
1588- // HACK!!!!!!!!!!!!!!! vulcan doesnÕt NONE .first_object field after adding scenery
1589- {
1590- for (count= 0; count<static_cast<size_t>(dynamic_world->polygon_count); ++count)
1591- {
1592- map_polygons[count].first_object= NONE;
1593- }
1594- }
1595- }
1596-
1597- /* Extract the annotations */
1598- data= (uint8 *)extract_type_from_wad(wad, ANNOTATION_TAG, &data_length);
1599- count = data_length/SIZEOF_map_annotation;
1600- assert(data_length == count*SIZEOF_map_annotation);
1601- load_annotations(data, count);
1602-
1603- /* Extract the objects */
1604- data= (uint8 *)extract_type_from_wad(wad, OBJECT_TAG, &data_length);
1605- count = data_length/SIZEOF_map_object;
1606- assert(data_length == count*static_cast<size_t>(SIZEOF_map_object));
1607- load_objects(data, count);
1608-
1609- /* Extract the map info data */
1610- data= (uint8 *)extract_type_from_wad(wad, MAP_INFO_TAG, &data_length);
1611- // LP change: made this more Pfhorte-friendly
1612- assert(static_cast<size_t>(SIZEOF_static_data)==data_length
1613- || static_cast<size_t>(SIZEOF_static_data-2)==data_length);
1614- load_map_info(data);
1615-
1616- /* Extract the game difficulty info.. */
1617- data= (uint8 *)extract_type_from_wad(wad, ITEM_PLACEMENT_STRUCTURE_TAG, &data_length);
1618- // In case of an absent placement chunk...
1619- if (data_length == 0)
1620- {
1621- data = new uint8[2*MAXIMUM_OBJECT_TYPES*SIZEOF_object_frequency_definition];
1622- memset(data,0,2*MAXIMUM_OBJECT_TYPES*SIZEOF_object_frequency_definition);
1623- }
1624- else
1625- assert(data_length == 2*MAXIMUM_OBJECT_TYPES*SIZEOF_object_frequency_definition);
1626- load_placement_data(data + MAXIMUM_OBJECT_TYPES*SIZEOF_object_frequency_definition, data);
1627- if (data_length == 0)
1628- delete []data;
1629-
1630- /* Extract the terminal data. */
1631- data= (uint8 *)extract_type_from_wad(wad, TERMINAL_DATA_TAG, &data_length);
1632- load_terminal_data(data, data_length);
1633-
1634- /* Extract the media definitions */
1635- if(restoring_game)
1636- {
1637- // Slurp it in
1638- data= (uint8 *)extract_type_from_wad(wad, MEDIA_TAG, &data_length);
1639- count= data_length/SIZEOF_media_data;
1640- assert(count*SIZEOF_media_data==data_length);
1641- MediaList.resize(count);
1642- unpack_media_data(data,medias,count);
1643- }
1644- else
1645- {
1646- data= (uint8 *)extract_type_from_wad(wad, MEDIA_TAG, &data_length);
1647- count= data_length/SIZEOF_media_data;
1648- assert(count*SIZEOF_media_data==data_length);
1649- load_media(data, count);
1650- }
1651-
1652- /* Extract the ambient sound images */
1653- data= (uint8 *)extract_type_from_wad(wad, AMBIENT_SOUND_TAG, &data_length);
1654- count = data_length/SIZEOF_ambient_sound_image_data;
1655- assert(data_length == count*SIZEOF_ambient_sound_image_data);
1656- load_ambient_sound_images(data, count);
1657- load_ambient_sound_images(data, data_length/SIZEOF_ambient_sound_image_data);
1658-
1659- /* Extract the random sound images */
1660- data= (uint8 *)extract_type_from_wad(wad, RANDOM_SOUND_TAG, &data_length);
1661- count = data_length/SIZEOF_random_sound_image_data;
1662- assert(data_length == count*SIZEOF_random_sound_image_data);
1663- load_random_sound_images(data, count);
1664-
1665- /* Extract embedded shapes */
1666- data= (uint8 *)extract_type_from_wad(wad, SHAPE_PATCH_TAG, &data_length);
1667- set_shapes_patch_data(data, data_length);
1668-
1669- /* Extract MMLS */
1670- data= (uint8 *)extract_type_from_wad(wad, MMLS_TAG, &data_length);
1671- SetMMLS(data, data_length);
1672-
1673- /* Extract LUAS */
1674- data= (uint8 *)extract_type_from_wad(wad, LUAS_TAG, &data_length);
1675- SetLUAS(data, data_length);
1676-
1677- /* Extract saved Lua state */
1678- data =(uint8 *)extract_type_from_wad(wad, LUA_STATE_TAG, &data_length);
1679- unpack_lua_states(data, data_length);
1680-
1681- // LP addition: load the physics-model chunks (all fixed-size)
1682- bool PhysicsModelLoaded = false;
1683-
1684- data= (uint8 *)extract_type_from_wad(wad, MONSTER_PHYSICS_TAG, &data_length);
1685- count = data_length/SIZEOF_monster_definition;
1686- assert(count*SIZEOF_monster_definition == data_length);
1687- assert(count <= NUMBER_OF_MONSTER_TYPES);
1688- if (data_length > 0)
1689- {
1690- if (!PhysicsModelLoaded) init_physics_wad_data();
1691- PhysicsModelLoaded = true;
1692- unpack_monster_definition(data,count);
1693- }
1694-
1695- data= (uint8 *)extract_type_from_wad(wad, EFFECTS_PHYSICS_TAG, &data_length);
1696- count = data_length/SIZEOF_effect_definition;
1697- assert(count*SIZEOF_effect_definition == data_length);
1698- assert(count <= NUMBER_OF_EFFECT_TYPES);
1699- if (data_length > 0)
1700- {
1701- if (!PhysicsModelLoaded) init_physics_wad_data();
1702- PhysicsModelLoaded = true;
1703- unpack_effect_definition(data,count);
1704- }
1705-
1706- data= (uint8 *)extract_type_from_wad(wad, PROJECTILE_PHYSICS_TAG, &data_length);
1707- count = data_length/SIZEOF_projectile_definition;
1708- assert(count*SIZEOF_projectile_definition == data_length);
1709- assert(count <= NUMBER_OF_PROJECTILE_TYPES);
1710- if (data_length > 0)
1711- {
1712- if (!PhysicsModelLoaded) init_physics_wad_data();
1713- PhysicsModelLoaded = true;
1714- unpack_projectile_definition(data,count);
1715- }
1716-
1717- data= (uint8 *)extract_type_from_wad(wad, PHYSICS_PHYSICS_TAG, &data_length);
1718- count = data_length/SIZEOF_physics_constants;
1719- assert(count*SIZEOF_physics_constants == data_length);
1720- assert(count <= get_number_of_physics_models());
1721- if (data_length > 0)
1722- {
1723- if (!PhysicsModelLoaded) init_physics_wad_data();
1724- PhysicsModelLoaded = true;
1725- unpack_physics_constants(data,count);
1726- }
1727-
1728- data= (uint8 *)extract_type_from_wad(wad, WEAPONS_PHYSICS_TAG, &data_length);
1729- count = data_length/SIZEOF_weapon_definition;
1730- assert(count*SIZEOF_weapon_definition == data_length);
1731- assert(count <= get_number_of_weapon_types());
1732- if (data_length > 0)
1733- {
1734- if (!PhysicsModelLoaded) init_physics_wad_data();
1735- PhysicsModelLoaded = true;
1736- unpack_weapon_definition(data,count);
1737- }
1738-
1739- // LP addition: Reload the physics model if it had been loaded in the previous level,
1740- // but not in the current level. This avoids the persistent-physics bug.
1741- // ghs: always reload the physics model if there isn't one merged
1742- if (PhysicsModelLoadedEarlier && !PhysicsModelLoaded && !game_is_networked)
1743- import_definition_structures();
1744- PhysicsModelLoadedEarlier = PhysicsModelLoaded;
1745-
1746- /* If we are restoring the game, then we need to add the dynamic data */
1747- if(restoring_game)
1748- {
1749- // Slurp it all in...
1750- data= (uint8 *)extract_type_from_wad(wad, MAP_INDEXES_TAG, &data_length);
1751- count= data_length/sizeof(short);
1752- assert(count*int32(sizeof(short))==data_length);
1753- MapIndexList.resize(count);
1754- StreamToList(data,map_indexes,count);
1755-
1756- data= (uint8 *)extract_type_from_wad(wad, PLAYER_STRUCTURE_TAG, &data_length);
1757- count= data_length/SIZEOF_player_data;
1758- assert(count*SIZEOF_player_data==data_length);
1759- unpack_player_data(data,players,count);
1760- team_damage_from_player_data();
1761-
1762- data= (uint8 *)extract_type_from_wad(wad, DYNAMIC_STRUCTURE_TAG, &data_length);
1763- assert(data_length == SIZEOF_dynamic_data);
1764- unpack_dynamic_data(data,dynamic_world,1);
1765-
1766- data= (uint8 *)extract_type_from_wad(wad, OBJECT_STRUCTURE_TAG, &data_length);
1767- count= data_length/SIZEOF_object_data;
1768- assert(count*SIZEOF_object_data==data_length);
1769- vassert(count <= MAXIMUM_OBJECTS_PER_MAP,
1770- csprintf(temporary,"Number of map objects %lu > limit %u",count,MAXIMUM_OBJECTS_PER_MAP));
1771- unpack_object_data(data,objects,count);
1772-
1773- // Unpacking is E-Z here...
1774- data= (uint8 *)extract_type_from_wad(wad, AUTOMAP_LINES, &data_length);
1775- memcpy(automap_lines,data,data_length);
1776- data= (uint8 *)extract_type_from_wad(wad, AUTOMAP_POLYGONS, &data_length);
1777- memcpy(automap_polygons,data,data_length);
1778-
1779- data= (uint8 *)extract_type_from_wad(wad, MONSTERS_STRUCTURE_TAG, &data_length);
1780- count= data_length/SIZEOF_monster_data;
1781- assert(count*SIZEOF_monster_data==data_length);
1782- vassert(count <= MAXIMUM_MONSTERS_PER_MAP,
1783- csprintf(temporary,"Number of monsters %lu > limit %u",count,MAXIMUM_MONSTERS_PER_MAP));
1784- unpack_monster_data(data,monsters,count);
1785-
1786- data= (uint8 *)extract_type_from_wad(wad, EFFECTS_STRUCTURE_TAG, &data_length);
1787- count= data_length/SIZEOF_effect_data;
1788- assert(count*SIZEOF_effect_data==data_length);
1789- vassert(count <= MAXIMUM_EFFECTS_PER_MAP,
1790- csprintf(temporary,"Number of effects %lu > limit %u",count,MAXIMUM_EFFECTS_PER_MAP));
1791- unpack_effect_data(data,effects,count);
1792-
1793- data= (uint8 *)extract_type_from_wad(wad, PROJECTILES_STRUCTURE_TAG, &data_length);
1794- count= data_length/SIZEOF_projectile_data;
1795- assert(count*SIZEOF_projectile_data==data_length);
1796- vassert(count <= MAXIMUM_PROJECTILES_PER_MAP,
1797- csprintf(temporary,"Number of projectiles %lu > limit %u",count,MAXIMUM_PROJECTILES_PER_MAP));
1798- unpack_projectile_data(data,projectiles,count);
1799-
1800- data= (uint8 *)extract_type_from_wad(wad, PLATFORM_STRUCTURE_TAG, &data_length);
1801- count= data_length/SIZEOF_platform_data;
1802- assert(count*SIZEOF_platform_data==data_length);
1803- PlatformList.resize(count);
1804- unpack_platform_data(data,platforms,count);
1805-
1806- data= (uint8 *)extract_type_from_wad(wad, WEAPON_STATE_TAG, &data_length);
1807- count= data_length/SIZEOF_player_weapon_data;
1808- assert(count*SIZEOF_player_weapon_data==data_length);
1809- unpack_player_weapon_data(data,count);
1810-
1811- data= (uint8 *)extract_type_from_wad(wad, TERMINAL_STATE_TAG, &data_length);
1812- count= data_length/SIZEOF_player_terminal_data;
1813- assert(count*SIZEOF_player_terminal_data==data_length);
1814- unpack_player_terminal_data(data,count);
1815-
1816- complete_restoring_level(wad);
1817- } else {
1818- uint8 *map_index_data;
1819- size_t map_index_count;
1820- uint8 *platform_structures;
1821- size_t platform_structure_count;
1822-
1823- if(version==MARATHON_ONE_DATA_VERSION)
1824- {
1825- /* Force precalculation */
1826- map_index_data= NULL;
1827- map_index_count= 0;
1828- } else {
1829- map_index_data= (uint8 *)extract_type_from_wad(wad, MAP_INDEXES_TAG, &data_length);
1830- map_index_count= data_length/sizeof(short);
1831- assert(map_index_count*sizeof(short)==data_length);
1832- }
1833-
1834- assert(is_preprocessed_map&&map_index_count || !is_preprocessed_map&&!map_index_count);
1835-
1836- data= (uint8 *)extract_type_from_wad(wad, PLATFORM_STATIC_DATA_TAG, &data_length);
1837- count= data_length/SIZEOF_static_platform_data;
1838- assert(count*SIZEOF_static_platform_data==data_length);
1839-
1840- platform_structures= (uint8 *)extract_type_from_wad(wad, PLATFORM_STRUCTURE_TAG, &data_length);
1841- platform_structure_count= data_length/SIZEOF_platform_data;
1842- assert(platform_structure_count*SIZEOF_platform_data==data_length);
1843-
1844- complete_loading_level((short *) map_index_data, map_index_count,
1845- data, count, platform_structures,
1846- platform_structure_count, version);
1847-
1848- }
1849-
1850- /* ... and bail */
1851- return true;
1852-}
1853-
1854-static void allocate_map_structure_for_map(
1855- struct wad_data *wad)
1856-{
1857- size_t data_length;
1858- size_t line_count, polygon_count, side_count, endpoint_count;
1859-
1860- /* Extract points */
1861- extract_type_from_wad(wad, POINT_TAG, &data_length);
1862- endpoint_count= data_length/SIZEOF_world_point2d;
1863- if(endpoint_count*SIZEOF_world_point2d!=data_length) alert_user(fatalError, strERRORS, corruptedMap, 0x7074); // 'pt'
1864-
1865- if(!endpoint_count)
1866- {
1867- extract_type_from_wad(wad, ENDPOINT_DATA_TAG, &data_length);
1868- endpoint_count= data_length/SIZEOF_endpoint_data;
1869- if(endpoint_count*SIZEOF_endpoint_data!=data_length) alert_user(fatalError, strERRORS, corruptedMap, 0x6570); // 'ep'
1870- }
1871-
1872- /* Extract lines */
1873- extract_type_from_wad(wad, LINE_TAG, &data_length);
1874- line_count= data_length/SIZEOF_line_data;
1875- if(line_count*SIZEOF_line_data!=data_length) alert_user(fatalError, strERRORS, corruptedMap, 0x6c69); // 'li'
1876-
1877- /* Sides.. */
1878- extract_type_from_wad(wad, SIDE_TAG, &data_length);
1879- side_count= data_length/SIZEOF_side_data;
1880- if(side_count*SIZEOF_side_data!=data_length) alert_user(fatalError, strERRORS, corruptedMap, 0x7369); // 'si'
1881-
1882- /* Extract polygons */
1883- extract_type_from_wad(wad, POLYGON_TAG, &data_length);
1884- polygon_count= data_length/SIZEOF_polygon_data;
1885- if(polygon_count*SIZEOF_polygon_data!=data_length) alert_user(fatalError, strERRORS, corruptedMap, 0x7369); // 'si'
1886-
1887- allocate_map_for_counts(polygon_count, side_count, endpoint_count, line_count);
1888-}
1889-
1890-/* Note that we assume the redundant data has already been recalculated... */
1891-static void load_redundant_map_data(
1892- short *redundant_data,
1893- size_t count)
1894-{
1895- if (redundant_data)
1896- {
1897- // assert(redundant_data && map_indexes);
1898- uint8 *Stream = (uint8 *)redundant_data;
1899- MapIndexList.resize(count);
1900- StreamToList(Stream,map_indexes,count);
1901- assert(count == static_cast<size_t>(static_cast<int16>(count)));
1902- assert(0 <= static_cast<int16>(count));
1903- dynamic_world->map_index_count= static_cast<int16>(count);
1904- }
1905- else
1906- {
1907- recalculate_redundant_map();
1908- precalculate_map_indexes();
1909- }
1910-}
1911-
1912-void load_terminal_data(
1913- uint8 *data,
1914- size_t length)
1915-{
1916- /* I would really like it if I could get these into computer_interface.c statically */
1917- unpack_map_terminal_data(data,length);
1918-}
1919-
1920-static void scan_and_add_scenery(
1921- void)
1922-{
1923- short ii;
1924- struct map_object *saved_object;
1925-
1926- saved_object= saved_objects;
1927- for(ii=0; ii<dynamic_world->initial_objects_count; ++ii)
1928- {
1929- if (saved_object->type==_saved_object)
1930- {
1931- struct object_location location;
1932-
1933- location.p= saved_object->location;
1934- location.flags= saved_object->flags;
1935- location.yaw= saved_object->facing;
1936- location.polygon_index= saved_object->polygon_index;
1937- new_scenery(&location, saved_object->index);
1938- }
1939-
1940- ++saved_object;
1941- }
1942-}
1943-
1944-struct save_game_data
1945-{
1946- uint32 tag;
1947- short unit_size;
1948- bool loaded_by_level;
1949-};
1950-
1951-#define NUMBER_OF_EXPORT_ARRAYS (sizeof(export_data)/sizeof(struct save_game_data))
1952-save_game_data export_data[]=
1953-{
1954- { POINT_TAG, SIZEOF_world_point2d, true },
1955- { LINE_TAG, SIZEOF_line_data, true },
1956- { POLYGON_TAG, SIZEOF_polygon_data, true },
1957- { SIDE_TAG, SIZEOF_side_data, true },
1958- { LIGHTSOURCE_TAG, SIZEOF_static_light_data, true, },
1959- { ANNOTATION_TAG, SIZEOF_map_annotation, true },
1960- { OBJECT_TAG, SIZEOF_map_object, true },
1961- { MAP_INFO_TAG, SIZEOF_static_data, true },
1962- { ITEM_PLACEMENT_STRUCTURE_TAG, SIZEOF_object_frequency_definition, true },
1963- { PLATFORM_STATIC_DATA_TAG, SIZEOF_static_platform_data, true },
1964- { TERMINAL_DATA_TAG, sizeof(byte), true },
1965- { MEDIA_TAG, SIZEOF_media_data, true }, // false },
1966- { AMBIENT_SOUND_TAG, SIZEOF_ambient_sound_image_data, true },
1967- { RANDOM_SOUND_TAG, SIZEOF_random_sound_image_data, true },
1968- { SHAPE_PATCH_TAG, sizeof(byte), true },
1969-// { PLATFORM_STRUCTURE_TAG, SIZEOF_platform_data, true },
1970-};
1971-
1972-#define NUMBER_OF_SAVE_ARRAYS (sizeof(save_data)/sizeof(struct save_game_data))
1973-struct save_game_data save_data[]=
1974-{
1975- { ENDPOINT_DATA_TAG, SIZEOF_endpoint_data, true },
1976- { LINE_TAG, SIZEOF_line_data, true },
1977- { SIDE_TAG, SIZEOF_side_data, true },
1978- { POLYGON_TAG, SIZEOF_polygon_data, true },
1979- { LIGHTSOURCE_TAG, SIZEOF_light_data, true }, // false },
1980- { ANNOTATION_TAG, SIZEOF_map_annotation, true },
1981- { OBJECT_TAG, SIZEOF_map_object, true },
1982- { MAP_INFO_TAG, SIZEOF_static_data, true },
1983- { ITEM_PLACEMENT_STRUCTURE_TAG, SIZEOF_object_frequency_definition, true },
1984- { MEDIA_TAG, SIZEOF_media_data, true }, // false },
1985- { AMBIENT_SOUND_TAG, SIZEOF_ambient_sound_image_data, true },
1986- { RANDOM_SOUND_TAG, SIZEOF_random_sound_image_data, true },
1987- { TERMINAL_DATA_TAG, sizeof(byte), true },
1988-
1989- // LP addition: handling of physics models
1990- { MONSTER_PHYSICS_TAG, SIZEOF_monster_definition, true},
1991- { EFFECTS_PHYSICS_TAG, SIZEOF_effect_definition, true},
1992- { PROJECTILE_PHYSICS_TAG, SIZEOF_projectile_definition, true},
1993- { PHYSICS_PHYSICS_TAG, SIZEOF_physics_constants, true},
1994- { WEAPONS_PHYSICS_TAG, SIZEOF_weapon_definition, true},
1995-
1996- // GHS: save the new embedded shapes
1997- { SHAPE_PATCH_TAG, sizeof(byte), true },
1998-
1999- { MMLS_TAG, sizeof(byte), true },
2000- { LUAS_TAG, sizeof(byte), true },
2001-
2002- { MAP_INDEXES_TAG, sizeof(short), true }, // false },
2003- { PLAYER_STRUCTURE_TAG, SIZEOF_player_data, true }, // false },
2004- { DYNAMIC_STRUCTURE_TAG, SIZEOF_dynamic_data, true }, // false },
2005- { OBJECT_STRUCTURE_TAG, SIZEOF_object_data, true }, // false },
2006- { AUTOMAP_LINES, sizeof(byte), true }, // false },
2007- { AUTOMAP_POLYGONS, sizeof(byte), true }, // false },
2008- { MONSTERS_STRUCTURE_TAG, SIZEOF_monster_data, true }, // false },
2009- { EFFECTS_STRUCTURE_TAG, SIZEOF_effect_data, true }, // false },
2010- { PROJECTILES_STRUCTURE_TAG, SIZEOF_projectile_data, true }, // false },
2011- { PLATFORM_STRUCTURE_TAG, SIZEOF_platform_data, true }, // false },
2012- { WEAPON_STATE_TAG, SIZEOF_player_weapon_data, true }, // false },
2013- { TERMINAL_STATE_TAG, SIZEOF_player_terminal_data, true }, // false }
2014-
2015- { LUA_STATE_TAG, sizeof(byte), true },
2016-};
2017-
2018-static uint8 *export_tag_to_global_array_and_size(
2019- uint32 tag,
2020- size_t *size
2021- )
2022-{
2023- uint8 *array = NULL;
2024- size_t unit_size = 0;
2025- size_t count = 0;
2026- unsigned index;
2027-
2028- for (index=0; index<NUMBER_OF_EXPORT_ARRAYS; ++index)
2029- {
2030- if(export_data[index].tag==tag)
2031- {
2032- unit_size= export_data[index].unit_size;
2033- break;
2034- }
2035- }
2036- assert(index != NUMBER_OF_EXPORT_ARRAYS);
2037-
2038- switch (tag)
2039- {
2040- case POINT_TAG:
2041- count = dynamic_world->endpoint_count;
2042- break;
2043-
2044- case LIGHTSOURCE_TAG:
2045- count = dynamic_world->light_count;
2046- break;
2047-
2048- case PLATFORM_STATIC_DATA_TAG:
2049- count = dynamic_world->platform_count;
2050- break;
2051-
2052- case POLYGON_TAG:
2053- count = dynamic_world->polygon_count;
2054- break;
2055-
2056- default:
2057- assert(false);
2058- break;
2059- }
2060-
2061- // Allocate a temporary packed-data chunk;
2062- // indicate if there is nothing to be written
2063- *size= count*unit_size;
2064- if (*size > 0)
2065- array = new byte[*size];
2066- else
2067- return NULL;
2068-
2069- objlist_clear(array, *size);
2070-
2071- // An OK-to-alter version of that array pointer
2072- uint8 *temp_array = array;
2073-
2074- switch (tag)
2075- {
2076- case POINT_TAG:
2077- for (size_t loop = 0; loop < count; ++loop)
2078- {
2079- world_point2d& vertex = map_endpoints[loop].vertex;
2080- ValueToStream(temp_array, vertex.x);
2081- ValueToStream(temp_array, vertex.y);
2082- }
2083- break;
2084-
2085- case LIGHTSOURCE_TAG:
2086- for (size_t loop = 0; loop < count; ++loop)
2087- {
2088- temp_array = pack_static_light_data(temp_array, &lights[loop].static_data, 1);
2089- }
2090- break;
2091-
2092- case PLATFORM_STATIC_DATA_TAG:
2093- if (static_platforms.size() == count)
2094- {
2095- // export them directly as they came in
2096- pack_static_platform_data(array, &static_platforms[0], count);
2097- }
2098- else
2099- {
2100- for (size_t loop = 0; loop < count; ++loop)
2101- {
2102- // ghs: this belongs somewhere else
2103- static_platform_data platform;
2104- obj_clear(platform);
2105- platform.type = platforms[loop].type;
2106- platform.speed = platforms[loop].speed;
2107- platform.delay = platforms[loop].delay;
2108- if (PLATFORM_GOES_BOTH_WAYS(&platforms[loop]))
2109- {
2110- platform.maximum_height = platforms[loop].maximum_ceiling_height;
2111- platform.minimum_height = platforms[loop].minimum_floor_height;
2112- }
2113- else if (PLATFORM_COMES_FROM_FLOOR(&platforms[loop]))
2114- {
2115- platform.maximum_height = platforms[loop].maximum_floor_height;
2116- platform.minimum_height = platforms[loop].minimum_floor_height;
2117- }
2118- else
2119- {
2120- platform.maximum_height = platforms[loop].maximum_ceiling_height;
2121- platform.minimum_height = platforms[loop].minimum_floor_height;
2122- }
2123- platform.static_flags = platforms[loop].static_flags;
2124- platform.polygon_index = platforms[loop].polygon_index;
2125- platform.tag = platforms[loop].tag;
2126-
2127- temp_array = pack_static_platform_data(temp_array, &platform, 1);
2128- }
2129- }
2130- break;
2131-
2132- case POLYGON_TAG:
2133- for (size_t loop = 0; loop < count; ++loop)
2134- {
2135- // Forge visual mode crashes if we don't do this
2136- polygon_data polygon = PolygonList[loop];
2137- polygon.first_object = NONE;
2138- temp_array = pack_polygon_data(temp_array, &polygon, 1);
2139- }
2140- break;
2141-
2142- default:
2143- assert(false);
2144- break;
2145- }
2146-
2147- return array;
2148-}
2149-
2150-extern size_t save_lua_states();
2151-extern void pack_lua_states(uint8*, size_t);
2152-
2153-
2154-/* the sizes are the sizes to save in the file, be aware! */
2155-static uint8 *tag_to_global_array_and_size(
2156- uint32 tag,
2157- size_t *size
2158- )
2159-{
2160- uint8 *array= NULL;
2161- size_t unit_size = 0;
2162- size_t count = 0;
2163- unsigned index;
2164-
2165- for (index=0; index<NUMBER_OF_SAVE_ARRAYS; ++index)
2166- {
2167- if(save_data[index].tag==tag)
2168- {
2169- unit_size= save_data[index].unit_size;
2170- break;
2171- }
2172- }
2173- assert(index != NUMBER_OF_SAVE_ARRAYS);
2174-
2175- // LP: had fixed off-by-one error in medias saving,
2176- // and had added physics-model saving
2177-
2178- switch (tag)
2179- {
2180- case ENDPOINT_DATA_TAG:
2181- count= dynamic_world->endpoint_count;
2182- break;
2183- case LINE_TAG:
2184- count= dynamic_world->line_count;
2185- break;
2186- case SIDE_TAG:
2187- count= dynamic_world->side_count;
2188- break;
2189- case POLYGON_TAG:
2190- count= dynamic_world->polygon_count;
2191- break;
2192- case LIGHTSOURCE_TAG:
2193- count= dynamic_world->light_count;
2194- break;
2195- case ANNOTATION_TAG:
2196- count= dynamic_world->default_annotation_count;
2197- break;
2198- case OBJECT_TAG:
2199- count= dynamic_world->initial_objects_count;
2200- break;
2201- case MAP_INFO_TAG:
2202- count= 1;
2203- break;
2204- case PLAYER_STRUCTURE_TAG:
2205- count= dynamic_world->player_count;
2206- break;
2207- case DYNAMIC_STRUCTURE_TAG:
2208- count= 1;
2209- break;
2210- case OBJECT_STRUCTURE_TAG:
2211- count= dynamic_world->object_count;
2212- break;
2213- case MAP_INDEXES_TAG:
2214- count= static_cast<unsigned short>(dynamic_world->map_index_count);
2215- break;
2216- case AUTOMAP_LINES:
2217- count= (dynamic_world->line_count/8+((dynamic_world->line_count%8)?1:0));
2218- break;
2219- case AUTOMAP_POLYGONS:
2220- count= (dynamic_world->polygon_count/8+((dynamic_world->polygon_count%8)?1:0));
2221- break;
2222- case MONSTERS_STRUCTURE_TAG:
2223- count= dynamic_world->monster_count;
2224- break;
2225- case EFFECTS_STRUCTURE_TAG:
2226- count= dynamic_world->effect_count;
2227- break;
2228- case PROJECTILES_STRUCTURE_TAG:
2229- count= dynamic_world->projectile_count;
2230- break;
2231- case MEDIA_TAG:
2232- count= count_number_of_medias_used();
2233- break;
2234- case ITEM_PLACEMENT_STRUCTURE_TAG:
2235- count= 2*MAXIMUM_OBJECT_TYPES;
2236- break;
2237- case PLATFORM_STRUCTURE_TAG:
2238- count= dynamic_world->platform_count;
2239- break;
2240- case AMBIENT_SOUND_TAG:
2241- count= dynamic_world->ambient_sound_image_count;
2242- break;
2243- case RANDOM_SOUND_TAG:
2244- count= dynamic_world->random_sound_image_count;
2245- break;
2246- case TERMINAL_DATA_TAG:
2247- count= calculate_packed_terminal_data_length();
2248- break;
2249- case WEAPON_STATE_TAG:
2250- count= dynamic_world->player_count;
2251- break;
2252- case TERMINAL_STATE_TAG:
2253- count= dynamic_world->player_count;
2254- break;
2255- case MONSTER_PHYSICS_TAG:
2256- count= NUMBER_OF_MONSTER_TYPES;
2257- break;
2258- case EFFECTS_PHYSICS_TAG:
2259- count= NUMBER_OF_EFFECT_TYPES;
2260- break;
2261- case PROJECTILE_PHYSICS_TAG:
2262- count= NUMBER_OF_PROJECTILE_TYPES;
2263- break;
2264- case PHYSICS_PHYSICS_TAG:
2265- count= get_number_of_physics_models();
2266- break;
2267- case WEAPONS_PHYSICS_TAG:
2268- count= get_number_of_weapon_types();
2269- break;
2270- case SHAPE_PATCH_TAG:
2271- get_shapes_patch_data(count);
2272- break;
2273- case MMLS_TAG:
2274- GetMMLS(count);
2275- break;
2276- case LUAS_TAG:
2277- GetLUAS(count);
2278- break;
2279- case LUA_STATE_TAG:
2280- count= save_lua_states();
2281- break;
2282- default:
2283- assert(false);
2284- break;
2285- }
2286-
2287- // Allocate a temporary packed-data chunk;
2288- // indicate if there is nothing to be written
2289- *size= count*unit_size;
2290- if (*size > 0)
2291- array = new byte[*size];
2292- else
2293- return NULL;
2294-
2295- objlist_clear(array, *size);
2296-
2297- // An OK-to-alter version of that array pointer
2298- uint8 *temp_array = array;
2299-
2300- switch (tag)
2301- {
2302- case ENDPOINT_DATA_TAG:
2303- pack_endpoint_data(array,map_endpoints,count);
2304- break;
2305- case LINE_TAG:
2306- pack_line_data(array,map_lines,count);
2307- break;
2308- case SIDE_TAG:
2309- pack_side_data(array,map_sides,count);
2310- break;
2311- case POLYGON_TAG:
2312- pack_polygon_data(array,map_polygons,count);
2313- break;
2314- case LIGHTSOURCE_TAG:
2315- pack_light_data(array,lights,count);
2316- break;
2317- case ANNOTATION_TAG:
2318- pack_map_annotation(array,map_annotations,count);
2319- break;
2320- case OBJECT_TAG:
2321- pack_map_object(array,saved_objects,count);
2322- break;
2323- case MAP_INFO_TAG:
2324- pack_static_data(array,static_world,count);
2325- break;
2326- case PLAYER_STRUCTURE_TAG:
2327- pack_player_data(array,players,count);
2328- break;
2329- case DYNAMIC_STRUCTURE_TAG:
2330- pack_dynamic_data(array,dynamic_world,count);
2331- break;
2332- case OBJECT_STRUCTURE_TAG:
2333- pack_object_data(array,objects,count);
2334- break;
2335- case MAP_INDEXES_TAG:
2336- ListToStream(temp_array,map_indexes,count); // E-Z packing here...
2337- break;
2338- case AUTOMAP_LINES:
2339- memcpy(array,automap_lines,*size);
2340- break;
2341- case AUTOMAP_POLYGONS:
2342- memcpy(array,automap_polygons,*size);
2343- break;
2344- case MONSTERS_STRUCTURE_TAG:
2345- pack_monster_data(array,monsters,count);
2346- break;
2347- case EFFECTS_STRUCTURE_TAG:
2348- pack_effect_data(array,effects,count);
2349- break;
2350- case PROJECTILES_STRUCTURE_TAG:
2351- pack_projectile_data(array,projectiles,count);
2352- break;
2353- case MEDIA_TAG:
2354- pack_media_data(array,medias,count);
2355- break;
2356- case ITEM_PLACEMENT_STRUCTURE_TAG:
2357- pack_object_frequency_definition(array,get_placement_info(),count);
2358- break;
2359- case PLATFORM_STRUCTURE_TAG:
2360- pack_platform_data(array,platforms,count);
2361- break;
2362- case AMBIENT_SOUND_TAG:
2363- pack_ambient_sound_image_data(array,ambient_sound_images,count);
2364- break;
2365- case RANDOM_SOUND_TAG:
2366- pack_random_sound_image_data(array,random_sound_images,count);
2367- break;
2368- case TERMINAL_DATA_TAG:
2369- pack_map_terminal_data(array,count);
2370- break;
2371- case WEAPON_STATE_TAG:
2372- pack_player_weapon_data(array,count);
2373- break;
2374- case TERMINAL_STATE_TAG:
2375- pack_player_terminal_data(array,count);
2376- break;
2377- case MONSTER_PHYSICS_TAG:
2378- pack_monster_definition(array,count);
2379- break;
2380- case EFFECTS_PHYSICS_TAG:
2381- pack_effect_definition(array,count);
2382- break;
2383- case PROJECTILE_PHYSICS_TAG:
2384- pack_projectile_definition(array,count);
2385- break;
2386- case PHYSICS_PHYSICS_TAG:
2387- pack_physics_constants(array,count);
2388- break;
2389- case WEAPONS_PHYSICS_TAG:
2390- pack_weapon_definition(array,count);
2391- break;
2392- case SHAPE_PATCH_TAG:
2393- memcpy(array, get_shapes_patch_data(count), count);
2394- break;
2395- case MMLS_TAG:
2396- memcpy(array, GetMMLS(count), count);
2397- break;
2398- case LUAS_TAG:
2399- memcpy(array, GetLUAS(count), count);
2400- break;
2401- case LUA_STATE_TAG:
2402- pack_lua_states(array, count);
2403- break;
2404- default:
2405- assert(false);
2406- break;
2407- }
2408-
2409- return array;
2410-}
2411-
2412-static wad_data *build_export_wad(wad_header *header, int32 *length)
2413-{
2414- struct wad_data *wad= NULL;
2415- uint8 *array_to_slam;
2416- size_t size;
2417-
2418- wad= create_empty_wad();
2419- if(wad)
2420- {
2421- recalculate_map_counts();
2422-
2423- // try to divine initial platform/polygon states
2424- vector<platform_data> SavedPlatforms = PlatformList;
2425- vector<polygon_data> SavedPolygons = PolygonList;
2426- vector<line_data> SavedLines = LineList;
2427-
2428- for (size_t loop = 0; loop < PlatformList.size(); ++loop)
2429- {
2430- platform_data *platform = &PlatformList[loop];
2431- // reset the polygon heights
2432- if (PLATFORM_COMES_FROM_FLOOR(platform))
2433- {
2434- platform->floor_height = platform->minimum_floor_height;
2435- PolygonList[platform->polygon_index].floor_height = platform->floor_height;
2436- }
2437- if (PLATFORM_COMES_FROM_CEILING(platform))
2438- {
2439- platform->ceiling_height = platform->maximum_ceiling_height;
2440- PolygonList[platform->polygon_index].ceiling_height = platform->ceiling_height;
2441- }
2442- }
2443-
2444- for (size_t loop = 0; loop < LineList.size(); ++loop)
2445- {
2446- line_data *line = &LineList[loop];
2447- if (LINE_IS_VARIABLE_ELEVATION(line))
2448- {
2449- SET_LINE_VARIABLE_ELEVATION(line, false);
2450- SET_LINE_SOLIDITY(line, false);
2451- SET_LINE_TRANSPARENCY(line, true);
2452- }
2453- }
2454-
2455- for(unsigned loop= 0; loop<NUMBER_OF_EXPORT_ARRAYS; ++loop)
2456- {
2457- /* If there is a conversion function, let it handle it */
2458- switch (export_data[loop].tag)
2459- {
2460- case POINT_TAG:
2461- case LIGHTSOURCE_TAG:
2462- case PLATFORM_STATIC_DATA_TAG:
2463- case POLYGON_TAG:
2464- array_to_slam= export_tag_to_global_array_and_size(export_data[loop].tag, &size);
2465- break;
2466- default:
2467- array_to_slam= tag_to_global_array_and_size(export_data[loop].tag, &size);
2468- }
2469-
2470- /* Add it to the wad.. */
2471- if(size)
2472- {
2473- wad= append_data_to_wad(wad, export_data[loop].tag, array_to_slam, size, 0);
2474- delete []array_to_slam;
2475- }
2476- }
2477-
2478- PlatformList = SavedPlatforms;
2479- PolygonList = SavedPolygons;
2480- LineList = SavedLines;
2481-
2482- if(wad) *length= calculate_wad_length(header, wad);
2483- }
2484-
2485- return wad;
2486-}
2487-
2488-/* Build the wad, with all the crap */
2489-static struct wad_data *build_save_game_wad(
2490- struct wad_header *header,
2491- int32 *length)
2492-{
2493- struct wad_data *wad= NULL;
2494- uint8 *array_to_slam;
2495- size_t size;
2496-
2497- wad= create_empty_wad();
2498- if(wad)
2499- {
2500- recalculate_map_counts();
2501- for(unsigned loop= 0; loop<NUMBER_OF_SAVE_ARRAYS; ++loop)
2502- {
2503- /* If there is a conversion function, let it handle it */
2504- array_to_slam= tag_to_global_array_and_size(save_data[loop].tag, &size);
2505-
2506- /* Add it to the wad.. */
2507- if(size)
2508- {
2509- wad= append_data_to_wad(wad, save_data[loop].tag, array_to_slam, size, 0);
2510- delete []array_to_slam;
2511- }
2512- }
2513- if(wad) *length= calculate_wad_length(header, wad);
2514- }
2515-
2516- return wad;
2517-}
2518-
2519-/* Load and slam all of the arrays */
2520-static void complete_restoring_level(
2521- struct wad_data *wad)
2522-{
2523- ok_to_reset_scenery_solidity = false;
2524- /* Loading games needs this done. */
2525- reset_action_queues();
2526-}
2527-
2528-
2529-/* CP Addition: get_map_file returns a pointer to the current map file */
2530-FileSpecifier& get_map_file()
2531-{
2532- return MapFileSpec;
2533-}
2534-
2535-void level_has_embedded_physics_lua(int Level, bool& HasPhysics, bool& HasLua)
2536-{
2537- // load the wad file and look for chunks !!??
2538- wad_header header;
2539- wad_data* wad;
2540- OpenedFile MapFile;
2541- if (open_wad_file_for_reading(get_map_file(), MapFile))
2542- {
2543- if (read_wad_header(MapFile, &header))
2544- {
2545- wad = read_indexed_wad_from_file(MapFile, &header, Level, true);
2546- if (wad)
2547- {
2548- size_t data_length;
2549- extract_type_from_wad(wad, PHYSICS_PHYSICS_TAG, &data_length);
2550- HasPhysics = data_length > 0;
2551-
2552- extract_type_from_wad(wad, LUAS_TAG, &data_length);
2553- HasLua = data_length > 0;
2554- free_wad(wad);
2555- }
2556- }
2557- close_wad_file(MapFile);
2558- }
2559-}
2560-
2561-
2562-/*
2563- * Unpacking/packing functions
2564- */
2565-
2566-static uint8 *unpack_directory_data(uint8 *Stream, directory_data *Objects, size_t Count)
2567-{
2568- uint8* S = Stream;
2569- directory_data* ObjPtr = Objects;
2570-
2571- for (size_t k = 0; k < Count; k++, ObjPtr++)
2572- {
2573- StreamToValue(S,ObjPtr->mission_flags);
2574- StreamToValue(S,ObjPtr->environment_flags);
2575- StreamToValue(S,ObjPtr->entry_point_flags);
2576- StreamToBytes(S,ObjPtr->level_name,LEVEL_NAME_LENGTH);
2577- }
2578-
2579- assert((S - Stream) == SIZEOF_directory_data);
2580- return S;
2581-}
2582-
2583-// ZZZ: gnu cc swears this is currently unused, and I don't see any sneaky #includes that might need it...
2584-/*
2585-static uint8 *pack_directory_data(uint8 *Stream, directory_data *Objects, int Count)
2586-{
2587- uint8* S = Stream;
2588- directory_data* ObjPtr = Objects;
2589-
2590- for (int k = 0; k < Count; k++, ObjPtr++)
2591- {
2592- ValueToStream(S,ObjPtr->mission_flags);
2593- ValueToStream(S,ObjPtr->environment_flags);
2594- ValueToStream(S,ObjPtr->entry_point_flags);
2595- BytesToStream(S,ObjPtr->level_name,LEVEL_NAME_LENGTH);
2596- }
2597-
2598- assert((S - Stream) == SIZEOF_directory_data);
2599- return S;
2600-}
2601-*/
1+/*
2+GAME_WAD.C
3+
4+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5+ and the "Aleph One" developers.
6+
7+ This program is free software; you can redistribute it and/or modify
8+ it under the terms of the GNU General Public License as published by
9+ the Free Software Foundation; either version 3 of the License, or
10+ (at your option) any later version.
11+
12+ This program is distributed in the hope that it will be useful,
13+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ GNU General Public License for more details.
16+
17+ This license is contained in the file "COPYING",
18+ which is included with this source code; it is available online at
19+ http://www.gnu.org/licenses/gpl.html
20+
21+Sunday, July 3, 1994 10:45:17 PM
22+
23+Routines for loading an entire game.
24+
25+Sunday, September 25, 1994 5:03:54 PM (alain)
26+ call recalculate_redundant_endpoint_data() upon restoring saved game since
27+ the redundant data isn't saved.
28+Sunday, November 6, 1994 5:35:34 PM
29+ added support for the unified platforms/doors, cleaned up some old code of mine...
30+Saturday, August 26, 1995 2:28:56 PM
31+ made portable.
32+
33+Jan 30, 2000 (Loren Petrich):
34+ Added some typecasts
35+ Removed some "static" declarations that conflict with "extern"
36+
37+Feb 4, 2000 (Loren Petrich):
38+ Changed halt() to assert(false) for better debugging
39+
40+Feb 6, 2000 (Loren Petrich):
41+ Added loading and saving of physics models in savegames and from map files
42+
43+Feb 12, 2000 (Loren Petrich):
44+ Added MARATHON_INFINITY_DATA_VERSION where appropriate
45+
46+Feb 14, 2000 (Loren Petrich):
47+ Added more Pfhorte-friendly error checking to reading in of
48+ map-info ('Minf') chunk; allowing it to be 2 bytes shorter.
49+
50+Feb 17, 2000 (Loren Petrich):
51+ Hides cursor after warning user about loading non-Bungie map files
52+ (strERRORS, warningExternalMapsFile)
53+
54+Feb 19, 2000 (Loren Petrich):
55+ Fixed off-by-one asserts in load_***() routines;
56+
57+Feb 26, 2000 (Loren Petrich):
58+ Added chase-cam initialization
59+
60+June 15, 2000 (Loren Petrich):
61+ Added supprt for Chris Pruett's Pfhortran
62+
63+Aug 12, 2000 (Loren Petrich):
64+ Using object-oriented file handler
65+
66+Aug 25, 2000 (Loren Petrich):
67+ Cleared errors (game_errors.c/h) produced by Pfhortran
68+ and by checking on a scenario's image files
69+
70+Aug 28, 2000 (Loren Petrich):
71+ Started on using new pack/unpack routines
72+
73+Nov 26, 2000 (Loren Petrich):
74+ Movied a RunLevelScript() before some other stuff, such as entering_map(),
75+ so that textures to be loaded can be specified before they actually get loaded.
76+
77+Feb 15, 2002 (Br'fin (Jeremy Parsons)):
78+ Additional save data is now applied to the Temporary file instead of the original
79+ (Old level preview info is now saved under Macintosh again)
80+*/
81+
82+// This needs to do the right thing on save game, which is storing the precalculated crap.
83+
84+#include "cseries.h"
85+
86+#include <string.h>
87+#include <stdlib.h>
88+
89+#include "map.h"
90+#include "monsters.h"
91+#include "network.h"
92+#include "projectiles.h"
93+#include "effects.h"
94+#include "player.h"
95+#include "platforms.h"
96+#include "flood_map.h"
97+#include "scenery.h"
98+#include "lightsource.h"
99+#include "media.h"
100+#include "weapons.h"
101+#include "shell.h"
102+#include "preferences.h"
103+#include "FileHandler.h"
104+
105+#include "editor.h"
106+#include "tags.h"
107+#include "wad.h"
108+#include "game_wad.h"
109+#include "interface.h"
110+#include "game_window.h"
111+#include "game_errors.h"
112+#include "computer_interface.h" // for loading/saving terminal state.
113+#include "images.h"
114+#include "shell.h"
115+#include "preferences.h"
116+#include "SoundManager.h"
117+#include "Plugins.h"
118+
119+// LP change: added chase-cam init and render allocation
120+#include "ChaseCam.h"
121+#include "render.h"
122+
123+#include "XML_LevelScript.h"
124+
125+// For packing and unpacking some of the stuff
126+#include "Packing.h"
127+
128+#include "motion_sensor.h" // ZZZ for reset_motion_sensor()
129+
130+#include "Music.h"
131+
132+#ifdef env68k
133+#pragma segment file_io
134+#endif
135+
136+// unify the save game code into one structure.
137+
138+/* -------- local globals */
139+FileSpecifier MapFileSpec;
140+static bool file_is_set= false;
141+
142+// LP addition: was a physics model loaded from the previous level loaded?
143+static bool PhysicsModelLoadedEarlier = false;
144+
145+// The following local globals are for handling games that need to be restored.
146+struct revert_game_info
147+{
148+ bool game_is_from_disk;
149+ struct game_data game_information;
150+ struct player_start_data player_start;
151+ struct entry_point entry_point;
152+ FileSpecifier SavedGame;
153+};
154+static struct revert_game_info revert_game_data;
155+
156+#if 0
157+/* Borrowed from the old lightsource.h, to allow Marathon II to open/use Marathon I maps */
158+struct old_light_data {
159+ uint16 flags;
160+
161+ int16 type;
162+ int16 mode; /* on, off, etc. */
163+ int16 phase;
164+
165+ fixed minimum_intensity, maximum_intensity;
166+ int16 period; /* on, in ticks (turning on and off periods are always the same for a given light type,
167+ or else are some function of this period) */
168+
169+ fixed intensity; /* current intensity */
170+
171+ int16 unused[5];
172+};
173+
174+enum /* old light types */
175+{
176+ _light_is_normal,
177+ _light_is_rheostat,
178+ _light_is_flourescent,
179+ _light_is_strobe,
180+ _light_flickers,
181+ _light_pulsates,
182+ _light_is_annoying,
183+ _light_is_energy_efficient
184+};
185+#endif
186+
187+/* -------- static functions */
188+static void scan_and_add_scenery(void);
189+static void complete_restoring_level(struct wad_data *wad);
190+static void load_redundant_map_data(short *redundant_data, size_t count);
191+static void allocate_map_structure_for_map(struct wad_data *wad);
192+static wad_data *build_export_wad(wad_header *header, int32 *length);
193+static struct wad_data *build_save_game_wad(struct wad_header *header, int32 *length);
194+
195+static void allocate_map_for_counts(size_t polygon_count, size_t side_count,
196+ size_t endpoint_count, size_t line_count);
197+static void load_points(uint8 *points, size_t count);
198+static void load_lines(uint8 *lines, size_t count);
199+static void load_sides(uint8 *sides, size_t count, short version);
200+static void load_polygons(uint8 *polys, size_t count, short version);
201+static void load_lights(uint8 *_lights, size_t count, short version);
202+static void load_annotations(uint8 *annotations, size_t count);
203+static void load_objects(uint8 *map_objects, size_t count);
204+static void load_media(uint8 *_medias, size_t count);
205+static void load_map_info(uint8 *map_info);
206+static void load_ambient_sound_images(uint8 *data, size_t count);
207+static void load_random_sound_images(uint8 *data, size_t count);
208+static void load_terminal_data(uint8 *data, size_t length);
209+
210+/* Used _ONLY_ by game_wad.c internally and precalculate.c. */
211+// ZZZ: hmm, no longer true, now using when resuming a network saved-game... hope that's ok?...
212+//static bool process_map_wad(struct wad_data *wad, bool restoring_game, short version);
213+
214+/* Final three calls, must be in this order! */
215+static void recalculate_redundant_map(void);
216+static void scan_and_add_platforms(uint8 *platform_static_data, size_t count);
217+static void complete_loading_level(short *_map_indexes, size_t map_index_count,
218+ uint8 *_platform_data, size_t platform_data_count,
219+ uint8 *actual_platform_data, size_t actual_platform_data_count, short version);
220+
221+static uint8 *unpack_directory_data(uint8 *Stream, directory_data *Objects, size_t Count);
222+//static uint8 *pack_directory_data(uint8 *Stream, directory_data *Objects, int Count);
223+
224+/* ------------------------ Net functions */
225+int32 get_net_map_data_length(
226+ void *data)
227+{
228+ return get_flat_data_length(data);
229+}
230+
231+/* Note that this frees it as well */
232+bool process_net_map_data(
233+ void *data)
234+{
235+ struct wad_header header;
236+ struct wad_data *wad;
237+ bool success= false;
238+
239+ wad= inflate_flat_data(data, &header);
240+ if(wad)
241+ {
242+ success= process_map_wad(wad, false, header.data_version);
243+ free_wad(wad); /* Note that the flat data points into the wad. */
244+ }
245+
246+ return success;
247+}
248+
249+/* This will have to do some interesting voodoo with union wads, methinks */
250+void *get_map_for_net_transfer(
251+ struct entry_point *entry)
252+{
253+ assert(file_is_set);
254+
255+ /* false means don't use union maps.. */
256+ return get_flat_data(MapFileSpec, false, entry->level_number);
257+}
258+
259+/* ---------------------- End Net Functions ----------- */
260+
261+/* This takes a cstring */
262+void set_map_file(FileSpecifier& File)
263+{
264+ // Do whatever parameter restoration is specified before changing the file
265+ if (file_is_set) RunRestorationScript();
266+
267+ MapFileSpec = File;
268+ set_scenario_images_file(File);
269+ // Only need to do this here
270+ LoadLevelScripts(File);
271+
272+ // Don't care whether there was an error when checking on the file's scenario images
273+ clear_game_error();
274+
275+ file_is_set= true;
276+}
277+
278+/* Set to the default map.. (Only if no map doubleclicked upon on startup.. */
279+void set_to_default_map(
280+ void)
281+{
282+ FileSpecifier NewMapFile;
283+
284+ get_default_map_spec(NewMapFile);
285+ set_map_file(NewMapFile);
286+}
287+
288+/* Return true if it finds the file, and it sets the mapfile to that file. */
289+/* Otherwise it returns false, meaning that we need have the file sent to us. */
290+bool use_map_file(
291+ uint32 checksum)
292+{
293+ FileSpecifier File;
294+ bool success= false;
295+
296+ if(find_wad_file_that_has_checksum(File, _typecode_scenario, strPATHS, checksum))
297+ {
298+ set_map_file(File);
299+ success= true;
300+ }
301+
302+ return success;
303+}
304+
305+bool load_level_from_map(
306+ short level_index)
307+{
308+ OpenedFile OFile;
309+ struct wad_header header;
310+ struct wad_data *wad;
311+ short index_to_load;
312+ bool restoring_game= false;
313+
314+ if(file_is_set)
315+ {
316+ /* Determine what we are trying to do.. */
317+ if(level_index==NONE)
318+ {
319+ restoring_game= true;
320+ index_to_load= 0; /* Saved games are always index 0 */
321+ } else {
322+ index_to_load= level_index;
323+ }
324+
325+ OpenedFile MapFile;
326+ if (open_wad_file_for_reading(MapFileSpec,MapFile))
327+ {
328+ /* Read the file */
329+ if(read_wad_header(MapFile, &header))
330+ {
331+ if(index_to_load>=0 && index_to_load<header.wad_count)
332+ {
333+
334+ wad= read_indexed_wad_from_file(MapFile, &header, index_to_load, true);
335+ if (wad)
336+ {
337+ /* Process everything... */
338+ process_map_wad(wad, restoring_game, header.data_version);
339+
340+ /* Nuke our memory... */
341+ free_wad(wad);
342+ } else {
343+ // error code has been set...
344+ }
345+ } else {
346+ set_game_error(gameError, errWadIndexOutOfRange);
347+ }
348+ } else {
349+ // error code has been set...
350+ }
351+
352+ /* Close the file.. */
353+ close_wad_file(MapFile);
354+ } else {
355+ // error code has been set..
356+ }
357+ } else {
358+ set_game_error(gameError, errMapFileNotSet);
359+ }
360+
361+ /* ... and bail */
362+ return (!error_pending());
363+}
364+
365+// keep these around for level export
366+static std::vector<static_platform_data> static_platforms;
367+
368+extern bool ok_to_reset_scenery_solidity;
369+
370+/* Hopefully this is in the correct order of initialization... */
371+/* This sucks, beavis. */
372+void complete_loading_level(
373+ short *_map_indexes,
374+ size_t map_index_count,
375+ uint8 *_platform_data,
376+ size_t platform_data_count,
377+ uint8 *actual_platform_data,
378+ size_t actual_platform_data_count,
379+ short version)
380+{
381+ /* Scan, add the doors, recalculate, and generally tie up all loose ends */
382+ /* Recalculate the redundant data.. */
383+ load_redundant_map_data(_map_indexes, map_index_count);
384+
385+ static_platforms.clear();
386+
387+ /* Add the platforms. */
388+ if(_platform_data || (_platform_data==NULL && actual_platform_data==NULL))
389+ {
390+ scan_and_add_platforms(_platform_data, platform_data_count);
391+ } else {
392+ assert(actual_platform_data);
393+ PlatformList.resize(actual_platform_data_count);
394+ unpack_platform_data(actual_platform_data,platforms,actual_platform_data_count);
395+ assert(actual_platform_data_count == static_cast<size_t>(static_cast<int16>(actual_platform_data_count)));
396+ assert(0 <= static_cast<int16>(actual_platform_data_count));
397+ dynamic_world->platform_count= static_cast<int16>(actual_platform_data_count);
398+ }
399+
400+ scan_and_add_scenery();
401+ ok_to_reset_scenery_solidity = true;
402+
403+ /* Gotta do this after recalculate redundant.. */
404+ if(version==MARATHON_ONE_DATA_VERSION)
405+ {
406+ short loop;
407+
408+ for(loop= 0; loop<dynamic_world->polygon_count; ++loop)
409+ {
410+ guess_side_lightsource_indexes(loop);
411+ }
412+ }
413+}
414+
415+/* Call with location of NULL to get the number of start locations for a */
416+/* given team or player */
417+short get_player_starting_location_and_facing(
418+ short team,
419+ short index,
420+ struct object_location *location)
421+{
422+#if 1
423+ short ii;
424+ struct map_object *saved_object;
425+ short count= 0;
426+ bool done= false;
427+
428+ saved_object= saved_objects;
429+ for(ii=0; !done && ii<dynamic_world->initial_objects_count; ++ii)
430+ {
431+ if(saved_object->type==_saved_player)
432+ {
433+ /* index=NONE means use any starting location */
434+ if(saved_object->index==team || team==NONE)
435+ {
436+ if(location && count==index)
437+ {
438+ location->p= saved_object->location;
439+ location->polygon_index= saved_object->polygon_index;
440+ location->yaw= saved_object->facing;
441+ location->pitch= 0;
442+ location->flags= saved_object->flags;
443+ done= true;
444+ }
445+ count++;
446+ }
447+ }
448+ ++saved_object;
449+ }
450+
451+ /* If they asked for a valid location, make sure that we gave them one */
452+ if(location) vassert(done, csprintf(temporary, "Tried to place: %d only %d starting pts.", index, count));
453+
454+ return count;
455+#else
456+ location->x= 0x14e9;
457+ location->y= 0x1ba0;
458+ *facing= 0x00;
459+ *polygon_index= 0x6f;
460+
461+ return 1;
462+#endif
463+}
464+
465+uint32 get_current_map_checksum(
466+ void)
467+{
468+ // fileref file_handle;
469+ struct wad_header header;
470+
471+ assert(file_is_set);
472+ OpenedFile MapFile;
473+ open_wad_file_for_reading(MapFileSpec, MapFile);
474+ assert(MapFile.IsOpen());
475+
476+ /* Read the file */
477+ read_wad_header(MapFile, &header);
478+
479+ /* Close the file.. */
480+ close_wad_file(MapFile);
481+
482+ return header.checksum;
483+}
484+
485+// ZZZ: split this out from new_game for sharing
486+void set_saved_game_name_to_default()
487+{
488+#if defined(mac) || defined(SDL_RFORK_HACK)
489+ revert_game_data.SavedGame.SetToApp();
490+ revert_game_data.SavedGame.SetName(getcstr(temporary, strFILENAMES, filenameDEFAULT_SAVE_GAME),_typecode_savegame);
491+#endif
492+#if defined(SDL) && !defined(SDL_RFORK_HACK)
493+ revert_game_data.SavedGame.SetToSavedGamesDir();
494+ revert_game_data.SavedGame += getcstr(temporary, strFILENAMES, filenameDEFAULT_SAVE_GAME);
495+#endif
496+}
497+
498+extern void ResetPassedLua();
499+
500+bool new_game(
501+ short number_of_players,
502+ bool network,
503+ struct game_data *game_information,
504+ struct player_start_data *player_start_information,
505+ struct entry_point *entry_point)
506+{
507+ short player_index, i;
508+ bool success= true;
509+
510+ ResetPassedLua();
511+
512+ /* Make sure our code is synchronized.. */
513+ assert(MAXIMUM_PLAYER_START_NAME_LENGTH==MAXIMUM_PLAYER_NAME_LENGTH);
514+
515+ /* Initialize the global network going flag... */
516+ game_is_networked= network;
517+
518+ /* If we want to save it, this is an untitled map.. */
519+ set_saved_game_name_to_default();
520+
521+ /* Set the random seed. */
522+ set_random_seed(game_information->initial_random_seed);
523+
524+ /* Initialize the players to a known state. This must be done before goto_level */
525+ /* because it sets dynamic_world->player_count to 0, which is crucial for when */
526+ /* I try to recreate the players... */
527+ initialize_map_for_new_game(); // memsets dynamic_world to 0
528+
529+ /* Copy the game data into the dynamic_world */
530+ /* ajr-this used to be done only when we successfully loaded the map. however, goto_level
531+ * will place the initial monsters on a level, which calls new_monster, which relies
532+ * on this information being setup properly, so we do it here instead. */
533+ obj_copy(dynamic_world->game_information, *game_information);
534+
535+ /* Load the level */
536+ assert(file_is_set);
537+ success= goto_level(entry_point, true);
538+ /* If we were able to load the map... */
539+ if(success)
540+ {
541+ /* Initialize the players-> note there may be more than one player in a */
542+ /* non-network game, for playback.. */
543+ for (i=0;i<number_of_players;++i)
544+ {
545+ player_index= new_player(player_start_information[i].team,
546+ player_start_information[i].color, player_start_information[i].identifier);
547+ assert(player_index==i);
548+
549+ /* Now copy in the name of the player.. */
550+ assert(strlen(player_start_information[i].name)<=MAXIMUM_PLAYER_NAME_LENGTH);
551+ strcpy(players[i].name, player_start_information[i].name);
552+ }
553+
554+#if !defined(DISABLE_NETWORKING)
555+ if(game_is_networked)
556+ {
557+ /* Make sure we can count. */
558+ assert(number_of_players==NetGetNumberOfPlayers());
559+
560+ set_local_player_index(NetGetLocalPlayerIndex());
561+ set_current_player_index(NetGetLocalPlayerIndex());
562+ }
563+ else
564+#endif // !defined(DISABLE_NETWORKING)
565+ {
566+ set_local_player_index(0);
567+ set_current_player_index(0);
568+ }
569+
570+ /* we need to alert the function that reverts the game of the game setup so that
571+ * new game can be called if the user wants to revert later.
572+ */
573+ setup_revert_game_info(game_information, player_start_information, entry_point);
574+
575+ // Reset the player queues (done here and in load_game)
576+ reset_action_queues();
577+
578+ /* Load the collections */
579+ /* entering map might fail if NetSync() fails.. */
580+ success= entering_map(false);
581+
582+ // ZZZ: set motion sensor to sane state - needs to come after entering_map() (which calls load_collections())
583+ reset_motion_sensor(current_player_index);
584+ }
585+
586+ // LP change: adding chase-cam initialization
587+ ChaseCam_Initialize();
588+
589+ return success;
590+}
591+
592+bool get_indexed_entry_point(
593+ struct entry_point *entry_point,
594+ short *index,
595+ int32 type)
596+{
597+ short actual_index;
598+
599+ // Open map file
600+ assert(file_is_set);
601+ OpenedFile MapFile;
602+ if (!open_wad_file_for_reading(MapFileSpec,MapFile))
603+ return false;
604+
605+ // Read header
606+ wad_header header;
607+ if (!read_wad_header(MapFile, &header)) {
608+ close_wad_file(MapFile);
609+ return false;
610+ }
611+
612+ bool success = false;
613+ if (header.application_specific_directory_data_size == SIZEOF_directory_data)
614+ {
615+
616+ // New style wad
617+ void *total_directory_data= read_directory_data(MapFile, &header);
618+
619+ assert(total_directory_data);
620+ for(actual_index= *index; actual_index<header.wad_count; ++actual_index)
621+ {
622+ uint8 *p = (uint8 *)get_indexed_directory_data(&header, actual_index, total_directory_data);
623+ directory_data directory;
624+ unpack_directory_data(p, &directory, 1);
625+
626+ /* Find the flags that match.. */
627+ if(directory.entry_point_flags & type)
628+ {
629+ /* This one is valid! */
630+ entry_point->level_number= actual_index;
631+ strcpy(entry_point->level_name, directory.level_name);
632+
633+ *index= actual_index+1;
634+ success= true;
635+ break; /* Out of the for loop */
636+ }
637+ }
638+ free(total_directory_data);
639+
640+ } else {
641+
642+ // Old style wad, find the index
643+ for(actual_index= *index; !success && actual_index<header.wad_count; ++actual_index)
644+ {
645+ struct wad_data *wad;
646+
647+ /* Read the file */
648+ wad= read_indexed_wad_from_file(MapFile, &header, actual_index, true);
649+ if (wad)
650+ {
651+ /* IF this has the proper type.. */
652+ size_t length;
653+ uint8 *p = (uint8 *)extract_type_from_wad(wad, MAP_INFO_TAG, &length);
654+ assert(length == SIZEOF_static_data);
655+ static_data map_info;
656+ unpack_static_data(p, &map_info, 1);
657+
658+ if(map_info.entry_point_flags & type)
659+ {
660+ /* This one is valid! */
661+ entry_point->level_number= actual_index;
662+ assert(strlen(map_info.level_name)<LEVEL_NAME_LENGTH);
663+ strcpy(entry_point->level_name, map_info.level_name);
664+
665+ *index= actual_index+1;
666+ success= true;
667+ }
668+
669+ free_wad(wad);
670+ }
671+ }
672+ }
673+
674+ return success;
675+}
676+
677+// Get vector of map entry points matching given type
678+bool get_entry_points(vector<entry_point> &vec, int32 type)
679+{
680+ vec.clear();
681+
682+ // Open map file
683+ assert(file_is_set);
684+ OpenedFile MapFile;
685+ if (!open_wad_file_for_reading(MapFileSpec,MapFile))
686+ return false;
687+
688+ // Read header
689+ wad_header header;
690+ if (!read_wad_header(MapFile, &header)) {
691+ close_wad_file(MapFile);
692+ return false;
693+ }
694+
695+ bool success = false;
696+ if (header.application_specific_directory_data_size == SIZEOF_directory_data) {
697+
698+ // New style wad, read directory data
699+ void *total_directory_data = read_directory_data(MapFile, &header);
700+ assert(total_directory_data);
701+
702+ // Push matching directory entries into vector
703+ for (int i=0; i<header.wad_count; i++) {
704+ uint8 *p = (uint8 *)get_indexed_directory_data(&header, i, total_directory_data);
705+ directory_data directory;
706+ unpack_directory_data(p, &directory, 1);
707+
708+ if (directory.entry_point_flags & type) {
709+
710+ // This one is valid
711+ entry_point point;
712+ point.level_number = i;
713+ strcpy(point.level_name, directory.level_name);
714+ vec.push_back(point);
715+ success = true;
716+ }
717+ }
718+ free(total_directory_data);
719+
720+ } else {
721+
722+ // Old style wad
723+ for (int i=0; i<header.wad_count; i++) {
724+
725+ wad_data *wad = read_indexed_wad_from_file(MapFile, &header, i, true);
726+ if (!wad)
727+ continue;
728+
729+ // Read map_info data
730+ size_t length;
731+ uint8 *p = (uint8 *)extract_type_from_wad(wad, MAP_INFO_TAG, &length);
732+ assert(length == SIZEOF_static_data);
733+ static_data map_info;
734+ unpack_static_data(p, &map_info, 1);
735+
736+ if (map_info.entry_point_flags & type) {
737+
738+ // This one is valid
739+ entry_point point;
740+ point.level_number = i;
741+ assert(strlen(map_info.level_name) < LEVEL_NAME_LENGTH);
742+ strcpy(point.level_name, map_info.level_name);
743+ vec.push_back(point);
744+ success = true;
745+ }
746+
747+ free_wad(wad);
748+ }
749+ }
750+
751+ return success;
752+}
753+
754+extern void LoadSoloLua();
755+extern void RunLuaScript();
756+
757+/* This is called when the game level is changed somehow */
758+/* The only thing that has to be valid in the entry point is the level_index */
759+
760+/* Returns a short that is an OSErr... */
761+bool goto_level(
762+ struct entry_point *entry,
763+ bool new_game)
764+{
765+ bool success= true;
766+
767+ if(!new_game)
768+ {
769+ /* Clear the current map */
770+ leaving_map();
771+
772+ // ghs: hack to get new MML-specified sounds loaded
773+ SoundManager::instance()->UnloadAllSounds();
774+ }
775+
776+#if !defined(DISABLE_NETWORKING)
777+ /* If the game is networked, then I must call the network code to do the right */
778+ /* thing with the map.. */
779+ if(game_is_networked)
780+ {
781+ /* This function, if it is a server, calls get_map_for_net_transfer, and */
782+ /* then calls process_map_wad on it. Non-server receives the map and then */
783+ /* calls process_map_wad on it. */
784+ success= NetChangeMap(entry);
785+ }
786+ else
787+#endif // !defined(DISABLE_NETWORKING)
788+ {
789+ /* Load it and then rock.. */
790+ load_level_from_map(entry->level_number);
791+ if(error_pending()) success= false;
792+ }
793+
794+ if (success)
795+ {
796+ // LP: doing this here because level-specific MML may specify which level-specific
797+ // textures to load.
798+ // Being careful to carry over errors so that Pfhortran errors can be ignored
799+ short SavedType, SavedError = get_game_error(&SavedType);
800+ if (!game_is_networked || use_map_file(((game_info *) NetGetGameData())->parent_checksum))
801+ {
802+ RunLevelScript(entry->level_number);
803+ }
804+ else
805+ {
806+ ResetLevelScript();
807+ }
808+ RunScriptChunks();
809+ if (!game_is_networked)
810+ {
811+ Plugins::instance()->load_solo_mml();
812+ LoadSoloLua();
813+ }
814+ Music::instance()->PreloadLevelMusic();
815+ set_game_error(SavedType,SavedError);
816+
817+ if (!new_game)
818+ {
819+ recreate_players_for_new_level();
820+ }
821+
822+ /* Load the collections */
823+ dynamic_world->current_level_number= entry->level_number;
824+
825+ // ghs: this runs very early now
826+ // we want to be before place_initial_objects, and
827+ // before MarkLuaCollections
828+ RunLuaScript();
829+
830+ if (film_profile.early_object_initialization)
831+ {
832+ place_initial_objects();
833+ initialize_control_panels_for_level();
834+ }
835+
836+ if (!new_game)
837+ {
838+
839+ /* entering_map might fail if netsync fails, but we will have already displayed */
840+ /* the error.. */
841+ success= entering_map(false);
842+ }
843+
844+ if (!film_profile.early_object_initialization && success)
845+ {
846+ place_initial_objects();
847+ initialize_control_panels_for_level();
848+ }
849+
850+ }
851+
852+// if(!success) alert_user(fatalError, strERRORS, badReadMap, -1);
853+
854+ /* We be done.. */
855+ return success;
856+}
857+
858+/* -------------------- Private or map editor functions */
859+void allocate_map_for_counts(
860+ size_t polygon_count,
861+ size_t side_count,
862+ size_t endpoint_count,
863+ size_t line_count)
864+{
865+ //long cumulative_length= 0;
866+ size_t automap_line_count, automap_polygon_count, map_index_count;
867+ // long automap_line_length, automap_polygon_length, map_index_length;
868+
869+ /* Give the map indexes a whole bunch of memory (cause we can't calculate it) */
870+ // map_index_length= (polygon_count*32+1024)*sizeof(int16);
871+ map_index_count= (polygon_count*32+1024);
872+
873+ /* Automap lines. */
874+ // automap_line_length= (line_count/8+((line_count%8)?1:0))*sizeof(byte);
875+ automap_line_count= (line_count/8+((line_count%8)?1:0));
876+
877+ /* Automap Polygons */
878+ // automap_polygon_length= (polygon_count/8+((polygon_count%8)?1:0))*sizeof(byte);
879+ automap_polygon_count= (polygon_count/8+((polygon_count%8)?1:0));
880+
881+ // cumulative_length+= polygon_count*sizeof(struct polygon_data);
882+ // cumulative_length+= side_count*sizeof(struct side_data);
883+ // cumulative_length+= endpoint_count*sizeof(struct endpoint_data);
884+ // cumulative_length+= line_count*sizeof(struct line_data);
885+ // cumulative_length+= map_index_length;
886+ // cumulative_length+= automap_line_length;
887+ // cumulative_length+= automap_polygon_length;
888+
889+ /* Okay, we now have the length. Allocate our block.. */
890+ // reallocate_map_structure_memory(cumulative_length);
891+
892+ /* Tell the recalculation data how big it is.. */
893+ // set_map_index_buffer_size(map_index_length);
894+
895+ /* Setup our pointers. */
896+ // map_polygons= (struct polygon_data *) get_map_structure_chunk(polygon_count*sizeof(struct polygon_data));
897+ // map_sides= (struct side_data *) get_map_structure_chunk(side_count*sizeof(struct side_data));
898+ // map_endpoints= (struct endpoint_data *) get_map_structure_chunk(endpoint_count*sizeof(struct endpoint_data));
899+ // map_lines= (struct line_data *) get_map_structure_chunk(line_count*sizeof(struct line_data));
900+ // map_indexes= (short *) get_map_structure_chunk(map_index_length);
901+ // automap_lines= (uint8 *) get_map_structure_chunk(automap_line_length);
902+ // automap_polygons= (uint8 *) get_map_structure_chunk(automap_polygon_length);
903+
904+ // Most of the other stuff: reallocate here
905+ EndpointList.resize(endpoint_count);
906+ LineList.resize(line_count);
907+ SideList.resize(side_count);
908+ PolygonList.resize(polygon_count);
909+ AutomapLineList.resize(automap_line_count);
910+ AutomapPolygonList.resize(automap_polygon_count);
911+
912+ // Map indexes: start off with none of them (of course),
913+ // but reserve a size equal to the map index length
914+ MapIndexList.clear();
915+ MapIndexList.reserve(map_index_count);
916+ dynamic_world->map_index_count= 0;
917+
918+ // Stuff that needs the max number of polygons
919+ allocate_render_memory();
920+ allocate_flood_map_memory();
921+}
922+
923+void load_points(
924+ uint8 *points,
925+ size_t count)
926+{
927+ size_t loop;
928+
929+ // OK to modify input-data pointer since it's called by value
930+ for(loop=0; loop<count; ++loop)
931+ {
932+ world_point2d& vertex = map_endpoints[loop].vertex;
933+ StreamToValue(points,vertex.x);
934+ StreamToValue(points,vertex.y);
935+ }
936+ assert(count == static_cast<size_t>(static_cast<int16>(count)));
937+ assert(0 <= static_cast<int16>(count));
938+ dynamic_world->endpoint_count= static_cast<int16>(count);
939+}
940+
941+void load_lines(
942+ uint8 *lines,
943+ size_t count)
944+{
945+ // assert(count>=0 && count<=MAXIMUM_LINES_PER_MAP);
946+ unpack_line_data(lines,map_lines,count);
947+ assert(count == static_cast<size_t>(static_cast<int16>(count)));
948+ assert(0 <= static_cast<int16>(count));
949+ dynamic_world->line_count= static_cast<int16>(count);
950+}
951+
952+void load_sides(
953+ uint8 *sides,
954+ size_t count,
955+ short version)
956+{
957+ size_t loop;
958+
959+ // assert(count>=0 && count<=MAXIMUM_SIDES_PER_MAP);
960+
961+ unpack_side_data(sides,map_sides,count);
962+
963+ for(loop=0; loop<count; ++loop)
964+ {
965+ if(version==MARATHON_ONE_DATA_VERSION)
966+ {
967+ map_sides[loop].transparent_texture.texture= UNONE;
968+ map_sides[loop].ambient_delta= 0;
969+ }
970+ ++sides;
971+ }
972+
973+ assert(count == static_cast<size_t>(static_cast<int16>(count)));
974+ assert(0 <= static_cast<int16>(count));
975+ dynamic_world->side_count= static_cast<int16>(count);
976+}
977+
978+void load_polygons(
979+ uint8 *polys,
980+ size_t count,
981+ short version)
982+{
983+ size_t loop;
984+
985+ // assert(count>=0 && count<=MAXIMUM_POLYGONS_PER_MAP);
986+
987+ unpack_polygon_data(polys,map_polygons,count);
988+ assert(count == static_cast<size_t>(static_cast<int16>(count)));
989+ assert(0 <= static_cast<int16>(count));
990+ dynamic_world->polygon_count= static_cast<int16>(count);
991+
992+ /* Allow for backward compatibility! */
993+ switch(version)
994+ {
995+ case MARATHON_ONE_DATA_VERSION:
996+ for(loop= 0; loop<count; ++loop)
997+ {
998+ map_polygons[loop].media_index= NONE;
999+ map_polygons[loop].floor_origin.x= map_polygons[loop].floor_origin.y= 0;
1000+ map_polygons[loop].ceiling_origin.x= map_polygons[loop].ceiling_origin.y= 0;
1001+ }
1002+ break;
1003+
1004+ case MARATHON_TWO_DATA_VERSION:
1005+ // LP addition:
1006+ case MARATHON_INFINITY_DATA_VERSION:
1007+ break;
1008+
1009+ default:
1010+ assert(false);
1011+ break;
1012+ }
1013+}
1014+
1015+void load_lights(
1016+ uint8 *_lights,
1017+ size_t count,
1018+ short version)
1019+{
1020+ unsigned short loop, new_index;
1021+
1022+ LightList.resize(count);
1023+ objlist_clear(lights,count);
1024+ // vassert(count>=0 && count<=MAXIMUM_LIGHTS_PER_MAP, csprintf(temporary, "Light count: %d vers: %d",
1025+ // count, version));
1026+
1027+ old_light_data *OldLights;
1028+
1029+ switch(version)
1030+ {
1031+ case MARATHON_ONE_DATA_VERSION: {
1032+
1033+ // Unpack the old lights into a temporary array
1034+ OldLights = new old_light_data[count];
1035+ unpack_old_light_data(_lights,OldLights,count);
1036+
1037+ old_light_data *OldLtPtr = OldLights;
1038+ for(loop= 0; loop<count; ++loop, OldLtPtr++)
1039+ {
1040+ static_light_data TempLight;
1041+ convert_old_light_data_to_new(&TempLight, OldLtPtr, 1);
1042+
1043+ new_index = new_light(&TempLight);
1044+ assert(new_index==loop);
1045+ }
1046+ delete []OldLights;
1047+ break;
1048+ }
1049+
1050+ case MARATHON_TWO_DATA_VERSION:
1051+ case MARATHON_INFINITY_DATA_VERSION:
1052+ // OK to modify the data pointer since it was passed by value
1053+ for(loop= 0; loop<count; ++loop)
1054+ {
1055+ static_light_data TempLight;
1056+ _lights = unpack_static_light_data(_lights, &TempLight, 1);
1057+
1058+ new_index = new_light(&TempLight);
1059+ assert(new_index==loop);
1060+ }
1061+ break;
1062+
1063+ default:
1064+ assert(false);
1065+ break;
1066+ }
1067+}
1068+
1069+void load_annotations(
1070+ uint8 *annotations,
1071+ size_t count)
1072+{
1073+ // assert(count>=0 && count<=MAXIMUM_ANNOTATIONS_PER_MAP);
1074+ MapAnnotationList.resize(count);
1075+ unpack_map_annotation(annotations,map_annotations,count);
1076+ assert(count == static_cast<size_t>(static_cast<int16>(count)));
1077+ assert(0 <= static_cast<int16>(count));
1078+ dynamic_world->default_annotation_count= static_cast<int16>(count);
1079+}
1080+
1081+void load_objects(uint8 *map_objects, size_t count)
1082+{
1083+ // assert(count>=0 && count<=MAXIMUM_SAVED_OBJECTS);
1084+ SavedObjectList.resize(count);
1085+ unpack_map_object(map_objects,saved_objects,count);
1086+ assert(count == static_cast<size_t>(static_cast<int16>(count)));
1087+ assert(0 <= static_cast<int16>(count));
1088+ dynamic_world->initial_objects_count= static_cast<int16>(count);
1089+}
1090+
1091+void load_map_info(
1092+ uint8 *map_info)
1093+{
1094+ unpack_static_data(map_info,static_world,1);
1095+ static_world->ball_in_play = false;
1096+}
1097+
1098+void load_media(
1099+ uint8 *_medias,
1100+ size_t count)
1101+{
1102+ // struct media_data *media= _medias;
1103+ size_t ii;
1104+
1105+ MediaList.resize(count);
1106+ objlist_clear(medias,count);
1107+ // assert(count>=0 && count<=MAXIMUM_MEDIAS_PER_MAP);
1108+
1109+ for(ii= 0; ii<count; ++ii)
1110+ {
1111+ media_data TempMedia;
1112+ _medias = unpack_media_data(_medias,&TempMedia,1);
1113+
1114+ size_t new_index = new_media(&TempMedia);
1115+ assert(new_index==ii);
1116+ }
1117+}
1118+
1119+void load_ambient_sound_images(
1120+ uint8 *data,
1121+ size_t count)
1122+{
1123+ // assert(count>=0 &&count<=MAXIMUM_AMBIENT_SOUND_IMAGES_PER_MAP);
1124+ AmbientSoundImageList.resize(count);
1125+ unpack_ambient_sound_image_data(data,ambient_sound_images,count);
1126+ assert(count == static_cast<size_t>(static_cast<int16>(count)));
1127+ assert(0 <= static_cast<int16>(count));
1128+ dynamic_world->ambient_sound_image_count= static_cast<int16>(count);
1129+}
1130+
1131+void load_random_sound_images(
1132+ uint8 *data,
1133+ size_t count)
1134+{
1135+ // assert(count>=0 &&count<=MAXIMUM_RANDOM_SOUND_IMAGES_PER_MAP);
1136+ RandomSoundImageList.resize(count);
1137+ unpack_random_sound_image_data(data,random_sound_images,count);
1138+ assert(count == static_cast<size_t>(static_cast<int16>(count)));
1139+ assert(0 <= static_cast<int16>(count));
1140+ dynamic_world->random_sound_image_count= static_cast<int16>(count);
1141+}
1142+
1143+/* Recalculate all the redundant crap- must be done before platforms/doors/etc.. */
1144+void recalculate_redundant_map(
1145+ void)
1146+{
1147+ short loop;
1148+
1149+ for(loop=0;loop<dynamic_world->polygon_count;++loop) recalculate_redundant_polygon_data(loop);
1150+ for(loop=0;loop<dynamic_world->line_count;++loop) recalculate_redundant_line_data(loop);
1151+ for(loop=0;loop<dynamic_world->endpoint_count;++loop) recalculate_redundant_endpoint_data(loop);
1152+}
1153+
1154+extern bool load_game_from_file(FileSpecifier& File);
1155+
1156+bool load_game_from_file(FileSpecifier& File)
1157+{
1158+ bool success= false;
1159+
1160+ ResetPassedLua();
1161+
1162+ /* Setup for a revert.. */
1163+ revert_game_data.game_is_from_disk = true;
1164+ revert_game_data.SavedGame = File;
1165+
1166+ /* Use the save game file.. */
1167+ set_map_file(File);
1168+
1169+ /* Load the level from the map */
1170+ success= load_level_from_map(NONE); /* Save games are ALWAYS index NONE */
1171+ if (success)
1172+ {
1173+ uint32 parent_checksum;
1174+
1175+ /* Find the original scenario this saved game was a part of.. */
1176+ parent_checksum= read_wad_file_parent_checksum(File);
1177+ if(use_map_file(parent_checksum))
1178+ {
1179+ // LP: getting the level scripting off of the map file
1180+ // Being careful to carry over errors so that Pfhortran errors can be ignored
1181+ short SavedType, SavedError = get_game_error(&SavedType);
1182+ RunLevelScript(dynamic_world->current_level_number);
1183+ RunScriptChunks();
1184+ if (!game_is_networked)
1185+ {
1186+ Plugins::instance()->load_solo_mml();
1187+ LoadSoloLua();
1188+ }
1189+ set_game_error(SavedType,SavedError);
1190+ }
1191+ else
1192+ {
1193+ /* Tell the user theyÕre screwed when they try to leave this level. */
1194+ alert_user(infoError, strERRORS, cantFindMap, 0);
1195+
1196+ // LP addition: makes the game look normal
1197+ hide_cursor();
1198+
1199+ /* Set to the default map. */
1200+ set_to_default_map();
1201+
1202+ ResetLevelScript();
1203+ RunScriptChunks();
1204+ }
1205+ }
1206+
1207+ return success;
1208+}
1209+
1210+void setup_revert_game_info(
1211+ struct game_data *game_info,
1212+ struct player_start_data *start,
1213+ struct entry_point *entry)
1214+{
1215+ revert_game_data.game_is_from_disk = false;
1216+ obj_copy(revert_game_data.game_information, *game_info);
1217+ obj_copy(revert_game_data.player_start, *start);
1218+ obj_copy(revert_game_data.entry_point, *entry);
1219+}
1220+
1221+extern void reset_messages();
1222+
1223+bool revert_game(
1224+ void)
1225+{
1226+ bool successful;
1227+
1228+ assert(dynamic_world->player_count==1);
1229+
1230+ leaving_map();
1231+
1232+ if (revert_game_data.game_is_from_disk)
1233+ {
1234+ /* Reload their last saved game.. */
1235+ successful= load_game_from_file(revert_game_data.SavedGame);
1236+ if (successful)
1237+ {
1238+ Music::instance()->PreloadLevelMusic();
1239+ RunLuaScript();
1240+
1241+ // LP: added for loading the textures if one had died on another level;
1242+ // this gets around WZ's moving of this line into make_restored_game_relevant()
1243+ successful = entering_map(true /*restoring game*/);
1244+ }
1245+
1246+ /* And they don't get to continue. */
1247+ stop_recording();
1248+ }
1249+ else
1250+ {
1251+ /* This was the totally evil line discussed above. */
1252+ successful= new_game(1, false, &revert_game_data.game_information, &revert_game_data.player_start,
1253+ &revert_game_data.entry_point);
1254+
1255+ /* And rewind so that the last player is used. */
1256+ rewind_recording();
1257+ }
1258+
1259+ if(successful)
1260+ {
1261+ update_interface(NONE);
1262+ ChaseCam_Reset();
1263+ ResetFieldOfView();
1264+ reset_messages();
1265+ ReloadViewContext();
1266+ }
1267+
1268+ return successful;
1269+}
1270+
1271+bool export_level(FileSpecifier& File)
1272+{
1273+ struct wad_header header;
1274+ short err = 0;
1275+ bool success = false;
1276+ int32 offset, wad_length;
1277+ struct directory_entry entry;
1278+ struct wad_data *wad;
1279+
1280+ FileSpecifier TempFile;
1281+ DirectorySpecifier TempFileDir;
1282+ File.ToDirectory(TempFileDir);
1283+ TempFile.FromDirectory(TempFileDir);
1284+ TempFile.AddPart("savetemp.dat");
1285+
1286+ /* Fill in the default wad header (we are using File instead of TempFile to get the name right in the header) */
1287+ fill_default_wad_header(File, CURRENT_WADFILE_VERSION, MARATHON_TWO_DATA_VERSION, 1, 0, &header);
1288+
1289+ if (create_wadfile(TempFile, _typecode_scenario))
1290+ {
1291+ OpenedFile SaveFile;
1292+ if (open_wad_file_for_writing(TempFile, SaveFile))
1293+ {
1294+ /* Write out the new header */
1295+ if (write_wad_header(SaveFile, &header))
1296+ {
1297+ offset = SIZEOF_wad_header;
1298+
1299+ wad = build_export_wad(&header, &wad_length);
1300+ if (wad)
1301+ {
1302+ set_indexed_directory_offset_and_length(&header, &entry, 0, offset, wad_length, 0);
1303+
1304+ if (write_wad(SaveFile, &header, wad, offset))
1305+ {
1306+ /* Update the new header */
1307+ offset+= wad_length;
1308+ header.directory_offset= offset;
1309+ if (write_wad_header(SaveFile, &header) && write_directorys(SaveFile, &header, &entry))
1310+ {
1311+ /* We win. */
1312+ success= true;
1313+ }
1314+ }
1315+
1316+ free_wad(wad);
1317+ }
1318+ }
1319+
1320+ err = SaveFile.GetError();
1321+ calculate_and_store_wadfile_checksum(SaveFile);
1322+ close_wad_file(SaveFile);
1323+ }
1324+
1325+ if (!err)
1326+ {
1327+ if (!File.Exists())
1328+ create_wadfile(File,_typecode_savegame);
1329+
1330+ TempFile.Exchange(File);
1331+ err = TempFile.GetError();
1332+ TempFile.Delete(); // it's not an error if this fails
1333+ }
1334+ }
1335+
1336+ if (err || error_pending())
1337+ {
1338+ success = false;
1339+ }
1340+
1341+
1342+ return success;
1343+
1344+}
1345+
1346+void get_current_saved_game_name(FileSpecifier& File)
1347+{
1348+ File = revert_game_data.SavedGame;
1349+}
1350+
1351+/* The current mapfile should be set to the save game file... */
1352+bool save_game_file(FileSpecifier& File)
1353+{
1354+ struct wad_header header;
1355+ short err = 0;
1356+ bool success= false;
1357+ int32 offset, wad_length;
1358+ struct directory_entry entry;
1359+ struct wad_data *wad;
1360+
1361+ /* Save off the random seed. */
1362+ dynamic_world->random_seed= get_random_seed();
1363+
1364+ /* Setup to revert the game properly */
1365+ revert_game_data.game_is_from_disk= true;
1366+ revert_game_data.SavedGame = File;
1367+
1368+ // LP: add a file here; use temporary file for a safe save.
1369+ // Write into the temporary file first
1370+ FileSpecifier TempFile;
1371+ DirectorySpecifier TempFileDir;
1372+ File.ToDirectory(TempFileDir);
1373+ TempFile.FromDirectory(TempFileDir);
1374+#if defined(mac) || defined(SDL_RFORK_HACK)
1375+ TempFile.SetName("savetemp.dat",NONE);
1376+#else
1377+ TempFile.AddPart("savetemp.dat");
1378+#endif
1379+
1380+ /* Fill in the default wad header (we are using File instead of TempFile to get the name right in the header) */
1381+ fill_default_wad_header(File, CURRENT_WADFILE_VERSION, EDITOR_MAP_VERSION, 1, 0, &header);
1382+
1383+ /* Assume that we confirmed on save as... */
1384+ if (create_wadfile(TempFile,_typecode_savegame))
1385+ {
1386+ OpenedFile SaveFile;
1387+ if(open_wad_file_for_writing(TempFile,SaveFile))
1388+ {
1389+ /* Write out the new header */
1390+ if (write_wad_header(SaveFile, &header))
1391+ {
1392+ offset= SIZEOF_wad_header;
1393+
1394+ wad= build_save_game_wad(&header, &wad_length);
1395+ if (wad)
1396+ {
1397+ /* Set the entry data.. */
1398+ set_indexed_directory_offset_and_length(&header,
1399+ &entry, 0, offset, wad_length, 0);
1400+
1401+ /* Save it.. */
1402+ if (write_wad(SaveFile, &header, wad, offset))
1403+ {
1404+ /* Update the new header */
1405+ offset+= wad_length;
1406+ header.directory_offset= offset;
1407+ header.parent_checksum= read_wad_file_checksum(MapFileSpec);
1408+ if (write_wad_header(SaveFile, &header) && write_directorys(SaveFile, &header, &entry))
1409+ {
1410+ /* This function saves the overhead map as a thumbnail, as well */
1411+ /* as adding the string resource that tells you what it is when */
1412+ /* it is clicked on & Marathon2 isn't installed. Obviously, both */
1413+ /* of these are superfluous for a dos environment. */
1414+ add_finishing_touches_to_save_file(TempFile);
1415+
1416+ /* We win. */
1417+ success= true;
1418+ }
1419+ }
1420+
1421+ free_wad(wad);
1422+ }
1423+ }
1424+
1425+ err = SaveFile.GetError();
1426+ close_wad_file(SaveFile);
1427+ }
1428+
1429+ // LP addition: exchange with temporary file;
1430+ // create target file if necessary
1431+ if (!err)
1432+ {
1433+ if (!File.Exists())
1434+ create_wadfile(File,_typecode_savegame);
1435+
1436+ TempFile.Exchange(File);
1437+ err = TempFile.GetError();
1438+ TempFile.Delete(); // it's not an error if this fails
1439+ }
1440+ }
1441+
1442+ if(err || error_pending())
1443+ {
1444+ if(!err) err= get_game_error(NULL);
1445+ alert_user(infoError, strERRORS, fileError, err);
1446+ clear_game_error();
1447+ success= false;
1448+ }
1449+
1450+ return success;
1451+}
1452+
1453+/* -------- static functions */
1454+static void scan_and_add_platforms(
1455+ uint8 *platform_static_data,
1456+ size_t count)
1457+{
1458+ struct polygon_data *polygon;
1459+ short loop;
1460+
1461+ PlatformList.resize(count);
1462+ objlist_clear(platforms,count);
1463+
1464+ static_platforms.resize(count);
1465+ unpack_static_platform_data(platform_static_data, &static_platforms[0], count);
1466+
1467+ polygon= map_polygons;
1468+ for(loop=0; loop<dynamic_world->polygon_count; ++loop)
1469+ {
1470+ if (polygon->type==_polygon_is_platform)
1471+ {
1472+ /* Search and find the extra data. If it is not there, use the permutation for */
1473+ /* backwards compatibility! */
1474+
1475+ size_t platform_static_data_index;
1476+ for(platform_static_data_index = 0; platform_static_data_index<count; ++platform_static_data_index)
1477+ {
1478+ if (static_platforms[platform_static_data_index].polygon_index == loop)
1479+ {
1480+ new_platform(&static_platforms[platform_static_data_index], loop);
1481+ break;
1482+ }
1483+ }
1484+
1485+ /* DIdn't find it- use a standard platform */
1486+ if(platform_static_data_index==count)
1487+ {
1488+ polygon->permutation= 1;
1489+ new_platform(get_defaults_for_platform_type(polygon->permutation), loop);
1490+ }
1491+ }
1492+ ++polygon;
1493+ }
1494+}
1495+
1496+
1497+extern void unpack_lua_states(uint8*, size_t);
1498+
1499+/* Load a level from a wad-> mainly used by the net stuff. */
1500+bool process_map_wad(
1501+ struct wad_data *wad,
1502+ bool restoring_game,
1503+ short version)
1504+{
1505+ size_t data_length;
1506+ uint8 *data;
1507+ size_t count;
1508+ bool is_preprocessed_map= false;
1509+
1510+ assert(version==MARATHON_INFINITY_DATA_VERSION || version==MARATHON_TWO_DATA_VERSION || version==MARATHON_ONE_DATA_VERSION);
1511+
1512+ /* zero everything so no slots are used */
1513+ initialize_map_for_new_level();
1514+
1515+ /* Calculate the length (for reallocate map) */
1516+ allocate_map_structure_for_map(wad);
1517+
1518+ /* Extract points */
1519+ data= (uint8 *)extract_type_from_wad(wad, POINT_TAG, &data_length);
1520+ count= data_length/SIZEOF_world_point2d;
1521+ assert(data_length == count*SIZEOF_world_point2d);
1522+
1523+ if(count)
1524+ {
1525+ load_points(data, count);
1526+ } else {
1527+
1528+ data= (uint8 *)extract_type_from_wad(wad, ENDPOINT_DATA_TAG, &data_length);
1529+ count= data_length/SIZEOF_endpoint_data;
1530+ assert(data_length == count*SIZEOF_endpoint_data);
1531+ // assert(count>=0 && count<MAXIMUM_ENDPOINTS_PER_MAP);
1532+
1533+ /* Slam! */
1534+ unpack_endpoint_data(data,map_endpoints,count);
1535+ assert(count == static_cast<size_t>(static_cast<int16>(count)));
1536+ assert(0 <= static_cast<int16>(count));
1537+ dynamic_world->endpoint_count= static_cast<int16>(count);
1538+
1539+ if (version > MARATHON_ONE_DATA_VERSION)
1540+ is_preprocessed_map= true;
1541+ }
1542+
1543+ /* Extract lines */
1544+ data= (uint8 *)extract_type_from_wad(wad, LINE_TAG, &data_length);
1545+ count = data_length/SIZEOF_line_data;
1546+ assert(data_length == count*SIZEOF_line_data);
1547+ load_lines(data, count);
1548+
1549+ /* Order is important! */
1550+ data= (uint8 *)extract_type_from_wad(wad, SIDE_TAG, &data_length);
1551+ count = data_length/SIZEOF_side_data;
1552+ assert(data_length == count*SIZEOF_side_data);
1553+ load_sides(data, count, version);
1554+
1555+ /* Extract polygons */
1556+ data= (uint8 *)extract_type_from_wad(wad, POLYGON_TAG, &data_length);
1557+ count = data_length/SIZEOF_polygon_data;
1558+ assert(data_length == count*SIZEOF_polygon_data);
1559+ load_polygons(data, count, version);
1560+
1561+ /* Extract the lightsources */
1562+ if(restoring_game)
1563+ {
1564+ // Slurp them in
1565+ data= (uint8 *)extract_type_from_wad(wad, LIGHTSOURCE_TAG, &data_length);
1566+ count = data_length/SIZEOF_light_data;
1567+ assert(data_length == count*SIZEOF_light_data);
1568+ LightList.resize(count);
1569+ unpack_light_data(data,lights,count);
1570+ }
1571+ else
1572+ {
1573+ /* When you are restoring a game, the actual light structure is set. */
1574+ data= (uint8 *)extract_type_from_wad(wad, LIGHTSOURCE_TAG, &data_length);
1575+ if(version==MARATHON_ONE_DATA_VERSION)
1576+ {
1577+ /* We have an old style light */
1578+ count= data_length/SIZEOF_old_light_data;
1579+ assert(count*SIZEOF_old_light_data==data_length);
1580+ load_lights(data, count, version);
1581+ } else {
1582+ count= data_length/SIZEOF_static_light_data;
1583+ assert(count*SIZEOF_static_light_data==data_length);
1584+ load_lights(data, count, version);
1585+ }
1586+
1587+ // HACK!!!!!!!!!!!!!!! vulcan doesnÕt NONE .first_object field after adding scenery
1588+ {
1589+ for (count= 0; count<static_cast<size_t>(dynamic_world->polygon_count); ++count)
1590+ {
1591+ map_polygons[count].first_object= NONE;
1592+ }
1593+ }
1594+ }
1595+
1596+ /* Extract the annotations */
1597+ data= (uint8 *)extract_type_from_wad(wad, ANNOTATION_TAG, &data_length);
1598+ count = data_length/SIZEOF_map_annotation;
1599+ assert(data_length == count*SIZEOF_map_annotation);
1600+ load_annotations(data, count);
1601+
1602+ /* Extract the objects */
1603+ data= (uint8 *)extract_type_from_wad(wad, OBJECT_TAG, &data_length);
1604+ count = data_length/SIZEOF_map_object;
1605+ assert(data_length == count*static_cast<size_t>(SIZEOF_map_object));
1606+ load_objects(data, count);
1607+
1608+ /* Extract the map info data */
1609+ data= (uint8 *)extract_type_from_wad(wad, MAP_INFO_TAG, &data_length);
1610+ // LP change: made this more Pfhorte-friendly
1611+ assert(static_cast<size_t>(SIZEOF_static_data)==data_length
1612+ || static_cast<size_t>(SIZEOF_static_data-2)==data_length);
1613+ load_map_info(data);
1614+
1615+ /* Extract the game difficulty info.. */
1616+ data= (uint8 *)extract_type_from_wad(wad, ITEM_PLACEMENT_STRUCTURE_TAG, &data_length);
1617+ // In case of an absent placement chunk...
1618+ if (data_length == 0)
1619+ {
1620+ data = new uint8[2*MAXIMUM_OBJECT_TYPES*SIZEOF_object_frequency_definition];
1621+ memset(data,0,2*MAXIMUM_OBJECT_TYPES*SIZEOF_object_frequency_definition);
1622+ }
1623+ else
1624+ assert(data_length == 2*MAXIMUM_OBJECT_TYPES*SIZEOF_object_frequency_definition);
1625+ load_placement_data(data + MAXIMUM_OBJECT_TYPES*SIZEOF_object_frequency_definition, data);
1626+ if (data_length == 0)
1627+ delete []data;
1628+
1629+ /* Extract the terminal data. */
1630+ data= (uint8 *)extract_type_from_wad(wad, TERMINAL_DATA_TAG, &data_length);
1631+ load_terminal_data(data, data_length);
1632+
1633+ /* Extract the media definitions */
1634+ if(restoring_game)
1635+ {
1636+ // Slurp it in
1637+ data= (uint8 *)extract_type_from_wad(wad, MEDIA_TAG, &data_length);
1638+ count= data_length/SIZEOF_media_data;
1639+ assert(count*SIZEOF_media_data==data_length);
1640+ MediaList.resize(count);
1641+ unpack_media_data(data,medias,count);
1642+ }
1643+ else
1644+ {
1645+ data= (uint8 *)extract_type_from_wad(wad, MEDIA_TAG, &data_length);
1646+ count= data_length/SIZEOF_media_data;
1647+ assert(count*SIZEOF_media_data==data_length);
1648+ load_media(data, count);
1649+ }
1650+
1651+ /* Extract the ambient sound images */
1652+ data= (uint8 *)extract_type_from_wad(wad, AMBIENT_SOUND_TAG, &data_length);
1653+ count = data_length/SIZEOF_ambient_sound_image_data;
1654+ assert(data_length == count*SIZEOF_ambient_sound_image_data);
1655+ load_ambient_sound_images(data, count);
1656+ load_ambient_sound_images(data, data_length/SIZEOF_ambient_sound_image_data);
1657+
1658+ /* Extract the random sound images */
1659+ data= (uint8 *)extract_type_from_wad(wad, RANDOM_SOUND_TAG, &data_length);
1660+ count = data_length/SIZEOF_random_sound_image_data;
1661+ assert(data_length == count*SIZEOF_random_sound_image_data);
1662+ load_random_sound_images(data, count);
1663+
1664+ /* Extract embedded shapes */
1665+ data= (uint8 *)extract_type_from_wad(wad, SHAPE_PATCH_TAG, &data_length);
1666+ set_shapes_patch_data(data, data_length);
1667+
1668+ /* Extract MMLS */
1669+ data= (uint8 *)extract_type_from_wad(wad, MMLS_TAG, &data_length);
1670+ SetMMLS(data, data_length);
1671+
1672+ /* Extract LUAS */
1673+ data= (uint8 *)extract_type_from_wad(wad, LUAS_TAG, &data_length);
1674+ SetLUAS(data, data_length);
1675+
1676+ /* Extract saved Lua state */
1677+ data =(uint8 *)extract_type_from_wad(wad, LUA_STATE_TAG, &data_length);
1678+ unpack_lua_states(data, data_length);
1679+
1680+ // LP addition: load the physics-model chunks (all fixed-size)
1681+ bool PhysicsModelLoaded = false;
1682+
1683+ data= (uint8 *)extract_type_from_wad(wad, MONSTER_PHYSICS_TAG, &data_length);
1684+ count = data_length/SIZEOF_monster_definition;
1685+ assert(count*SIZEOF_monster_definition == data_length);
1686+ assert(count <= NUMBER_OF_MONSTER_TYPES);
1687+ if (data_length > 0)
1688+ {
1689+ if (!PhysicsModelLoaded) init_physics_wad_data();
1690+ PhysicsModelLoaded = true;
1691+ unpack_monster_definition(data,count);
1692+ }
1693+
1694+ data= (uint8 *)extract_type_from_wad(wad, EFFECTS_PHYSICS_TAG, &data_length);
1695+ count = data_length/SIZEOF_effect_definition;
1696+ assert(count*SIZEOF_effect_definition == data_length);
1697+ assert(count <= NUMBER_OF_EFFECT_TYPES);
1698+ if (data_length > 0)
1699+ {
1700+ if (!PhysicsModelLoaded) init_physics_wad_data();
1701+ PhysicsModelLoaded = true;
1702+ unpack_effect_definition(data,count);
1703+ }
1704+
1705+ data= (uint8 *)extract_type_from_wad(wad, PROJECTILE_PHYSICS_TAG, &data_length);
1706+ count = data_length/SIZEOF_projectile_definition;
1707+ assert(count*SIZEOF_projectile_definition == data_length);
1708+ assert(count <= NUMBER_OF_PROJECTILE_TYPES);
1709+ if (data_length > 0)
1710+ {
1711+ if (!PhysicsModelLoaded) init_physics_wad_data();
1712+ PhysicsModelLoaded = true;
1713+ unpack_projectile_definition(data,count);
1714+ }
1715+
1716+ data= (uint8 *)extract_type_from_wad(wad, PHYSICS_PHYSICS_TAG, &data_length);
1717+ count = data_length/SIZEOF_physics_constants;
1718+ assert(count*SIZEOF_physics_constants == data_length);
1719+ assert(count <= get_number_of_physics_models());
1720+ if (data_length > 0)
1721+ {
1722+ if (!PhysicsModelLoaded) init_physics_wad_data();
1723+ PhysicsModelLoaded = true;
1724+ unpack_physics_constants(data,count);
1725+ }
1726+
1727+ data= (uint8 *)extract_type_from_wad(wad, WEAPONS_PHYSICS_TAG, &data_length);
1728+ count = data_length/SIZEOF_weapon_definition;
1729+ assert(count*SIZEOF_weapon_definition == data_length);
1730+ assert(count <= get_number_of_weapon_types());
1731+ if (data_length > 0)
1732+ {
1733+ if (!PhysicsModelLoaded) init_physics_wad_data();
1734+ PhysicsModelLoaded = true;
1735+ unpack_weapon_definition(data,count);
1736+ }
1737+
1738+ // LP addition: Reload the physics model if it had been loaded in the previous level,
1739+ // but not in the current level. This avoids the persistent-physics bug.
1740+ // ghs: always reload the physics model if there isn't one merged
1741+ if (PhysicsModelLoadedEarlier && !PhysicsModelLoaded && !game_is_networked)
1742+ import_definition_structures();
1743+ PhysicsModelLoadedEarlier = PhysicsModelLoaded;
1744+
1745+ /* If we are restoring the game, then we need to add the dynamic data */
1746+ if(restoring_game)
1747+ {
1748+ // Slurp it all in...
1749+ data= (uint8 *)extract_type_from_wad(wad, MAP_INDEXES_TAG, &data_length);
1750+ count= data_length/sizeof(short);
1751+ assert(count*int32(sizeof(short))==data_length);
1752+ MapIndexList.resize(count);
1753+ StreamToList(data,map_indexes,count);
1754+
1755+ data= (uint8 *)extract_type_from_wad(wad, PLAYER_STRUCTURE_TAG, &data_length);
1756+ count= data_length/SIZEOF_player_data;
1757+ assert(count*SIZEOF_player_data==data_length);
1758+ unpack_player_data(data,players,count);
1759+ team_damage_from_player_data();
1760+
1761+ data= (uint8 *)extract_type_from_wad(wad, DYNAMIC_STRUCTURE_TAG, &data_length);
1762+ assert(data_length == SIZEOF_dynamic_data);
1763+ unpack_dynamic_data(data,dynamic_world,1);
1764+
1765+ data= (uint8 *)extract_type_from_wad(wad, OBJECT_STRUCTURE_TAG, &data_length);
1766+ count= data_length/SIZEOF_object_data;
1767+ assert(count*SIZEOF_object_data==data_length);
1768+ vassert(count <= MAXIMUM_OBJECTS_PER_MAP,
1769+ csprintf(temporary,"Number of map objects %lu > limit %u",count,MAXIMUM_OBJECTS_PER_MAP));
1770+ unpack_object_data(data,objects,count);
1771+
1772+ // Unpacking is E-Z here...
1773+ data= (uint8 *)extract_type_from_wad(wad, AUTOMAP_LINES, &data_length);
1774+ memcpy(automap_lines,data,data_length);
1775+ data= (uint8 *)extract_type_from_wad(wad, AUTOMAP_POLYGONS, &data_length);
1776+ memcpy(automap_polygons,data,data_length);
1777+
1778+ data= (uint8 *)extract_type_from_wad(wad, MONSTERS_STRUCTURE_TAG, &data_length);
1779+ count= data_length/SIZEOF_monster_data;
1780+ assert(count*SIZEOF_monster_data==data_length);
1781+ vassert(count <= MAXIMUM_MONSTERS_PER_MAP,
1782+ csprintf(temporary,"Number of monsters %lu > limit %u",count,MAXIMUM_MONSTERS_PER_MAP));
1783+ unpack_monster_data(data,monsters,count);
1784+
1785+ data= (uint8 *)extract_type_from_wad(wad, EFFECTS_STRUCTURE_TAG, &data_length);
1786+ count= data_length/SIZEOF_effect_data;
1787+ assert(count*SIZEOF_effect_data==data_length);
1788+ vassert(count <= MAXIMUM_EFFECTS_PER_MAP,
1789+ csprintf(temporary,"Number of effects %lu > limit %u",count,MAXIMUM_EFFECTS_PER_MAP));
1790+ unpack_effect_data(data,effects,count);
1791+
1792+ data= (uint8 *)extract_type_from_wad(wad, PROJECTILES_STRUCTURE_TAG, &data_length);
1793+ count= data_length/SIZEOF_projectile_data;
1794+ assert(count*SIZEOF_projectile_data==data_length);
1795+ vassert(count <= MAXIMUM_PROJECTILES_PER_MAP,
1796+ csprintf(temporary,"Number of projectiles %lu > limit %u",count,MAXIMUM_PROJECTILES_PER_MAP));
1797+ unpack_projectile_data(data,projectiles,count);
1798+
1799+ data= (uint8 *)extract_type_from_wad(wad, PLATFORM_STRUCTURE_TAG, &data_length);
1800+ count= data_length/SIZEOF_platform_data;
1801+ assert(count*SIZEOF_platform_data==data_length);
1802+ PlatformList.resize(count);
1803+ unpack_platform_data(data,platforms,count);
1804+
1805+ data= (uint8 *)extract_type_from_wad(wad, WEAPON_STATE_TAG, &data_length);
1806+ count= data_length/SIZEOF_player_weapon_data;
1807+ assert(count*SIZEOF_player_weapon_data==data_length);
1808+ unpack_player_weapon_data(data,count);
1809+
1810+ data= (uint8 *)extract_type_from_wad(wad, TERMINAL_STATE_TAG, &data_length);
1811+ count= data_length/SIZEOF_player_terminal_data;
1812+ assert(count*SIZEOF_player_terminal_data==data_length);
1813+ unpack_player_terminal_data(data,count);
1814+
1815+ complete_restoring_level(wad);
1816+ } else {
1817+ uint8 *map_index_data;
1818+ size_t map_index_count;
1819+ uint8 *platform_structures;
1820+ size_t platform_structure_count;
1821+
1822+ if(version==MARATHON_ONE_DATA_VERSION)
1823+ {
1824+ /* Force precalculation */
1825+ map_index_data= NULL;
1826+ map_index_count= 0;
1827+ } else {
1828+ map_index_data= (uint8 *)extract_type_from_wad(wad, MAP_INDEXES_TAG, &data_length);
1829+ map_index_count= data_length/sizeof(short);
1830+ assert(map_index_count*sizeof(short)==data_length);
1831+ }
1832+
1833+ assert(is_preprocessed_map&&map_index_count || !is_preprocessed_map&&!map_index_count);
1834+
1835+ data= (uint8 *)extract_type_from_wad(wad, PLATFORM_STATIC_DATA_TAG, &data_length);
1836+ count= data_length/SIZEOF_static_platform_data;
1837+ assert(count*SIZEOF_static_platform_data==data_length);
1838+
1839+ platform_structures= (uint8 *)extract_type_from_wad(wad, PLATFORM_STRUCTURE_TAG, &data_length);
1840+ platform_structure_count= data_length/SIZEOF_platform_data;
1841+ assert(platform_structure_count*SIZEOF_platform_data==data_length);
1842+
1843+ complete_loading_level((short *) map_index_data, map_index_count,
1844+ data, count, platform_structures,
1845+ platform_structure_count, version);
1846+
1847+ }
1848+
1849+ /* ... and bail */
1850+ return true;
1851+}
1852+
1853+static void allocate_map_structure_for_map(
1854+ struct wad_data *wad)
1855+{
1856+ size_t data_length;
1857+ size_t line_count, polygon_count, side_count, endpoint_count;
1858+
1859+ /* Extract points */
1860+ extract_type_from_wad(wad, POINT_TAG, &data_length);
1861+ endpoint_count= data_length/SIZEOF_world_point2d;
1862+ if(endpoint_count*SIZEOF_world_point2d!=data_length) alert_user(fatalError, strERRORS, corruptedMap, 0x7074); // 'pt'
1863+
1864+ if(!endpoint_count)
1865+ {
1866+ extract_type_from_wad(wad, ENDPOINT_DATA_TAG, &data_length);
1867+ endpoint_count= data_length/SIZEOF_endpoint_data;
1868+ if(endpoint_count*SIZEOF_endpoint_data!=data_length) alert_user(fatalError, strERRORS, corruptedMap, 0x6570); // 'ep'
1869+ }
1870+
1871+ /* Extract lines */
1872+ extract_type_from_wad(wad, LINE_TAG, &data_length);
1873+ line_count= data_length/SIZEOF_line_data;
1874+ if(line_count*SIZEOF_line_data!=data_length) alert_user(fatalError, strERRORS, corruptedMap, 0x6c69); // 'li'
1875+
1876+ /* Sides.. */
1877+ extract_type_from_wad(wad, SIDE_TAG, &data_length);
1878+ side_count= data_length/SIZEOF_side_data;
1879+ if(side_count*SIZEOF_side_data!=data_length) alert_user(fatalError, strERRORS, corruptedMap, 0x7369); // 'si'
1880+
1881+ /* Extract polygons */
1882+ extract_type_from_wad(wad, POLYGON_TAG, &data_length);
1883+ polygon_count= data_length/SIZEOF_polygon_data;
1884+ if(polygon_count*SIZEOF_polygon_data!=data_length) alert_user(fatalError, strERRORS, corruptedMap, 0x7369); // 'si'
1885+
1886+ allocate_map_for_counts(polygon_count, side_count, endpoint_count, line_count);
1887+}
1888+
1889+/* Note that we assume the redundant data has already been recalculated... */
1890+static void load_redundant_map_data(
1891+ short *redundant_data,
1892+ size_t count)
1893+{
1894+ if (redundant_data)
1895+ {
1896+ // assert(redundant_data && map_indexes);
1897+ uint8 *Stream = (uint8 *)redundant_data;
1898+ MapIndexList.resize(count);
1899+ StreamToList(Stream,map_indexes,count);
1900+ assert(count == static_cast<size_t>(static_cast<int16>(count)));
1901+ assert(0 <= static_cast<int16>(count));
1902+ dynamic_world->map_index_count= static_cast<int16>(count);
1903+ }
1904+ else
1905+ {
1906+ recalculate_redundant_map();
1907+ precalculate_map_indexes();
1908+ }
1909+}
1910+
1911+void load_terminal_data(
1912+ uint8 *data,
1913+ size_t length)
1914+{
1915+ /* I would really like it if I could get these into computer_interface.c statically */
1916+ unpack_map_terminal_data(data,length);
1917+}
1918+
1919+static void scan_and_add_scenery(
1920+ void)
1921+{
1922+ short ii;
1923+ struct map_object *saved_object;
1924+
1925+ saved_object= saved_objects;
1926+ for(ii=0; ii<dynamic_world->initial_objects_count; ++ii)
1927+ {
1928+ if (saved_object->type==_saved_object)
1929+ {
1930+ struct object_location location;
1931+
1932+ location.p= saved_object->location;
1933+ location.flags= saved_object->flags;
1934+ location.yaw= saved_object->facing;
1935+ location.polygon_index= saved_object->polygon_index;
1936+ new_scenery(&location, saved_object->index);
1937+ }
1938+
1939+ ++saved_object;
1940+ }
1941+}
1942+
1943+struct save_game_data
1944+{
1945+ uint32 tag;
1946+ short unit_size;
1947+ bool loaded_by_level;
1948+};
1949+
1950+#define NUMBER_OF_EXPORT_ARRAYS (sizeof(export_data)/sizeof(struct save_game_data))
1951+save_game_data export_data[]=
1952+{
1953+ { POINT_TAG, SIZEOF_world_point2d, true },
1954+ { LINE_TAG, SIZEOF_line_data, true },
1955+ { POLYGON_TAG, SIZEOF_polygon_data, true },
1956+ { SIDE_TAG, SIZEOF_side_data, true },
1957+ { LIGHTSOURCE_TAG, SIZEOF_static_light_data, true, },
1958+ { ANNOTATION_TAG, SIZEOF_map_annotation, true },
1959+ { OBJECT_TAG, SIZEOF_map_object, true },
1960+ { MAP_INFO_TAG, SIZEOF_static_data, true },
1961+ { ITEM_PLACEMENT_STRUCTURE_TAG, SIZEOF_object_frequency_definition, true },
1962+ { PLATFORM_STATIC_DATA_TAG, SIZEOF_static_platform_data, true },
1963+ { TERMINAL_DATA_TAG, sizeof(byte), true },
1964+ { MEDIA_TAG, SIZEOF_media_data, true }, // false },
1965+ { AMBIENT_SOUND_TAG, SIZEOF_ambient_sound_image_data, true },
1966+ { RANDOM_SOUND_TAG, SIZEOF_random_sound_image_data, true },
1967+ { SHAPE_PATCH_TAG, sizeof(byte), true },
1968+// { PLATFORM_STRUCTURE_TAG, SIZEOF_platform_data, true },
1969+};
1970+
1971+#define NUMBER_OF_SAVE_ARRAYS (sizeof(save_data)/sizeof(struct save_game_data))
1972+struct save_game_data save_data[]=
1973+{
1974+ { ENDPOINT_DATA_TAG, SIZEOF_endpoint_data, true },
1975+ { LINE_TAG, SIZEOF_line_data, true },
1976+ { SIDE_TAG, SIZEOF_side_data, true },
1977+ { POLYGON_TAG, SIZEOF_polygon_data, true },
1978+ { LIGHTSOURCE_TAG, SIZEOF_light_data, true }, // false },
1979+ { ANNOTATION_TAG, SIZEOF_map_annotation, true },
1980+ { OBJECT_TAG, SIZEOF_map_object, true },
1981+ { MAP_INFO_TAG, SIZEOF_static_data, true },
1982+ { ITEM_PLACEMENT_STRUCTURE_TAG, SIZEOF_object_frequency_definition, true },
1983+ { MEDIA_TAG, SIZEOF_media_data, true }, // false },
1984+ { AMBIENT_SOUND_TAG, SIZEOF_ambient_sound_image_data, true },
1985+ { RANDOM_SOUND_TAG, SIZEOF_random_sound_image_data, true },
1986+ { TERMINAL_DATA_TAG, sizeof(byte), true },
1987+
1988+ // LP addition: handling of physics models
1989+ { MONSTER_PHYSICS_TAG, SIZEOF_monster_definition, true},
1990+ { EFFECTS_PHYSICS_TAG, SIZEOF_effect_definition, true},
1991+ { PROJECTILE_PHYSICS_TAG, SIZEOF_projectile_definition, true},
1992+ { PHYSICS_PHYSICS_TAG, SIZEOF_physics_constants, true},
1993+ { WEAPONS_PHYSICS_TAG, SIZEOF_weapon_definition, true},
1994+
1995+ // GHS: save the new embedded shapes
1996+ { SHAPE_PATCH_TAG, sizeof(byte), true },
1997+
1998+ { MMLS_TAG, sizeof(byte), true },
1999+ { LUAS_TAG, sizeof(byte), true },
2000+
2001+ { MAP_INDEXES_TAG, sizeof(short), true }, // false },
2002+ { PLAYER_STRUCTURE_TAG, SIZEOF_player_data, true }, // false },
2003+ { DYNAMIC_STRUCTURE_TAG, SIZEOF_dynamic_data, true }, // false },
2004+ { OBJECT_STRUCTURE_TAG, SIZEOF_object_data, true }, // false },
2005+ { AUTOMAP_LINES, sizeof(byte), true }, // false },
2006+ { AUTOMAP_POLYGONS, sizeof(byte), true }, // false },
2007+ { MONSTERS_STRUCTURE_TAG, SIZEOF_monster_data, true }, // false },
2008+ { EFFECTS_STRUCTURE_TAG, SIZEOF_effect_data, true }, // false },
2009+ { PROJECTILES_STRUCTURE_TAG, SIZEOF_projectile_data, true }, // false },
2010+ { PLATFORM_STRUCTURE_TAG, SIZEOF_platform_data, true }, // false },
2011+ { WEAPON_STATE_TAG, SIZEOF_player_weapon_data, true }, // false },
2012+ { TERMINAL_STATE_TAG, SIZEOF_player_terminal_data, true }, // false }
2013+
2014+ { LUA_STATE_TAG, sizeof(byte), true },
2015+};
2016+
2017+static uint8 *export_tag_to_global_array_and_size(
2018+ uint32 tag,
2019+ size_t *size
2020+ )
2021+{
2022+ uint8 *array = NULL;
2023+ size_t unit_size = 0;
2024+ size_t count = 0;
2025+ unsigned index;
2026+
2027+ for (index=0; index<NUMBER_OF_EXPORT_ARRAYS; ++index)
2028+ {
2029+ if(export_data[index].tag==tag)
2030+ {
2031+ unit_size= export_data[index].unit_size;
2032+ break;
2033+ }
2034+ }
2035+ assert(index != NUMBER_OF_EXPORT_ARRAYS);
2036+
2037+ switch (tag)
2038+ {
2039+ case POINT_TAG:
2040+ count = dynamic_world->endpoint_count;
2041+ break;
2042+
2043+ case LIGHTSOURCE_TAG:
2044+ count = dynamic_world->light_count;
2045+ break;
2046+
2047+ case PLATFORM_STATIC_DATA_TAG:
2048+ count = dynamic_world->platform_count;
2049+ break;
2050+
2051+ case POLYGON_TAG:
2052+ count = dynamic_world->polygon_count;
2053+ break;
2054+
2055+ default:
2056+ assert(false);
2057+ break;
2058+ }
2059+
2060+ // Allocate a temporary packed-data chunk;
2061+ // indicate if there is nothing to be written
2062+ *size= count*unit_size;
2063+ if (*size > 0)
2064+ array = new byte[*size];
2065+ else
2066+ return NULL;
2067+
2068+ objlist_clear(array, *size);
2069+
2070+ // An OK-to-alter version of that array pointer
2071+ uint8 *temp_array = array;
2072+
2073+ switch (tag)
2074+ {
2075+ case POINT_TAG:
2076+ for (size_t loop = 0; loop < count; ++loop)
2077+ {
2078+ world_point2d& vertex = map_endpoints[loop].vertex;
2079+ ValueToStream(temp_array, vertex.x);
2080+ ValueToStream(temp_array, vertex.y);
2081+ }
2082+ break;
2083+
2084+ case LIGHTSOURCE_TAG:
2085+ for (size_t loop = 0; loop < count; ++loop)
2086+ {
2087+ temp_array = pack_static_light_data(temp_array, &lights[loop].static_data, 1);
2088+ }
2089+ break;
2090+
2091+ case PLATFORM_STATIC_DATA_TAG:
2092+ if (static_platforms.size() == count)
2093+ {
2094+ // export them directly as they came in
2095+ pack_static_platform_data(array, &static_platforms[0], count);
2096+ }
2097+ else
2098+ {
2099+ for (size_t loop = 0; loop < count; ++loop)
2100+ {
2101+ // ghs: this belongs somewhere else
2102+ static_platform_data platform;
2103+ obj_clear(platform);
2104+ platform.type = platforms[loop].type;
2105+ platform.speed = platforms[loop].speed;
2106+ platform.delay = platforms[loop].delay;
2107+ if (PLATFORM_GOES_BOTH_WAYS(&platforms[loop]))
2108+ {
2109+ platform.maximum_height = platforms[loop].maximum_ceiling_height;
2110+ platform.minimum_height = platforms[loop].minimum_floor_height;
2111+ }
2112+ else if (PLATFORM_COMES_FROM_FLOOR(&platforms[loop]))
2113+ {
2114+ platform.maximum_height = platforms[loop].maximum_floor_height;
2115+ platform.minimum_height = platforms[loop].minimum_floor_height;
2116+ }
2117+ else
2118+ {
2119+ platform.maximum_height = platforms[loop].maximum_ceiling_height;
2120+ platform.minimum_height = platforms[loop].minimum_floor_height;
2121+ }
2122+ platform.static_flags = platforms[loop].static_flags;
2123+ platform.polygon_index = platforms[loop].polygon_index;
2124+ platform.tag = platforms[loop].tag;
2125+
2126+ temp_array = pack_static_platform_data(temp_array, &platform, 1);
2127+ }
2128+ }
2129+ break;
2130+
2131+ case POLYGON_TAG:
2132+ for (size_t loop = 0; loop < count; ++loop)
2133+ {
2134+ // Forge visual mode crashes if we don't do this
2135+ polygon_data polygon = PolygonList[loop];
2136+ polygon.first_object = NONE;
2137+ temp_array = pack_polygon_data(temp_array, &polygon, 1);
2138+ }
2139+ break;
2140+
2141+ default:
2142+ assert(false);
2143+ break;
2144+ }
2145+
2146+ return array;
2147+}
2148+
2149+extern size_t save_lua_states();
2150+extern void pack_lua_states(uint8*, size_t);
2151+
2152+
2153+/* the sizes are the sizes to save in the file, be aware! */
2154+static uint8 *tag_to_global_array_and_size(
2155+ uint32 tag,
2156+ size_t *size
2157+ )
2158+{
2159+ uint8 *array= NULL;
2160+ size_t unit_size = 0;
2161+ size_t count = 0;
2162+ unsigned index;
2163+
2164+ for (index=0; index<NUMBER_OF_SAVE_ARRAYS; ++index)
2165+ {
2166+ if(save_data[index].tag==tag)
2167+ {
2168+ unit_size= save_data[index].unit_size;
2169+ break;
2170+ }
2171+ }
2172+ assert(index != NUMBER_OF_SAVE_ARRAYS);
2173+
2174+ // LP: had fixed off-by-one error in medias saving,
2175+ // and had added physics-model saving
2176+
2177+ switch (tag)
2178+ {
2179+ case ENDPOINT_DATA_TAG:
2180+ count= dynamic_world->endpoint_count;
2181+ break;
2182+ case LINE_TAG:
2183+ count= dynamic_world->line_count;
2184+ break;
2185+ case SIDE_TAG:
2186+ count= dynamic_world->side_count;
2187+ break;
2188+ case POLYGON_TAG:
2189+ count= dynamic_world->polygon_count;
2190+ break;
2191+ case LIGHTSOURCE_TAG:
2192+ count= dynamic_world->light_count;
2193+ break;
2194+ case ANNOTATION_TAG:
2195+ count= dynamic_world->default_annotation_count;
2196+ break;
2197+ case OBJECT_TAG:
2198+ count= dynamic_world->initial_objects_count;
2199+ break;
2200+ case MAP_INFO_TAG:
2201+ count= 1;
2202+ break;
2203+ case PLAYER_STRUCTURE_TAG:
2204+ count= dynamic_world->player_count;
2205+ break;
2206+ case DYNAMIC_STRUCTURE_TAG:
2207+ count= 1;
2208+ break;
2209+ case OBJECT_STRUCTURE_TAG:
2210+ count= dynamic_world->object_count;
2211+ break;
2212+ case MAP_INDEXES_TAG:
2213+ count= static_cast<unsigned short>(dynamic_world->map_index_count);
2214+ break;
2215+ case AUTOMAP_LINES:
2216+ count= (dynamic_world->line_count/8+((dynamic_world->line_count%8)?1:0));
2217+ break;
2218+ case AUTOMAP_POLYGONS:
2219+ count= (dynamic_world->polygon_count/8+((dynamic_world->polygon_count%8)?1:0));
2220+ break;
2221+ case MONSTERS_STRUCTURE_TAG:
2222+ count= dynamic_world->monster_count;
2223+ break;
2224+ case EFFECTS_STRUCTURE_TAG:
2225+ count= dynamic_world->effect_count;
2226+ break;
2227+ case PROJECTILES_STRUCTURE_TAG:
2228+ count= dynamic_world->projectile_count;
2229+ break;
2230+ case MEDIA_TAG:
2231+ count= count_number_of_medias_used();
2232+ break;
2233+ case ITEM_PLACEMENT_STRUCTURE_TAG:
2234+ count= 2*MAXIMUM_OBJECT_TYPES;
2235+ break;
2236+ case PLATFORM_STRUCTURE_TAG:
2237+ count= dynamic_world->platform_count;
2238+ break;
2239+ case AMBIENT_SOUND_TAG:
2240+ count= dynamic_world->ambient_sound_image_count;
2241+ break;
2242+ case RANDOM_SOUND_TAG:
2243+ count= dynamic_world->random_sound_image_count;
2244+ break;
2245+ case TERMINAL_DATA_TAG:
2246+ count= calculate_packed_terminal_data_length();
2247+ break;
2248+ case WEAPON_STATE_TAG:
2249+ count= dynamic_world->player_count;
2250+ break;
2251+ case TERMINAL_STATE_TAG:
2252+ count= dynamic_world->player_count;
2253+ break;
2254+ case MONSTER_PHYSICS_TAG:
2255+ count= NUMBER_OF_MONSTER_TYPES;
2256+ break;
2257+ case EFFECTS_PHYSICS_TAG:
2258+ count= NUMBER_OF_EFFECT_TYPES;
2259+ break;
2260+ case PROJECTILE_PHYSICS_TAG:
2261+ count= NUMBER_OF_PROJECTILE_TYPES;
2262+ break;
2263+ case PHYSICS_PHYSICS_TAG:
2264+ count= get_number_of_physics_models();
2265+ break;
2266+ case WEAPONS_PHYSICS_TAG:
2267+ count= get_number_of_weapon_types();
2268+ break;
2269+ case SHAPE_PATCH_TAG:
2270+ get_shapes_patch_data(count);
2271+ break;
2272+ case MMLS_TAG:
2273+ GetMMLS(count);
2274+ break;
2275+ case LUAS_TAG:
2276+ GetLUAS(count);
2277+ break;
2278+ case LUA_STATE_TAG:
2279+ count= save_lua_states();
2280+ break;
2281+ default:
2282+ assert(false);
2283+ break;
2284+ }
2285+
2286+ // Allocate a temporary packed-data chunk;
2287+ // indicate if there is nothing to be written
2288+ *size= count*unit_size;
2289+ if (*size > 0)
2290+ array = new byte[*size];
2291+ else
2292+ return NULL;
2293+
2294+ objlist_clear(array, *size);
2295+
2296+ // An OK-to-alter version of that array pointer
2297+ uint8 *temp_array = array;
2298+
2299+ switch (tag)
2300+ {
2301+ case ENDPOINT_DATA_TAG:
2302+ pack_endpoint_data(array,map_endpoints,count);
2303+ break;
2304+ case LINE_TAG:
2305+ pack_line_data(array,map_lines,count);
2306+ break;
2307+ case SIDE_TAG:
2308+ pack_side_data(array,map_sides,count);
2309+ break;
2310+ case POLYGON_TAG:
2311+ pack_polygon_data(array,map_polygons,count);
2312+ break;
2313+ case LIGHTSOURCE_TAG:
2314+ pack_light_data(array,lights,count);
2315+ break;
2316+ case ANNOTATION_TAG:
2317+ pack_map_annotation(array,map_annotations,count);
2318+ break;
2319+ case OBJECT_TAG:
2320+ pack_map_object(array,saved_objects,count);
2321+ break;
2322+ case MAP_INFO_TAG:
2323+ pack_static_data(array,static_world,count);
2324+ break;
2325+ case PLAYER_STRUCTURE_TAG:
2326+ pack_player_data(array,players,count);
2327+ break;
2328+ case DYNAMIC_STRUCTURE_TAG:
2329+ pack_dynamic_data(array,dynamic_world,count);
2330+ break;
2331+ case OBJECT_STRUCTURE_TAG:
2332+ pack_object_data(array,objects,count);
2333+ break;
2334+ case MAP_INDEXES_TAG:
2335+ ListToStream(temp_array,map_indexes,count); // E-Z packing here...
2336+ break;
2337+ case AUTOMAP_LINES:
2338+ memcpy(array,automap_lines,*size);
2339+ break;
2340+ case AUTOMAP_POLYGONS:
2341+ memcpy(array,automap_polygons,*size);
2342+ break;
2343+ case MONSTERS_STRUCTURE_TAG:
2344+ pack_monster_data(array,monsters,count);
2345+ break;
2346+ case EFFECTS_STRUCTURE_TAG:
2347+ pack_effect_data(array,effects,count);
2348+ break;
2349+ case PROJECTILES_STRUCTURE_TAG:
2350+ pack_projectile_data(array,projectiles,count);
2351+ break;
2352+ case MEDIA_TAG:
2353+ pack_media_data(array,medias,count);
2354+ break;
2355+ case ITEM_PLACEMENT_STRUCTURE_TAG:
2356+ pack_object_frequency_definition(array,get_placement_info(),count);
2357+ break;
2358+ case PLATFORM_STRUCTURE_TAG:
2359+ pack_platform_data(array,platforms,count);
2360+ break;
2361+ case AMBIENT_SOUND_TAG:
2362+ pack_ambient_sound_image_data(array,ambient_sound_images,count);
2363+ break;
2364+ case RANDOM_SOUND_TAG:
2365+ pack_random_sound_image_data(array,random_sound_images,count);
2366+ break;
2367+ case TERMINAL_DATA_TAG:
2368+ pack_map_terminal_data(array,count);
2369+ break;
2370+ case WEAPON_STATE_TAG:
2371+ pack_player_weapon_data(array,count);
2372+ break;
2373+ case TERMINAL_STATE_TAG:
2374+ pack_player_terminal_data(array,count);
2375+ break;
2376+ case MONSTER_PHYSICS_TAG:
2377+ pack_monster_definition(array,count);
2378+ break;
2379+ case EFFECTS_PHYSICS_TAG:
2380+ pack_effect_definition(array,count);
2381+ break;
2382+ case PROJECTILE_PHYSICS_TAG:
2383+ pack_projectile_definition(array,count);
2384+ break;
2385+ case PHYSICS_PHYSICS_TAG:
2386+ pack_physics_constants(array,count);
2387+ break;
2388+ case WEAPONS_PHYSICS_TAG:
2389+ pack_weapon_definition(array,count);
2390+ break;
2391+ case SHAPE_PATCH_TAG:
2392+ memcpy(array, get_shapes_patch_data(count), count);
2393+ break;
2394+ case MMLS_TAG:
2395+ memcpy(array, GetMMLS(count), count);
2396+ break;
2397+ case LUAS_TAG:
2398+ memcpy(array, GetLUAS(count), count);
2399+ break;
2400+ case LUA_STATE_TAG:
2401+ pack_lua_states(array, count);
2402+ break;
2403+ default:
2404+ assert(false);
2405+ break;
2406+ }
2407+
2408+ return array;
2409+}
2410+
2411+static wad_data *build_export_wad(wad_header *header, int32 *length)
2412+{
2413+ struct wad_data *wad= NULL;
2414+ uint8 *array_to_slam;
2415+ size_t size;
2416+
2417+ wad= create_empty_wad();
2418+ if(wad)
2419+ {
2420+ recalculate_map_counts();
2421+
2422+ // try to divine initial platform/polygon states
2423+ vector<platform_data> SavedPlatforms = PlatformList;
2424+ vector<polygon_data> SavedPolygons = PolygonList;
2425+ vector<line_data> SavedLines = LineList;
2426+
2427+ for (size_t loop = 0; loop < PlatformList.size(); ++loop)
2428+ {
2429+ platform_data *platform = &PlatformList[loop];
2430+ // reset the polygon heights
2431+ if (PLATFORM_COMES_FROM_FLOOR(platform))
2432+ {
2433+ platform->floor_height = platform->minimum_floor_height;
2434+ PolygonList[platform->polygon_index].floor_height = platform->floor_height;
2435+ }
2436+ if (PLATFORM_COMES_FROM_CEILING(platform))
2437+ {
2438+ platform->ceiling_height = platform->maximum_ceiling_height;
2439+ PolygonList[platform->polygon_index].ceiling_height = platform->ceiling_height;
2440+ }
2441+ }
2442+
2443+ for (size_t loop = 0; loop < LineList.size(); ++loop)
2444+ {
2445+ line_data *line = &LineList[loop];
2446+ if (LINE_IS_VARIABLE_ELEVATION(line))
2447+ {
2448+ SET_LINE_VARIABLE_ELEVATION(line, false);
2449+ SET_LINE_SOLIDITY(line, false);
2450+ SET_LINE_TRANSPARENCY(line, true);
2451+ }
2452+ }
2453+
2454+ for(unsigned loop= 0; loop<NUMBER_OF_EXPORT_ARRAYS; ++loop)
2455+ {
2456+ /* If there is a conversion function, let it handle it */
2457+ switch (export_data[loop].tag)
2458+ {
2459+ case POINT_TAG:
2460+ case LIGHTSOURCE_TAG:
2461+ case PLATFORM_STATIC_DATA_TAG:
2462+ case POLYGON_TAG:
2463+ array_to_slam= export_tag_to_global_array_and_size(export_data[loop].tag, &size);
2464+ break;
2465+ default:
2466+ array_to_slam= tag_to_global_array_and_size(export_data[loop].tag, &size);
2467+ }
2468+
2469+ /* Add it to the wad.. */
2470+ if(size)
2471+ {
2472+ wad= append_data_to_wad(wad, export_data[loop].tag, array_to_slam, size, 0);
2473+ delete []array_to_slam;
2474+ }
2475+ }
2476+
2477+ PlatformList = SavedPlatforms;
2478+ PolygonList = SavedPolygons;
2479+ LineList = SavedLines;
2480+
2481+ if(wad) *length= calculate_wad_length(header, wad);
2482+ }
2483+
2484+ return wad;
2485+}
2486+
2487+/* Build the wad, with all the crap */
2488+static struct wad_data *build_save_game_wad(
2489+ struct wad_header *header,
2490+ int32 *length)
2491+{
2492+ struct wad_data *wad= NULL;
2493+ uint8 *array_to_slam;
2494+ size_t size;
2495+
2496+ wad= create_empty_wad();
2497+ if(wad)
2498+ {
2499+ recalculate_map_counts();
2500+ for(unsigned loop= 0; loop<NUMBER_OF_SAVE_ARRAYS; ++loop)
2501+ {
2502+ /* If there is a conversion function, let it handle it */
2503+ array_to_slam= tag_to_global_array_and_size(save_data[loop].tag, &size);
2504+
2505+ /* Add it to the wad.. */
2506+ if(size)
2507+ {
2508+ wad= append_data_to_wad(wad, save_data[loop].tag, array_to_slam, size, 0);
2509+ delete []array_to_slam;
2510+ }
2511+ }
2512+ if(wad) *length= calculate_wad_length(header, wad);
2513+ }
2514+
2515+ return wad;
2516+}
2517+
2518+/* Load and slam all of the arrays */
2519+static void complete_restoring_level(
2520+ struct wad_data *wad)
2521+{
2522+ ok_to_reset_scenery_solidity = false;
2523+ /* Loading games needs this done. */
2524+ reset_action_queues();
2525+}
2526+
2527+
2528+/* CP Addition: get_map_file returns a pointer to the current map file */
2529+FileSpecifier& get_map_file()
2530+{
2531+ return MapFileSpec;
2532+}
2533+
2534+void level_has_embedded_physics_lua(int Level, bool& HasPhysics, bool& HasLua)
2535+{
2536+ // load the wad file and look for chunks !!??
2537+ wad_header header;
2538+ wad_data* wad;
2539+ OpenedFile MapFile;
2540+ if (open_wad_file_for_reading(get_map_file(), MapFile))
2541+ {
2542+ if (read_wad_header(MapFile, &header))
2543+ {
2544+ wad = read_indexed_wad_from_file(MapFile, &header, Level, true);
2545+ if (wad)
2546+ {
2547+ size_t data_length;
2548+ extract_type_from_wad(wad, PHYSICS_PHYSICS_TAG, &data_length);
2549+ HasPhysics = data_length > 0;
2550+
2551+ extract_type_from_wad(wad, LUAS_TAG, &data_length);
2552+ HasLua = data_length > 0;
2553+ free_wad(wad);
2554+ }
2555+ }
2556+ close_wad_file(MapFile);
2557+ }
2558+}
2559+
2560+
2561+/*
2562+ * Unpacking/packing functions
2563+ */
2564+
2565+static uint8 *unpack_directory_data(uint8 *Stream, directory_data *Objects, size_t Count)
2566+{
2567+ uint8* S = Stream;
2568+ directory_data* ObjPtr = Objects;
2569+
2570+ for (size_t k = 0; k < Count; k++, ObjPtr++)
2571+ {
2572+ StreamToValue(S,ObjPtr->mission_flags);
2573+ StreamToValue(S,ObjPtr->environment_flags);
2574+ StreamToValue(S,ObjPtr->entry_point_flags);
2575+ StreamToBytes(S,ObjPtr->level_name,LEVEL_NAME_LENGTH);
2576+ }
2577+
2578+ assert((S - Stream) == SIZEOF_directory_data);
2579+ return S;
2580+}
2581+
2582+// ZZZ: gnu cc swears this is currently unused, and I don't see any sneaky #includes that might need it...
2583+/*
2584+static uint8 *pack_directory_data(uint8 *Stream, directory_data *Objects, int Count)
2585+{
2586+ uint8* S = Stream;
2587+ directory_data* ObjPtr = Objects;
2588+
2589+ for (int k = 0; k < Count; k++, ObjPtr++)
2590+ {
2591+ ValueToStream(S,ObjPtr->mission_flags);
2592+ ValueToStream(S,ObjPtr->environment_flags);
2593+ ValueToStream(S,ObjPtr->entry_point_flags);
2594+ BytesToStream(S,ObjPtr->level_name,LEVEL_NAME_LENGTH);
2595+ }
2596+
2597+ assert((S - Stream) == SIZEOF_directory_data);
2598+ return S;
2599+}
2600+*/
--- marathon/trunk/Source_Files/Files/FileHandler.cpp (revision 522)
+++ marathon/trunk/Source_Files/Files/FileHandler.cpp (revision 523)
@@ -1,1480 +1,1477 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20-*/
21-
22-/*
23- * FileHandler_SDL.cpp - Platform-independant file handling, SDL implementation
24- *
25- * Written in 2000 by Christian Bauer
26- */
27-#ifndef SDL_RFORK_HACK
28-#include "cseries.h"
29-#include "FileHandler.h"
30-#include "resource_manager.h"
31-
32-#include "shell.h"
33-#include "interface.h"
34-#include "game_errors.h"
35-#include "tags.h"
36-
37-#include <stdio.h>
38-#include <stdlib.h>
39-#include <errno.h>
40-#include <limits.h>
41-#include <string>
42-#include <vector>
43-
44-#include <SDL_endian.h>
45-
46-#ifdef HAVE_UNISTD_H
47-#include <sys/stat.h>
48-#include <fcntl.h>
49-#include <dirent.h>
50-#include <unistd.h>
51-#endif
52-
53-#ifdef HAVE_ZZIP
54-#include <zzip/lib.h>
55-#include "SDL_rwops_zzip.h"
56-#endif
57-
58-#ifdef __MACOS__
59-#include "mac_rwops.h"
60-#endif
61-
62-#if defined(__WIN32__)
63-#define PATH_SEP '\\'
64-#elif !defined(__MACOS__)
65-#define PATH_SEP '/'
66-#else
67-#define PATH_SEP ':'
68-#endif
69-
70-#ifdef __MVCPP__
71-
72-#include <direct.h> // for mkdir()
73-#include <io.h> // for access()
74-#define R_OK 4 // for access(), this checks for read access. 6 should be used for read and write access both.
75-#include <sys/types.h> // for stat()
76-#include <sys/stat.h> // for stat()
77-
78-#endif
79-
80-#include "sdl_dialogs.h"
81-#include "sdl_widgets.h"
82-#include "SoundManager.h" // !
83-
84-#include "preferences.h"
85-
86-#include <boost/bind.hpp>
87-#include <boost/function.hpp>
88-#include <boost/algorithm/string/predicate.hpp>
89-
90-// From shell_sdl.cpp
91-extern vector<DirectorySpecifier> data_search_path;
92-extern DirectorySpecifier local_data_dir, preferences_dir, saved_games_dir, recordings_dir;
93-
94-extern bool is_applesingle(SDL_RWops *f, bool rsrc_fork, int32 &offset, int32 &length);
95-extern bool is_macbinary(SDL_RWops *f, int32 &data_length, int32 &rsrc_length);
96-
97-/*
98- * Opened file
99- */
100-
101-OpenedFile::OpenedFile() : f(NULL), err(0), is_forked(false), fork_offset(0), fork_length(0) {}
102-
103-bool OpenedFile::IsOpen()
104-{
105- return f != NULL;
106-}
107-
108-bool OpenedFile::Close()
109-{
110- if (f) {
111- SDL_RWclose(f);
112- f = NULL;
113- err = 0;
114- }
115- is_forked = false;
116- fork_offset = 0;
117- fork_length = 0;
118- return true;
119-}
120-
121-bool OpenedFile::GetPosition(int32 &Position)
122-{
123- if (f == NULL)
124- return false;
125-
126- err = 0;
127- Position = SDL_RWtell(f) - fork_offset;
128- return true;
129-}
130-
131-bool OpenedFile::SetPosition(int32 Position)
132-{
133- if (f == NULL)
134- return false;
135-
136- err = 0;
137- if (SDL_RWseek(f, Position + fork_offset, SEEK_SET) < 0)
138- err = errno;
139- return err == 0;
140-}
141-
142-bool OpenedFile::GetLength(int32 &Length)
143-{
144- if (f == NULL)
145- return false;
146-
147- if (is_forked)
148- Length = fork_length;
149- else {
150- int32 pos = SDL_RWtell(f);
151- SDL_RWseek(f, 0, SEEK_END);
152- Length = SDL_RWtell(f);
153- SDL_RWseek(f, pos, SEEK_SET);
154- }
155- err = 0;
156- return true;
157-}
158-
159-bool OpenedFile::Read(int32 Count, void *Buffer)
160-{
161- if (f == NULL)
162- return false;
163-
164- err = 0;
165- return (SDL_RWread(f, Buffer, 1, Count) == Count);
166-}
167-
168-bool OpenedFile::Write(int32 Count, void *Buffer)
169-{
170- if (f == NULL)
171- return false;
172-
173- err = 0;
174- return (SDL_RWwrite(f, Buffer, 1, Count) == Count);
175-}
176-
177-
178-SDL_RWops *OpenedFile::TakeRWops ()
179-{
180- SDL_RWops *taken = f;
181- f = NULL;
182- Close ();
183- return taken;
184-}
185-
186-/*
187- * Loaded resource
188- */
189-
190-LoadedResource::LoadedResource() : p(NULL), size(0) {}
191-
192-bool LoadedResource::IsLoaded()
193-{
194- return p != NULL;
195-}
196-
197-void LoadedResource::Unload()
198-{
199- if (p) {
200- free(p);
201- p = NULL;
202- size = 0;
203- }
204-}
205-
206-size_t LoadedResource::GetLength()
207-{
208- return size;
209-}
210-
211-void *LoadedResource::GetPointer(bool DoDetach)
212-{
213- void *ret = p;
214- if (DoDetach)
215- Detach();
216- return ret;
217-}
218-
219-void LoadedResource::SetData(void *data, size_t length)
220-{
221- Unload();
222- p = data;
223- size = length;
224-}
225-
226-void LoadedResource::Detach()
227-{
228- p = NULL;
229- size = 0;
230-}
231-
232-
233-/*
234- * Opened resource file
235- */
236-
237-OpenedResourceFile::OpenedResourceFile() : f(NULL), saved_f(NULL), err(0) {}
238-
239-bool OpenedResourceFile::Push()
240-{
241- saved_f = cur_res_file();
242- if (saved_f != f)
243- use_res_file(f);
244- err = 0;
245- return true;
246-}
247-
248-bool OpenedResourceFile::Pop()
249-{
250- if (f != saved_f)
251- use_res_file(saved_f);
252- err = 0;
253- return true;
254-}
255-
256-bool OpenedResourceFile::Check(uint32 Type, int16 ID)
257-{
258- Push();
259- bool result = has_1_resource(Type, ID);
260- err = result ? 0 : errno;
261- Pop();
262- return result;
263-}
264-
265-bool OpenedResourceFile::Get(uint32 Type, int16 ID, LoadedResource &Rsrc)
266-{
267- Push();
268- bool success = get_1_resource(Type, ID, Rsrc);
269- err = success ? 0 : errno;
270- Pop();
271- return success;
272-}
273-
274-bool OpenedResourceFile::IsOpen()
275-{
276- return f != NULL;
277-}
278-
279-bool OpenedResourceFile::Close()
280-{
281- if (f) {
282- close_res_file(f);
283- f = NULL;
284- err = 0;
285- }
286- return true;
287-}
288-
289-
290-/*
291- * File specification
292- */
293-//AS: Constructor moved here to fix linking errors
294-FileSpecifier::FileSpecifier(): err(0) {}
295-const FileSpecifier &FileSpecifier::operator=(const FileSpecifier &other)
296-{
297- if (this != &other) {
298- name = other.name;
299- err = other.err;
300- }
301- return *this;
302-}
303-
304-// Create file
305-bool FileSpecifier::Create(Typecode Type)
306-{
307- Delete();
308- // files are automatically created when opened for writing
309- err = 0;
310- return true;
311-}
312-
313-// Create directory
314-bool FileSpecifier::CreateDirectory()
315-{
316- err = 0;
317-#if defined(__WIN32__)
318- if (mkdir(GetPath()) < 0)
319-#else
320- if (mkdir(GetPath(), 0777) < 0)
321-#endif
322- err = errno;
323- return err == 0;
324-}
325-
326-static std::string unix_path_separators(const std::string& input)
327-{
328- if (PATH_SEP == '/') return input;
329-
330- std::string output;
331- for (std::string::const_iterator it = input.begin(); it != input.end(); ++it) {
332- if (*it == PATH_SEP)
333- output.push_back('/');
334- else
335- output.push_back(*it);
336- }
337-
338- return output;
339-}
340-
341-// Open data file
342-bool FileSpecifier::Open(OpenedFile &OFile, bool Writable)
343-{
344- OFile.Close();
345-
346- SDL_RWops *f;
347-#ifdef __MACOS__
348- if (!Writable)
349- f = OFile.f = open_fork_from_existing_path(GetPath(), false);
350- else
351-#endif
352- {
353-#ifdef HAVE_ZZIP
354- if (!Writable)
355- {
356- f = OFile.f = SDL_RWFromZZIP(unix_path_separators(GetPath()).c_str(), "rb");
357- }
358- else
359-#endif
360- f = OFile.f = SDL_RWFromFile(GetPath(), Writable ? "wb+" : "rb");
361- }
362-
363- err = f ? 0 : errno;
364- if (f == NULL) {
365- set_game_error(systemError, err);
366- return false;
367- }
368- if (Writable)
369- return true;
370-
371- // Transparently handle AppleSingle and MacBinary files on reading
372- int32 offset, data_length, rsrc_length;
373- if (is_applesingle(f, false, offset, data_length)) {
374- OFile.is_forked = true;
375- OFile.fork_offset = offset;
376- OFile.fork_length = data_length;
377- SDL_RWseek(f, offset, SEEK_SET);
378- return true;
379- } else if (is_macbinary(f, data_length, rsrc_length)) {
380- OFile.is_forked = true;
381- OFile.fork_offset = 128;
382- OFile.fork_length = data_length;
383- SDL_RWseek(f, 128, SEEK_SET);
384- return true;
385- }
386- SDL_RWseek(f, 0, SEEK_SET);
387- return true;
388-}
389-
390-// Open resource file
391-bool FileSpecifier::Open(OpenedResourceFile &OFile, bool Writable)
392-{
393- OFile.Close();
394-
395- OFile.f = open_res_file(*this);
396- err = OFile.f ? 0 : errno;
397- if (OFile.f == NULL) {
398- set_game_error(systemError, err);
399- return false;
400- } else
401- return true;
402-}
403-
404-// Check for existence of file
405-bool FileSpecifier::Exists()
406-{
407- // Check whether the file is readable
408- err = 0;
409- if (access(GetPath(), R_OK) < 0)
410- err = errno;
411-
412-#ifdef HAVE_ZZIP
413- if (err)
414- {
415- // Check whether zzip can open the file (slow!)
416- ZZIP_FILE* file = zzip_open(unix_path_separators(GetPath()).c_str(), R_OK);
417- if (file)
418- {
419- zzip_close(file);
420- return true;
421- }
422- else
423- {
424- return false;
425- }
426- }
427-#endif
428- return (err == 0);
429-}
430-
431-bool FileSpecifier::IsDir()
432-{
433- struct stat st;
434- err = 0;
435- if (stat(GetPath(), &st) < 0)
436- return false;
437- return (S_ISDIR(st.st_mode));
438-}
439-
440-// Get modification date
441-TimeType FileSpecifier::GetDate()
442-{
443- struct stat st;
444- err = 0;
445- if (stat(GetPath(), &st) < 0) {
446- err = errno;
447- return 0;
448- }
449- return st.st_mtime;
450-}
451-
452-static const char * alephone_extensions[] = {
453- ".sceA",
454- ".sgaA",
455- ".filA",
456- ".phyA",
457- ".shpA",
458- ".sndA",
459- 0
460-};
461-
462-std::string FileSpecifier::HideExtension(const std::string& filename)
463-{
464- if (environment_preferences->hide_extensions)
465- {
466- const char **extension = alephone_extensions;
467- while (*extension)
468- {
469- if (boost::algorithm::ends_with(filename, *extension))
470- {
471- return filename.substr(0, filename.length() - strlen(*extension));
472- }
473-
474- ++extension;
475- }
476- }
477-
478- return filename;
479-}
480-
481-struct extension_mapping
482-{
483- const char *extension;
484- bool case_sensitive;
485- Typecode typecode;
486-};
487-
488-static extension_mapping extensions[] =
489-{
490- // some common extensions, to speed up building map lists
491- { "dds", false, _typecode_unknown },
492- { "jpg", false, _typecode_unknown },
493- { "png", false, _typecode_unknown },
494- { "bmp", false, _typecode_unknown },
495- { "txt", false, _typecode_unknown },
496- { "ttf", false, _typecode_unknown },
497-
498- { "lua", false, _typecode_netscript }, // netscript, or unknown?
499- { "mml", false, _typecode_unknown }, // no type code for this yet
500-
501- { "sceA", false, _typecode_scenario },
502- { "sgaA", false, _typecode_savegame },
503- { "filA", false, _typecode_film },
504- { "phyA", false, _typecode_physics },
505- { "ShPa", true, _typecode_shapespatch }, // must come before shpA
506- { "shpA", false, _typecode_shapes },
507- { "sndA", false, _typecode_sounds },
508-
509- { "scen", false, _typecode_scenario },
510- { "shps", false, _typecode_shapes },
511-
512- {0, false, _typecode_unknown}
513-};
514-
515-// Determine file type
516-Typecode FileSpecifier::GetType()
517-{
518-
519- // if there's an extension, assume it's correct
520- const char *extension = strrchr(GetPath(), '.');
521- if (extension) {
522- extension_mapping *mapping = extensions;
523- while (mapping->extension)
524- {
525- if (( mapping->case_sensitive && (strcmp(extension + 1, mapping->extension) == 0)) ||
526- (!mapping->case_sensitive && (strcasecmp(extension + 1, mapping->extension) == 0)))
527- {
528- return mapping->typecode;
529- }
530- ++mapping;
531- }
532- }
533-
534- // Open file
535- OpenedFile f;
536- if (!Open(f))
537- return _typecode_unknown;
538- SDL_RWops *p = f.GetRWops();
539- int32 file_length = 0;
540- f.GetLength(file_length);
541-
542- // Check for Sounds file
543- {
544- f.SetPosition(0);
545- uint32 version = SDL_ReadBE32(p);
546- uint32 tag = SDL_ReadBE32(p);
547- if ((version == 0 || version == 1) && tag == FOUR_CHARS_TO_INT('s', 'n', 'd', '2'))
548- return _typecode_sounds;
549- }
550-
551- // Check for Map/Physics file
552- {
553- f.SetPosition(0);
554- int version = SDL_ReadBE16(p);
555- int data_version = SDL_ReadBE16(p);
556- if ((version == 0 || version == 1 || version == 2 || version == 4) && (data_version == 0 || data_version == 1 || data_version == 2)) {
557- SDL_RWseek(p, 68, SEEK_CUR);
558- int32 directory_offset = SDL_ReadBE32(p);
559- if (directory_offset >= file_length)
560- goto not_map;
561- f.SetPosition(128);
562- uint32 tag = SDL_ReadBE32(p);
563- // ghs: I do not believe this list is comprehensive
564- // I think it's just what we've seen so far?
565- switch (tag) {
566- case LINE_TAG:
567- case POINT_TAG:
568- case SIDE_TAG:
569- return _typecode_scenario;
570- break;
571- case MONSTER_PHYSICS_TAG:
572- return _typecode_physics;
573- break;
574- }
575-
576- }
577-not_map: ;
578- }
579-
580- // Check for Shapes file
581- {
582- f.SetPosition(0);
583- for (int i=0; i<32; i++) {
584- uint32 status_flags = SDL_ReadBE32(p);
585- int32 offset = SDL_ReadBE32(p);
586- int32 length = SDL_ReadBE32(p);
587- int32 offset16 = SDL_ReadBE32(p);
588- int32 length16 = SDL_ReadBE32(p);
589- if (status_flags != 0
590- || (offset != NONE && (offset >= file_length || offset + length > file_length))
591- || (offset16 != NONE && (offset16 >= file_length || offset16 + length16 > file_length)))
592- goto not_shapes;
593- SDL_RWseek(p, 12, SEEK_CUR);
594- }
595- return _typecode_shapes;
596-not_shapes: ;
597- }
598-
599- // Not identified
600- return _typecode_unknown;
601-}
602-
603-// Get free space on disk
604-bool FileSpecifier::GetFreeSpace(uint32 &FreeSpace)
605-{
606- // This is impossible to do in a platform-independant way, so we
607- // just return 16MB which should be enough for everything
608- FreeSpace = 16 * 1024 * 1024;
609- err = 0;
610- return true;
611-}
612-
613-// Exchange two files
614-bool FileSpecifier::Exchange(FileSpecifier &other)
615-{
616- // Create temporary name (this is cheap, we should make sure that the
617- // name is not already in use...)
618- FileSpecifier tmp;
619- ToDirectory(tmp);
620- tmp.AddPart("exchange_tmp_file");
621-
622- err = 0;
623- if (rename(GetPath(), tmp.GetPath()) < 0)
624- err = errno;
625- else
626- rename(other.GetPath(), GetPath());
627- if (rename(tmp.GetPath(), other.GetPath()) < 0)
628- err = errno;
629- return err == 0;
630-}
631-
632-// Delete file
633-bool FileSpecifier::Delete()
634-{
635- err = 0;
636- if (remove(GetPath()) < 0)
637- err = errno;
638- return err == 0;
639-}
640-
641-bool FileSpecifier::Rename(const FileSpecifier& Destination)
642-{
643- return rename(GetPath(), Destination.GetPath()) == 0;
644-}
645-
646-// Set to local (per-user) data directory
647-void FileSpecifier::SetToLocalDataDir()
648-{
649- name = local_data_dir.name;
650-}
651-
652-// Set to preferences directory
653-void FileSpecifier::SetToPreferencesDir()
654-{
655- name = preferences_dir.name;
656-}
657-
658-// Set to saved games directory
659-void FileSpecifier::SetToSavedGamesDir()
660-{
661- name = saved_games_dir.name;
662-}
663-
664-// Set to recordings directory
665-void FileSpecifier::SetToRecordingsDir()
666-{
667- name = recordings_dir.name;
668-}
669-
670-static string local_path_separators(const char *path)
671-{
672- string local_path = path;
673- if (PATH_SEP == '/') return local_path;
674-
675- for (size_t k = 0; k < local_path.size(); ++k) {
676- if (local_path[k] == '/')
677- local_path[k] = PATH_SEP;
678- }
679-
680- return local_path;
681-}
682-
683-// Traverse search path, look for file given relative path name
684-bool FileSpecifier::SetNameWithPath(const char *NameWithPath)
685-{
686- FileSpecifier full_path;
687- string rel_path = local_path_separators(NameWithPath);
688-
689- vector<DirectorySpecifier>::const_iterator i = data_search_path.begin(), end = data_search_path.end();
690- while (i != end) {
691- full_path = *i + rel_path;
692- if (full_path.Exists()) {
693- name = full_path.name;
694- err = 0;
695- return true;
696- }
697- i++;
698- }
699- err = ENOENT;
700- return false;
701-}
702-
703-bool FileSpecifier::SetNameWithPath(const char* NameWithPath, const DirectorySpecifier& Directory)
704-{
705- FileSpecifier full_path;
706- string rel_path = local_path_separators(NameWithPath);
707-
708- full_path = Directory + rel_path;
709- if (full_path.Exists()) {
710- name = full_path.name;
711- err = 0;
712- return true;
713- }
714-
715- err = ENOENT;
716- return false;
717-}
718-
719-// Get last element of path
720-void FileSpecifier::GetName(char *part) const
721-{
722- string::size_type pos = name.rfind(PATH_SEP);
723- if (pos == string::npos)
724- strcpy(part, name.c_str());
725- else
726- strcpy(part, name.substr(pos + 1).c_str());
727-}
728-
729-// Add part to path name
730-void FileSpecifier::AddPart(const string &part)
731-{
732- if (name.length() && name[name.length() - 1] == PATH_SEP)
733- name += local_path_separators(part.c_str());
734- else
735- name = name + PATH_SEP + local_path_separators(part.c_str());
736-
737- canonicalize_path();
738-}
739-
740-// Split path to base and last part
741-void FileSpecifier::SplitPath(string &base, string &part) const
742-{
743- string::size_type pos = name.rfind(PATH_SEP);
744- if (pos == string::npos) {
745- base = name;
746- part.erase();
747- } else if (pos == 0) {
748- base = PATH_SEP;
749- part = name.substr(1);
750- } else {
751- base = name.substr(0, pos);
752- part = name.substr(pos + 1);
753- }
754-}
755-
756-// Fill file specifier with base name
757-void FileSpecifier::ToDirectory(DirectorySpecifier &dir)
758-{
759- string part;
760- SplitPath(dir, part);
761-}
762-
763-// Set file specifier from directory specifier
764-void FileSpecifier::FromDirectory(DirectorySpecifier &Dir)
765-{
766- name = Dir.name;
767-}
768-
769-// Canonicalize path
770-void FileSpecifier::canonicalize_path(void)
771-{
772-#if !defined(__WIN32__)
773-
774- // Replace multiple consecutive '/'s by a single '/'
775- while (true) {
776- string::size_type pos = name.find("//");
777- if (pos == string::npos)
778- break;
779- name.erase(pos, 1);
780- }
781-
782-#endif
783-
784- // Remove trailing '/'
785- // ZZZ: only if we're not naming the root directory /
786- if (!name.empty() && name[name.size()-1] == PATH_SEP && name.size() != 1)
787- name.erase(name.size()-1, 1);
788-}
789-
790-// Read directory contents
791-bool FileSpecifier::ReadDirectory(vector<dir_entry> &vec)
792-{
793- vec.clear();
794-
795-#if defined(__MVCPP__)
796-
797- WIN32_FIND_DATA findData;
798-
799- // We need to add a wildcard to the search name
800- string search_name;
801- search_name = name;
802- search_name += "\\*.*";
803-
804- HANDLE hFind = ::FindFirstFile(search_name.c_str(), &findData);
805-
806- if (hFind == INVALID_HANDLE_VALUE) {
807- err = ::GetLastError();
808- return false;
809- }
810-
811- do {
812- // Exclude current and parent directories
813- if (findData.cFileName[0] != '.' ||
814- (findData.cFileName[1] && findData.cFileName[1] != '.')) {
815- // Return found files to dir_entry
816- int32 fileSize = (findData.nFileSizeHigh * MAXDWORD) + findData.nFileSizeLow;
817- vec.push_back(dir_entry(findData.cFileName, fileSize,
818- (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0, false));
819- }
820- } while(::FindNextFile(hFind, &findData));
821-
822- if (!::FindClose(hFind))
823- err = ::GetLastError(); // not sure if we should return this or not
824- else
825- err = 0;
826- return true;
827-
828-#else
829-
830- DIR *d = opendir(GetPath());
831-
832- if (d == NULL) {
833- err = errno;
834- return false;
835- }
836- struct dirent *de = readdir(d);
837- while (de) {
838- FileSpecifier full_path = name;
839- full_path += de->d_name;
840- struct stat st;
841- if (stat(full_path.GetPath(), &st) == 0) {
842- // Ignore files starting with '.' and the directories '.' and '..'
843- if (de->d_name[0] != '.' || (S_ISDIR(st.st_mode) && !(de->d_name[1] == '\0' || de->d_name[1] == '.')))
844- vec.push_back(dir_entry(de->d_name, st.st_size, S_ISDIR(st.st_mode), false, st.st_mtime));
845- }
846- de = readdir(d);
847- }
848- closedir(d);
849- err = 0;
850- return true;
851-
852-#endif
853-}
854-
855-// Copy file contents
856-bool FileSpecifier::CopyContents(FileSpecifier &source_name)
857-{
858- err = 0;
859- OpenedFile src, dst;
860- if (source_name.Open(src)) {
861- Delete();
862- if (Open(dst, true)) {
863- const int BUFFER_SIZE = 1024;
864- uint8 buffer[BUFFER_SIZE];
865-
866- int32 length = 0;
867- src.GetLength(length);
868-
869- while (length && err == 0) {
870- int32 count = length > BUFFER_SIZE ? BUFFER_SIZE : length;
871- if (src.Read(count, buffer)) {
872- if (!dst.Write(count, buffer))
873- err = dst.GetError();
874- } else
875- err = src.GetError();
876- length -= count;
877- }
878- }
879- } else
880- err = source_name.GetError();
881- if (err)
882- Delete();
883- return err == 0;
884-}
885-
886-// ZZZ: Filesystem browsing list that lets user actually navigate directories...
887-class w_directory_browsing_list : public w_list<dir_entry>
888-{
889-public:
890- w_directory_browsing_list(const FileSpecifier& inStartingDirectory, dialog* inParentDialog)
891- : w_list<dir_entry>(entries, 400, 15, 0), parent_dialog(inParentDialog), current_directory(inStartingDirectory), sort_order(sort_by_name)
892- {
893- refresh_entries();
894- }
895-
896-
897- w_directory_browsing_list(const FileSpecifier& inStartingDirectory, dialog* inParentDialog, const string& inStartingFile)
898- : w_list<dir_entry>(entries, 400, 15, 0), parent_dialog(inParentDialog), current_directory(inStartingDirectory)
899- {
900- refresh_entries();
901- if(entries.size() != 0)
902- select_entry(inStartingFile, false);
903- }
904-
905-
906- void set_directory_changed_callback(action_proc inCallback, void* inArg = NULL)
907- {
908- directory_changed_proc = inCallback;
909- directory_changed_proc_arg = inArg;
910- }
911-
912-
913- void draw_item(vector<dir_entry>::const_iterator i, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const
914- {
915- y += font->get_ascent();
916- set_drawing_clip_rectangle(0, x, s->h, x + width);
917-
918- if(i->is_directory)
919- {
920- string theName = i->name + "/";
921- draw_text(s, theName.c_str (), x, y, selected ? get_theme_color (ITEM_WIDGET, ACTIVE_STATE) : get_theme_color (ITEM_WIDGET, DEFAULT_STATE), font, style, true);
922- }
923- else
924- {
925- char date[256];
926- tm *time_info = localtime(&i->date);
927-
928- if (time_info)
929- {
930- strftime(date, 256, "%x %R", time_info);
931- int date_width = text_width(date, font, style);
932- draw_text(s, date, x + width - date_width, y, selected ? get_theme_color(ITEM_WIDGET, ACTIVE_STATE) : get_theme_color(ITEM_WIDGET, DEFAULT_STATE), font, style);
933- set_drawing_clip_rectangle(0, x, s->h, x + width - date_width - 4);
934- }
935- draw_text(s, FileSpecifier::HideExtension(i->name).c_str (), x, y, selected ? get_theme_color (ITEM_WIDGET, ACTIVE_STATE) : get_theme_color (ITEM_WIDGET, DEFAULT_STATE), font, style, true);
936- }
937-
938- set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
939- }
940-
941-
942- bool can_move_up_a_level()
943- {
944- string base;
945- string part;
946- current_directory.SplitPath(base, part);
947- return (part != string());
948- }
949-
950-
951- void move_up_a_level()
952- {
953- string base;
954- string part;
955- current_directory.SplitPath(base, part);
956- if(part != string())
957- {
958- FileSpecifier parent_directory(base);
959- if(parent_directory.Exists())
960- {
961- current_directory = parent_directory;
962- refresh_entries();
963- select_entry(part, true);
964- announce_directory_changed();
965- }
966- }
967- }
968-
969-
970- void item_selected(void)
971- {
972- current_directory.AddPart(entries[selection].name);
973-
974- if(entries[selection].is_directory)
975- {
976- refresh_entries();
977- announce_directory_changed();
978- }
979- else if (file_selected)
980- {
981- file_selected(entries[selection].name);
982- }
983- }
984-
985- enum SortOrder {
986- sort_by_name,
987- sort_by_date,
988- };
989-
990- void sort_by(SortOrder order)
991- {
992- sort_order = order;
993- refresh_entries();
994- }
995-
996-
997- const FileSpecifier& get_file() { return current_directory; }
998-
999- boost::function<void(const std::string&)> file_selected;
1000-
1001-private:
1002- vector<dir_entry> entries;
1003- dialog* parent_dialog;
1004- FileSpecifier current_directory;
1005- action_proc directory_changed_proc;
1006- void* directory_changed_proc_arg;
1007- SortOrder sort_order;
1008-
1009- struct most_recent
1010- {
1011- bool operator()(const dir_entry& a, const dir_entry& b)
1012- {
1013- return a.date > b.date;
1014- }
1015- };
1016-
1017- void refresh_entries()
1018- {
1019- if(current_directory.ReadDirectory(entries))
1020- {
1021- if (sort_order == sort_by_name)
1022- {
1023- sort(entries.begin(), entries.end());
1024- }
1025- else
1026- {
1027- sort(entries.begin(), entries.end(), most_recent());
1028- }
1029- }
1030- num_items = entries.size();
1031- new_items();
1032- }
1033-
1034- void select_entry(const string& inName, bool inIsDirectory)
1035- {
1036- dir_entry theEntryToFind(inName, NONE /* length - ignored for our purpose */, inIsDirectory);
1037- vector<dir_entry>::iterator theEntry = find(entries.begin(), entries.end(), theEntryToFind);
1038- if(theEntry != entries.end())
1039- set_selection(theEntry - entries.begin());
1040- }
1041-
1042- void announce_directory_changed()
1043- {
1044- if(directory_changed_proc != NULL)
1045- directory_changed_proc(directory_changed_proc_arg);
1046- }
1047-};
1048-
1049-const char* sort_by_labels[] = {
1050- "名前",
1051- "更新日時",
1052- 0
1053-};
1054-
1055-// common functionality for read and write dialogs
1056-class FileDialog {
1057-public:
1058- FileDialog() {
1059- }
1060-
1061- bool Run() {
1062- Layout();
1063-
1064- bool result = false;
1065- if (m_dialog.run() == 0)
1066- {
1067- result = true;
1068- }
1069-
1070- if (get_game_state() == _game_in_progress) update_game_window();
1071- return result;
1072- }
1073-
1074-protected:
1075- void Init(const FileSpecifier& dir, w_directory_browsing_list::SortOrder default_order, std::string filename) {
1076- m_sort_by_w = new w_select(static_cast<size_t>(default_order), sort_by_labels);
1077- m_sort_by_w->set_selection_changed_callback(boost::bind(&FileDialog::on_change_sort_order, this));
1078- m_up_button_w = new w_button("上の階層へ", boost::bind(&FileDialog::on_up, this));
1079- if (filename.empty())
1080- {
1081- m_list_w = new w_directory_browsing_list(dir, &m_dialog);
1082- }
1083- else
1084- {
1085- m_list_w = new w_directory_browsing_list(dir, &m_dialog, filename);
1086- }
1087- m_list_w->sort_by(default_order);
1088- m_list_w->set_directory_changed_callback(boost::bind(&FileDialog::on_directory_changed, this));
1089-
1090- dir.GetName(temporary);
1091- m_directory_name_w = new w_static_text(temporary);
1092- }
1093-
1094- dialog m_dialog;
1095- w_select* m_sort_by_w;
1096- w_button* m_up_button_w;
1097- w_static_text* m_directory_name_w;
1098- w_directory_browsing_list* m_list_w;
1099-
1100-private:
1101- virtual void Layout() = 0;
1102-
1103-
1104- void on_directory_changed() {
1105- m_list_w->get_file().GetName(temporary);
1106- m_directory_name_w->set_text(temporary);
1107-
1108- m_up_button_w->set_enabled(m_list_w->can_move_up_a_level());
1109-
1110- m_dialog.draw();
1111- }
1112-
1113- void on_change_sort_order() {
1114- m_list_w->sort_by(static_cast<w_directory_browsing_list::SortOrder>(m_sort_by_w->get_selection()));
1115- }
1116-
1117- void on_up() {
1118- m_list_w->move_up_a_level();
1119- }
1120-
1121-};
1122-
1123-class ReadFileDialog : public FileDialog
1124-{
1125-public:
1126- ReadFileDialog(FileSpecifier dir, Typecode type, const char* prompt) : FileDialog(), m_prompt(prompt) {
1127- w_directory_browsing_list::SortOrder default_order = w_directory_browsing_list::sort_by_name;
1128-
1129- if (!m_prompt)
1130- {
1131- switch(type)
1132- {
1133- case _typecode_savegame:
1134- m_prompt = "ゲーム再開";
1135- break;
1136- case _typecode_film:
1137- m_prompt = "フィルムを再生";
1138- break;
1139- default:
1140- m_prompt = "ファイルを開く";
1141- break;
1142- }
1143- }
1144-
1145- std::string filename;
1146- switch (type)
1147- {
1148- case _typecode_savegame:
1149- dir.SetToSavedGamesDir();
1150- default_order = w_directory_browsing_list::sort_by_date;
1151- break;
1152- case _typecode_film:
1153- dir.SetToRecordingsDir();
1154- break;
1155- case _typecode_scenario:
1156- case _typecode_netscript:
1157- {
1158- // Go to most recently-used directory
1159- DirectorySpecifier theDirectory;
1160- dir.SplitPath(theDirectory, filename);
1161- dir.FromDirectory(theDirectory);
1162- if (!dir.Exists())
1163- dir.SetToLocalDataDir();
1164- }
1165- break;
1166- default:
1167- dir.SetToLocalDataDir();
1168- break;
1169- }
1170-
1171- Init(dir, default_order, filename);
1172-
1173- m_list_w->file_selected = boost::bind(&ReadFileDialog::on_file_selected, this);
1174- }
1175-
1176- void Layout() {
1177- vertical_placer* placer = new vertical_placer;
1178- placer->dual_add(new w_title(m_prompt), m_dialog);
1179- placer->add(new w_spacer, true);
1180-
1181- placer->dual_add(m_directory_name_w, m_dialog);
1182-
1183- placer->add(new w_spacer(), true);
1184-
1185- horizontal_placer* top_row_placer = new horizontal_placer;
1186-
1187- top_row_placer->dual_add(m_sort_by_w->label("ソ\ート順:"), m_dialog);
1188- top_row_placer->dual_add(m_sort_by_w, m_dialog);
1189- top_row_placer->add_flags(placeable::kFill);
1190- top_row_placer->add(new w_spacer, true);
1191- top_row_placer->add_flags();
1192- top_row_placer->dual_add(m_up_button_w, m_dialog);
1193-
1194- placer->add_flags(placeable::kFill);
1195- placer->add(top_row_placer, true);
1196- placer->add_flags();
1197-
1198- placer->dual_add(m_list_w, m_dialog);
1199- placer->add(new w_spacer, true);
1200-
1201- horizontal_placer* button_placer = new horizontal_placer;
1202- button_placer->dual_add(new w_button("キャンセル", dialog_cancel, &m_dialog), m_dialog);
1203-
1204- placer->add(button_placer, true);
1205-
1206- m_dialog.activate_widget(m_list_w);
1207- m_dialog.set_widget_placer(placer);
1208- }
1209-
1210- FileSpecifier GetFile() {
1211- return m_list_w->get_file();
1212- }
1213-
1214-private:
1215- void on_file_selected() {
1216- m_dialog.quit(0);
1217- }
1218-
1219- const char* m_prompt;
1220- std::string m_filename;
1221-};
1222-
1223-bool FileSpecifier::ReadDialog(Typecode type, const char *prompt)
1224-{
1225- ReadFileDialog d(*this, type, prompt);
1226- if (d.Run())
1227- {
1228- *this = d.GetFile();
1229- return true;
1230- }
1231- else
1232- {
1233- return false;
1234- }
1235-}
1236-
1237-class w_file_name : public w_text_entry {
1238-public:
1239- w_file_name(dialog *d, const char *initial_name = NULL) : w_text_entry(31, initial_name), parent(d) {}
1240- ~w_file_name() {}
1241-
1242- void event(SDL_Event & e)
1243- {
1244- // Return = close dialog
1245- if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RETURN)
1246- parent->quit(0);
1247- w_text_entry::event(e);
1248- }
1249-
1250-private:
1251- dialog *parent;
1252-};
1253-
1254-class WriteFileDialog : public FileDialog
1255-{
1256-public:
1257- WriteFileDialog(FileSpecifier dir, Typecode type, const char* prompt, const char* default_name) : FileDialog(), m_prompt(prompt), m_default_name(default_name), m_extension(0) {
1258- if (!m_prompt)
1259- {
1260- switch (type)
1261- {
1262- case _typecode_savegame:
1263- prompt = "ゲーム保存";
1264- break;
1265- case _typecode_film:
1266- prompt = "フィルム保存";
1267- break;
1268- default:
1269- prompt = "ファイル保存";
1270- break;
1271- }
1272- }
1273-
1274- switch (type)
1275- {
1276- case _typecode_savegame:
1277- m_extension = ".sgaA";
1278- break;
1279- case _typecode_film:
1280- m_extension = ".filA";
1281- break;
1282- default:
1283- break;
1284- }
1285-
1286- if (m_extension && boost::algorithm::ends_with(m_default_name, m_extension))
1287- {
1288- m_default_name.resize(m_default_name.size() - strlen(m_extension));
1289- }
1290-
1291- w_directory_browsing_list::SortOrder default_order = w_directory_browsing_list::sort_by_name;
1292- switch (type) {
1293- case _typecode_savegame:
1294- {
1295- string base;
1296- string part;
1297- dir.SplitPath(base, part);
1298- if (part != string())
1299- {
1300- dir = base;
1301- }
1302- default_order = w_directory_browsing_list::sort_by_date;
1303- }
1304- break;
1305- case _typecode_film:
1306- dir.SetToRecordingsDir();
1307- break;
1308- default:
1309- dir.SetToLocalDataDir();
1310- break;
1311- }
1312-
1313- Init(dir, default_order, m_default_name);
1314-
1315- m_list_w->file_selected = boost::bind(&WriteFileDialog::on_file_selected, this, _1);
1316- }
1317-
1318- void Layout() {
1319- vertical_placer* placer = new vertical_placer;
1320- placer->dual_add(new w_title(m_prompt), m_dialog);
1321- placer->add(new w_spacer, true);
1322-
1323- placer->dual_add(m_directory_name_w, m_dialog);
1324-
1325- placer->add(new w_spacer(), true);
1326-
1327- horizontal_placer* top_row_placer = new horizontal_placer;
1328-
1329- top_row_placer->dual_add(m_sort_by_w->label("ソ\ート順:"), m_dialog);
1330- top_row_placer->dual_add(m_sort_by_w, m_dialog);
1331- top_row_placer->add_flags(placeable::kFill);
1332- top_row_placer->add(new w_spacer, true);
1333- top_row_placer->add_flags();
1334- top_row_placer->dual_add(m_up_button_w, m_dialog);
1335-
1336- placer->add_flags(placeable::kFill);
1337- placer->add(top_row_placer, true);
1338- placer->add_flags();
1339-
1340- placer->dual_add(m_list_w, m_dialog);
1341- placer->add(new w_spacer, true);
1342-
1343- placer->add_flags(placeable::kFill);
1344-
1345- horizontal_placer* file_name_placer = new horizontal_placer;
1346- m_name_w = new w_file_name(&m_dialog, m_default_name.c_str());
1347- file_name_placer->dual_add(m_name_w->label("ファイル名:"), m_dialog);
1348- file_name_placer->add_flags(placeable::kFill);
1349- file_name_placer->dual_add(m_name_w, m_dialog);
1350-
1351- placer->add_flags(placeable::kFill);
1352- placer->add(file_name_placer, true);
1353- placer->add_flags();
1354- placer->add(new w_spacer, true);
1355-
1356- horizontal_placer* button_placer = new horizontal_placer;
1357- button_placer->dual_add(new w_button("OK", dialog_ok, &m_dialog), m_dialog);
1358- button_placer->dual_add(new w_button("キャンセル", dialog_cancel, &m_dialog), m_dialog);
1359-
1360- placer->add(button_placer, true);
1361-
1362- m_dialog.activate_widget(m_name_w);
1363- m_dialog.set_widget_placer(placer);
1364- }
1365-
1366- FileSpecifier GetPath() {
1367- FileSpecifier dir = m_list_w->get_file();
1368- std::string base;
1369- std::string part;
1370- dir.SplitPath(base, part);
1371-
1372- std::string filename = GetFilename();
1373- if (part == filename)
1374- {
1375- dir = base;
1376- }
1377-
1378- if (m_extension && !boost::algorithm::ends_with(filename, m_extension))
1379- {
1380- filename += m_extension;
1381- }
1382- dir.AddPart(filename);
1383- return dir;
1384- }
1385-
1386- std::string GetFilename() {
1387- return m_name_w->get_text();
1388- }
1389-
1390-private:
1391- void on_file_selected(const std::string& filename) {
1392- m_name_w->set_text(filename.c_str());
1393- m_dialog.quit(0);
1394- }
1395-
1396- const char* m_prompt;
1397- std::string m_default_name;
1398- const char* m_extension;
1399- w_file_name* m_name_w;
1400-};
1401-
1402-static bool confirm_save_choice(FileSpecifier & file);
1403-
1404-bool FileSpecifier::WriteDialog(Typecode type, const char *prompt, const char *default_name)
1405-{
1406-again:
1407- WriteFileDialog d(*this, type, prompt, default_name);
1408- bool result = false;
1409- if (d.Run())
1410- {
1411- if (d.GetFilename().empty())
1412- {
1413- play_dialog_sound(DIALOG_ERROR_SOUND);
1414- goto again;
1415- }
1416-
1417- *this = d.GetPath();
1418-
1419- if (!confirm_save_choice(*this))
1420- {
1421- goto again;
1422- }
1423-
1424- result = true;
1425- }
1426-
1427- return result;
1428-
1429-}
1430-
1431-bool FileSpecifier::WriteDialogAsync(Typecode type, char *prompt, char *default_name)
1432-{
1433- return FileSpecifier::WriteDialog(type, prompt, default_name);
1434-}
1435-
1436-static bool confirm_save_choice(FileSpecifier & file)
1437-{
1438- // If the file doesn't exist, everything is alright
1439- if (!file.Exists())
1440- return true;
1441-
1442- // Construct message
1443- char name[256];
1444- file.GetName(name);
1445- char message[512];
1446- sprintf(message, "'%sは、すでに存在しています。", name);
1447-
1448- // Create dialog
1449- dialog d;
1450- vertical_placer *placer = new vertical_placer;
1451- placer->dual_add(new w_static_text(message), d);
1452- placer->dual_add(new w_static_text("上書きしてもよろしいですか?"), d);
1453- placer->add(new w_spacer(), true);
1454-
1455- horizontal_placer *button_placer = new horizontal_placer;
1456- w_button *default_button = new w_button("はい", dialog_ok, &d);
1457- button_placer->dual_add(default_button, d);
1458- button_placer->dual_add(new w_button("いいえ", dialog_cancel, &d), d);
1459-
1460- placer->add(button_placer, true);
1461-
1462- d.activate_widget(default_button);
1463-
1464- d.set_widget_placer(placer);
1465-
1466- // Run dialog
1467- return d.run() == 0;
1468-}
1469-
1470-ScopedSearchPath::ScopedSearchPath(const DirectorySpecifier& dir)
1471-{
1472- data_search_path.insert(data_search_path.begin(), dir);
1473-}
1474-
1475-ScopedSearchPath::~ScopedSearchPath()
1476-{
1477- data_search_path.erase(data_search_path.begin());
1478-}
1479-
1480-#endif
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 3 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+*/
21+
22+/*
23+ * FileHandler_SDL.cpp - Platform-independant file handling, SDL implementation
24+ *
25+ * Written in 2000 by Christian Bauer
26+ */
27+#ifndef SDL_RFORK_HACK
28+#include "cseries.h"
29+#include "FileHandler.h"
30+#include "resource_manager.h"
31+
32+#include "shell.h"
33+#include "interface.h"
34+#include "game_errors.h"
35+#include "tags.h"
36+
37+#include <stdio.h>
38+#include <stdlib.h>
39+#include <errno.h>
40+#include <limits.h>
41+#include <string>
42+#include <vector>
43+
44+#include <SDL_endian.h>
45+
46+#ifdef HAVE_UNISTD_H
47+#include <sys/stat.h>
48+#include <fcntl.h>
49+#include <dirent.h>
50+#include <unistd.h>
51+#endif
52+
53+#ifdef HAVE_ZZIP
54+#include <zzip/lib.h>
55+#include "SDL_rwops_zzip.h"
56+#endif
57+
58+#ifdef __MACOS__
59+#include "mac_rwops.h"
60+#endif
61+
62+#if defined(__WIN32__)
63+#define PATH_SEP '\\'
64+#elif !defined(__MACOS__)
65+#define PATH_SEP '/'
66+#else
67+#define PATH_SEP ':'
68+#endif
69+
70+#ifdef __MVCPP__
71+
72+#include <direct.h> // for mkdir()
73+#include <io.h> // for access()
74+#define R_OK 4 // for access(), this checks for read access. 6 should be used for read and write access both.
75+#include <sys/types.h> // for stat()
76+#include <sys/stat.h> // for stat()
77+
78+#endif
79+
80+#include "sdl_dialogs.h"
81+#include "sdl_widgets.h"
82+#include "SoundManager.h" // !
83+
84+#include "preferences.h"
85+
86+#include <boost/bind.hpp>
87+#include <boost/function.hpp>
88+#include <boost/algorithm/string/predicate.hpp>
89+
90+// From shell_sdl.cpp
91+extern vector<DirectorySpecifier> data_search_path;
92+extern DirectorySpecifier local_data_dir, preferences_dir, saved_games_dir, recordings_dir;
93+
94+extern bool is_applesingle(SDL_RWops *f, bool rsrc_fork, int32 &offset, int32 &length);
95+extern bool is_macbinary(SDL_RWops *f, int32 &data_length, int32 &rsrc_length);
96+
97+/*
98+ * Opened file
99+ */
100+
101+OpenedFile::OpenedFile() : f(NULL), err(0), is_forked(false), fork_offset(0), fork_length(0) {}
102+
103+bool OpenedFile::IsOpen()
104+{
105+ return f != NULL;
106+}
107+
108+bool OpenedFile::Close()
109+{
110+ if (f) {
111+ SDL_RWclose(f);
112+ f = NULL;
113+ err = 0;
114+ }
115+ is_forked = false;
116+ fork_offset = 0;
117+ fork_length = 0;
118+ return true;
119+}
120+
121+bool OpenedFile::GetPosition(int32 &Position)
122+{
123+ if (f == NULL)
124+ return false;
125+
126+ err = 0;
127+ Position = SDL_RWtell(f) - fork_offset;
128+ return true;
129+}
130+
131+bool OpenedFile::SetPosition(int32 Position)
132+{
133+ if (f == NULL)
134+ return false;
135+
136+ err = 0;
137+ if (SDL_RWseek(f, Position + fork_offset, SEEK_SET) < 0)
138+ err = errno;
139+ return err == 0;
140+}
141+
142+bool OpenedFile::GetLength(int32 &Length)
143+{
144+ if (f == NULL)
145+ return false;
146+
147+ if (is_forked)
148+ Length = fork_length;
149+ else {
150+ int32 pos = SDL_RWtell(f);
151+ SDL_RWseek(f, 0, SEEK_END);
152+ Length = SDL_RWtell(f);
153+ SDL_RWseek(f, pos, SEEK_SET);
154+ }
155+ err = 0;
156+ return true;
157+}
158+
159+bool OpenedFile::Read(int32 Count, void *Buffer)
160+{
161+ if (f == NULL)
162+ return false;
163+
164+ err = 0;
165+ return (SDL_RWread(f, Buffer, 1, Count) == Count);
166+}
167+
168+bool OpenedFile::Write(int32 Count, void *Buffer)
169+{
170+ if (f == NULL)
171+ return false;
172+
173+ err = 0;
174+ return (SDL_RWwrite(f, Buffer, 1, Count) == Count);
175+}
176+
177+
178+SDL_RWops *OpenedFile::TakeRWops ()
179+{
180+ SDL_RWops *taken = f;
181+ f = NULL;
182+ Close ();
183+ return taken;
184+}
185+
186+/*
187+ * Loaded resource
188+ */
189+
190+LoadedResource::LoadedResource() : p(NULL), size(0) {}
191+
192+bool LoadedResource::IsLoaded()
193+{
194+ return p != NULL;
195+}
196+
197+void LoadedResource::Unload()
198+{
199+ if (p) {
200+ free(p);
201+ p = NULL;
202+ size = 0;
203+ }
204+}
205+
206+size_t LoadedResource::GetLength()
207+{
208+ return size;
209+}
210+
211+void *LoadedResource::GetPointer(bool DoDetach)
212+{
213+ void *ret = p;
214+ if (DoDetach)
215+ Detach();
216+ return ret;
217+}
218+
219+void LoadedResource::SetData(void *data, size_t length)
220+{
221+ Unload();
222+ p = data;
223+ size = length;
224+}
225+
226+void LoadedResource::Detach()
227+{
228+ p = NULL;
229+ size = 0;
230+}
231+
232+
233+/*
234+ * Opened resource file
235+ */
236+
237+OpenedResourceFile::OpenedResourceFile() : f(NULL), saved_f(NULL), err(0) {}
238+
239+bool OpenedResourceFile::Push()
240+{
241+ saved_f = cur_res_file();
242+ if (saved_f != f)
243+ use_res_file(f);
244+ err = 0;
245+ return true;
246+}
247+
248+bool OpenedResourceFile::Pop()
249+{
250+ if (f != saved_f)
251+ use_res_file(saved_f);
252+ err = 0;
253+ return true;
254+}
255+
256+bool OpenedResourceFile::Check(uint32 Type, int16 ID)
257+{
258+ Push();
259+ bool result = has_1_resource(Type, ID);
260+ err = result ? 0 : errno;
261+ Pop();
262+ return result;
263+}
264+
265+bool OpenedResourceFile::Get(uint32 Type, int16 ID, LoadedResource &Rsrc)
266+{
267+ Push();
268+ bool success = get_1_resource(Type, ID, Rsrc);
269+ err = success ? 0 : errno;
270+ Pop();
271+ return success;
272+}
273+
274+bool OpenedResourceFile::IsOpen()
275+{
276+ return f != NULL;
277+}
278+
279+bool OpenedResourceFile::Close()
280+{
281+ if (f) {
282+ close_res_file(f);
283+ f = NULL;
284+ err = 0;
285+ }
286+ return true;
287+}
288+
289+
290+/*
291+ * File specification
292+ */
293+//AS: Constructor moved here to fix linking errors
294+FileSpecifier::FileSpecifier(): err(0) {}
295+const FileSpecifier &FileSpecifier::operator=(const FileSpecifier &other)
296+{
297+ if (this != &other) {
298+ name = other.name;
299+ err = other.err;
300+ }
301+ return *this;
302+}
303+
304+// Create file
305+bool FileSpecifier::Create(Typecode Type)
306+{
307+ Delete();
308+ // files are automatically created when opened for writing
309+ err = 0;
310+ return true;
311+}
312+
313+// Create directory
314+bool FileSpecifier::CreateDirectory()
315+{
316+ err = 0;
317+#if defined(__WIN32__)
318+ if (mkdir(GetPath()) < 0)
319+#else
320+ if (mkdir(GetPath(), 0777) < 0)
321+#endif
322+ err = errno;
323+ return err == 0;
324+}
325+
326+static std::string unix_path_separators(const std::string& input)
327+{
328+ if (PATH_SEP == '/') return input;
329+
330+ std::string output;
331+ for (std::string::const_iterator it = input.begin(); it != input.end(); ++it) {
332+ if (*it == PATH_SEP)
333+ output.push_back('/');
334+ else
335+ output.push_back(*it);
336+ }
337+
338+ return output;
339+}
340+
341+// Open data file
342+bool FileSpecifier::Open(OpenedFile &OFile, bool Writable)
343+{
344+ OFile.Close();
345+
346+ SDL_RWops *f;
347+#ifdef __MACOS__
348+ if (!Writable)
349+ f = OFile.f = open_fork_from_existing_path(GetPath(), false);
350+ else
351+#endif
352+ {
353+#ifdef HAVE_ZZIP
354+ if (!Writable)
355+ {
356+ f = OFile.f = SDL_RWFromZZIP(unix_path_separators(GetPath()).c_str(), "rb");
357+ }
358+ else
359+#endif
360+ f = OFile.f = SDL_RWFromFile(GetPath(), Writable ? "wb+" : "rb");
361+ }
362+
363+ err = f ? 0 : errno;
364+ if (f == NULL) {
365+ set_game_error(systemError, err);
366+ return false;
367+ }
368+ if (Writable)
369+ return true;
370+
371+ // Transparently handle AppleSingle and MacBinary files on reading
372+ int32 offset, data_length, rsrc_length;
373+ if (is_applesingle(f, false, offset, data_length)) {
374+ OFile.is_forked = true;
375+ OFile.fork_offset = offset;
376+ OFile.fork_length = data_length;
377+ SDL_RWseek(f, offset, SEEK_SET);
378+ return true;
379+ } else if (is_macbinary(f, data_length, rsrc_length)) {
380+ OFile.is_forked = true;
381+ OFile.fork_offset = 128;
382+ OFile.fork_length = data_length;
383+ SDL_RWseek(f, 128, SEEK_SET);
384+ return true;
385+ }
386+ SDL_RWseek(f, 0, SEEK_SET);
387+ return true;
388+}
389+
390+// Open resource file
391+bool FileSpecifier::Open(OpenedResourceFile &OFile, bool Writable)
392+{
393+ OFile.Close();
394+
395+ OFile.f = open_res_file(*this);
396+ err = OFile.f ? 0 : errno;
397+ if (OFile.f == NULL) {
398+ set_game_error(systemError, err);
399+ return false;
400+ } else
401+ return true;
402+}
403+
404+// Check for existence of file
405+bool FileSpecifier::Exists()
406+{
407+ // Check whether the file is readable
408+ err = 0;
409+ if (access(GetPath(), R_OK) < 0)
410+ err = errno;
411+
412+#ifdef HAVE_ZZIP
413+ if (err)
414+ {
415+ // Check whether zzip can open the file (slow!)
416+ ZZIP_FILE* file = zzip_open(unix_path_separators(GetPath()).c_str(), R_OK);
417+ if (file)
418+ {
419+ zzip_close(file);
420+ return true;
421+ }
422+ else
423+ {
424+ return false;
425+ }
426+ }
427+#endif
428+ return (err == 0);
429+}
430+
431+bool FileSpecifier::IsDir()
432+{
433+ struct stat st;
434+ err = 0;
435+ if (stat(GetPath(), &st) < 0)
436+ return false;
437+ return (S_ISDIR(st.st_mode));
438+}
439+
440+// Get modification date
441+TimeType FileSpecifier::GetDate()
442+{
443+ struct stat st;
444+ err = 0;
445+ if (stat(GetPath(), &st) < 0) {
446+ err = errno;
447+ return 0;
448+ }
449+ return st.st_mtime;
450+}
451+
452+static const char * alephone_extensions[] = {
453+ ".sceA",
454+ ".sgaA",
455+ ".filA",
456+ ".phyA",
457+ ".shpA",
458+ ".sndA",
459+ 0
460+};
461+
462+std::string FileSpecifier::HideExtension(const std::string& filename)
463+{
464+ if (environment_preferences->hide_extensions)
465+ {
466+ const char **extension = alephone_extensions;
467+ while (*extension)
468+ {
469+ if (boost::algorithm::ends_with(filename, *extension))
470+ {
471+ return filename.substr(0, filename.length() - strlen(*extension));
472+ }
473+
474+ ++extension;
475+ }
476+ }
477+
478+ return filename;
479+}
480+
481+struct extension_mapping
482+{
483+ const char *extension;
484+ bool case_sensitive;
485+ Typecode typecode;
486+};
487+
488+static extension_mapping extensions[] =
489+{
490+ // some common extensions, to speed up building map lists
491+ { "dds", false, _typecode_unknown },
492+ { "jpg", false, _typecode_unknown },
493+ { "png", false, _typecode_unknown },
494+ { "bmp", false, _typecode_unknown },
495+ { "txt", false, _typecode_unknown },
496+ { "ttf", false, _typecode_unknown },
497+
498+ { "lua", false, _typecode_netscript }, // netscript, or unknown?
499+ { "mml", false, _typecode_unknown }, // no type code for this yet
500+
501+ { "sceA", false, _typecode_scenario },
502+ { "sgaA", false, _typecode_savegame },
503+ { "filA", false, _typecode_film },
504+ { "phyA", false, _typecode_physics },
505+ { "ShPa", true, _typecode_shapespatch }, // must come before shpA
506+ { "shpA", false, _typecode_shapes },
507+ { "sndA", false, _typecode_sounds },
508+
509+ {0, false, _typecode_unknown}
510+};
511+
512+// Determine file type
513+Typecode FileSpecifier::GetType()
514+{
515+
516+ // if there's an extension, assume it's correct
517+ const char *extension = strrchr(GetPath(), '.');
518+ if (extension) {
519+ extension_mapping *mapping = extensions;
520+ while (mapping->extension)
521+ {
522+ if (( mapping->case_sensitive && (strcmp(extension + 1, mapping->extension) == 0)) ||
523+ (!mapping->case_sensitive && (strcasecmp(extension + 1, mapping->extension) == 0)))
524+ {
525+ return mapping->typecode;
526+ }
527+ ++mapping;
528+ }
529+ }
530+
531+ // Open file
532+ OpenedFile f;
533+ if (!Open(f))
534+ return _typecode_unknown;
535+ SDL_RWops *p = f.GetRWops();
536+ int32 file_length = 0;
537+ f.GetLength(file_length);
538+
539+ // Check for Sounds file
540+ {
541+ f.SetPosition(0);
542+ uint32 version = SDL_ReadBE32(p);
543+ uint32 tag = SDL_ReadBE32(p);
544+ if ((version == 0 || version == 1) && tag == FOUR_CHARS_TO_INT('s', 'n', 'd', '2'))
545+ return _typecode_sounds;
546+ }
547+
548+ // Check for Map/Physics file
549+ {
550+ f.SetPosition(0);
551+ int version = SDL_ReadBE16(p);
552+ int data_version = SDL_ReadBE16(p);
553+ if ((version == 0 || version == 1 || version == 2 || version == 4) && (data_version == 0 || data_version == 1 || data_version == 2)) {
554+ SDL_RWseek(p, 68, SEEK_CUR);
555+ int32 directory_offset = SDL_ReadBE32(p);
556+ if (directory_offset >= file_length)
557+ goto not_map;
558+ f.SetPosition(128);
559+ uint32 tag = SDL_ReadBE32(p);
560+ // ghs: I do not believe this list is comprehensive
561+ // I think it's just what we've seen so far?
562+ switch (tag) {
563+ case LINE_TAG:
564+ case POINT_TAG:
565+ case SIDE_TAG:
566+ return _typecode_scenario;
567+ break;
568+ case MONSTER_PHYSICS_TAG:
569+ return _typecode_physics;
570+ break;
571+ }
572+
573+ }
574+not_map: ;
575+ }
576+
577+ // Check for Shapes file
578+ {
579+ f.SetPosition(0);
580+ for (int i=0; i<32; i++) {
581+ uint32 status_flags = SDL_ReadBE32(p);
582+ int32 offset = SDL_ReadBE32(p);
583+ int32 length = SDL_ReadBE32(p);
584+ int32 offset16 = SDL_ReadBE32(p);
585+ int32 length16 = SDL_ReadBE32(p);
586+ if (status_flags != 0
587+ || (offset != NONE && (offset >= file_length || offset + length > file_length))
588+ || (offset16 != NONE && (offset16 >= file_length || offset16 + length16 > file_length)))
589+ goto not_shapes;
590+ SDL_RWseek(p, 12, SEEK_CUR);
591+ }
592+ return _typecode_shapes;
593+not_shapes: ;
594+ }
595+
596+ // Not identified
597+ return _typecode_unknown;
598+}
599+
600+// Get free space on disk
601+bool FileSpecifier::GetFreeSpace(uint32 &FreeSpace)
602+{
603+ // This is impossible to do in a platform-independant way, so we
604+ // just return 16MB which should be enough for everything
605+ FreeSpace = 16 * 1024 * 1024;
606+ err = 0;
607+ return true;
608+}
609+
610+// Exchange two files
611+bool FileSpecifier::Exchange(FileSpecifier &other)
612+{
613+ // Create temporary name (this is cheap, we should make sure that the
614+ // name is not already in use...)
615+ FileSpecifier tmp;
616+ ToDirectory(tmp);
617+ tmp.AddPart("exchange_tmp_file");
618+
619+ err = 0;
620+ if (rename(GetPath(), tmp.GetPath()) < 0)
621+ err = errno;
622+ else
623+ rename(other.GetPath(), GetPath());
624+ if (rename(tmp.GetPath(), other.GetPath()) < 0)
625+ err = errno;
626+ return err == 0;
627+}
628+
629+// Delete file
630+bool FileSpecifier::Delete()
631+{
632+ err = 0;
633+ if (remove(GetPath()) < 0)
634+ err = errno;
635+ return err == 0;
636+}
637+
638+bool FileSpecifier::Rename(const FileSpecifier& Destination)
639+{
640+ return rename(GetPath(), Destination.GetPath()) == 0;
641+}
642+
643+// Set to local (per-user) data directory
644+void FileSpecifier::SetToLocalDataDir()
645+{
646+ name = local_data_dir.name;
647+}
648+
649+// Set to preferences directory
650+void FileSpecifier::SetToPreferencesDir()
651+{
652+ name = preferences_dir.name;
653+}
654+
655+// Set to saved games directory
656+void FileSpecifier::SetToSavedGamesDir()
657+{
658+ name = saved_games_dir.name;
659+}
660+
661+// Set to recordings directory
662+void FileSpecifier::SetToRecordingsDir()
663+{
664+ name = recordings_dir.name;
665+}
666+
667+static string local_path_separators(const char *path)
668+{
669+ string local_path = path;
670+ if (PATH_SEP == '/') return local_path;
671+
672+ for (size_t k = 0; k < local_path.size(); ++k) {
673+ if (local_path[k] == '/')
674+ local_path[k] = PATH_SEP;
675+ }
676+
677+ return local_path;
678+}
679+
680+// Traverse search path, look for file given relative path name
681+bool FileSpecifier::SetNameWithPath(const char *NameWithPath)
682+{
683+ FileSpecifier full_path;
684+ string rel_path = local_path_separators(NameWithPath);
685+
686+ vector<DirectorySpecifier>::const_iterator i = data_search_path.begin(), end = data_search_path.end();
687+ while (i != end) {
688+ full_path = *i + rel_path;
689+ if (full_path.Exists()) {
690+ name = full_path.name;
691+ err = 0;
692+ return true;
693+ }
694+ i++;
695+ }
696+ err = ENOENT;
697+ return false;
698+}
699+
700+bool FileSpecifier::SetNameWithPath(const char* NameWithPath, const DirectorySpecifier& Directory)
701+{
702+ FileSpecifier full_path;
703+ string rel_path = local_path_separators(NameWithPath);
704+
705+ full_path = Directory + rel_path;
706+ if (full_path.Exists()) {
707+ name = full_path.name;
708+ err = 0;
709+ return true;
710+ }
711+
712+ err = ENOENT;
713+ return false;
714+}
715+
716+// Get last element of path
717+void FileSpecifier::GetName(char *part) const
718+{
719+ string::size_type pos = name.rfind(PATH_SEP);
720+ if (pos == string::npos)
721+ strcpy(part, name.c_str());
722+ else
723+ strcpy(part, name.substr(pos + 1).c_str());
724+}
725+
726+// Add part to path name
727+void FileSpecifier::AddPart(const string &part)
728+{
729+ if (name.length() && name[name.length() - 1] == PATH_SEP)
730+ name += local_path_separators(part.c_str());
731+ else
732+ name = name + PATH_SEP + local_path_separators(part.c_str());
733+
734+ canonicalize_path();
735+}
736+
737+// Split path to base and last part
738+void FileSpecifier::SplitPath(string &base, string &part) const
739+{
740+ string::size_type pos = name.rfind(PATH_SEP);
741+ if (pos == string::npos) {
742+ base = name;
743+ part.erase();
744+ } else if (pos == 0) {
745+ base = PATH_SEP;
746+ part = name.substr(1);
747+ } else {
748+ base = name.substr(0, pos);
749+ part = name.substr(pos + 1);
750+ }
751+}
752+
753+// Fill file specifier with base name
754+void FileSpecifier::ToDirectory(DirectorySpecifier &dir)
755+{
756+ string part;
757+ SplitPath(dir, part);
758+}
759+
760+// Set file specifier from directory specifier
761+void FileSpecifier::FromDirectory(DirectorySpecifier &Dir)
762+{
763+ name = Dir.name;
764+}
765+
766+// Canonicalize path
767+void FileSpecifier::canonicalize_path(void)
768+{
769+#if !defined(__WIN32__)
770+
771+ // Replace multiple consecutive '/'s by a single '/'
772+ while (true) {
773+ string::size_type pos = name.find("//");
774+ if (pos == string::npos)
775+ break;
776+ name.erase(pos, 1);
777+ }
778+
779+#endif
780+
781+ // Remove trailing '/'
782+ // ZZZ: only if we're not naming the root directory /
783+ if (!name.empty() && name[name.size()-1] == PATH_SEP && name.size() != 1)
784+ name.erase(name.size()-1, 1);
785+}
786+
787+// Read directory contents
788+bool FileSpecifier::ReadDirectory(vector<dir_entry> &vec)
789+{
790+ vec.clear();
791+
792+#if defined(__MVCPP__)
793+
794+ WIN32_FIND_DATA findData;
795+
796+ // We need to add a wildcard to the search name
797+ string search_name;
798+ search_name = name;
799+ search_name += "\\*.*";
800+
801+ HANDLE hFind = ::FindFirstFile(search_name.c_str(), &findData);
802+
803+ if (hFind == INVALID_HANDLE_VALUE) {
804+ err = ::GetLastError();
805+ return false;
806+ }
807+
808+ do {
809+ // Exclude current and parent directories
810+ if (findData.cFileName[0] != '.' ||
811+ (findData.cFileName[1] && findData.cFileName[1] != '.')) {
812+ // Return found files to dir_entry
813+ int32 fileSize = (findData.nFileSizeHigh * MAXDWORD) + findData.nFileSizeLow;
814+ vec.push_back(dir_entry(findData.cFileName, fileSize,
815+ (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0, false));
816+ }
817+ } while(::FindNextFile(hFind, &findData));
818+
819+ if (!::FindClose(hFind))
820+ err = ::GetLastError(); // not sure if we should return this or not
821+ else
822+ err = 0;
823+ return true;
824+
825+#else
826+
827+ DIR *d = opendir(GetPath());
828+
829+ if (d == NULL) {
830+ err = errno;
831+ return false;
832+ }
833+ struct dirent *de = readdir(d);
834+ while (de) {
835+ FileSpecifier full_path = name;
836+ full_path += de->d_name;
837+ struct stat st;
838+ if (stat(full_path.GetPath(), &st) == 0) {
839+ // Ignore files starting with '.' and the directories '.' and '..'
840+ if (de->d_name[0] != '.' || (S_ISDIR(st.st_mode) && !(de->d_name[1] == '\0' || de->d_name[1] == '.')))
841+ vec.push_back(dir_entry(de->d_name, st.st_size, S_ISDIR(st.st_mode), false, st.st_mtime));
842+ }
843+ de = readdir(d);
844+ }
845+ closedir(d);
846+ err = 0;
847+ return true;
848+
849+#endif
850+}
851+
852+// Copy file contents
853+bool FileSpecifier::CopyContents(FileSpecifier &source_name)
854+{
855+ err = 0;
856+ OpenedFile src, dst;
857+ if (source_name.Open(src)) {
858+ Delete();
859+ if (Open(dst, true)) {
860+ const int BUFFER_SIZE = 1024;
861+ uint8 buffer[BUFFER_SIZE];
862+
863+ int32 length = 0;
864+ src.GetLength(length);
865+
866+ while (length && err == 0) {
867+ int32 count = length > BUFFER_SIZE ? BUFFER_SIZE : length;
868+ if (src.Read(count, buffer)) {
869+ if (!dst.Write(count, buffer))
870+ err = dst.GetError();
871+ } else
872+ err = src.GetError();
873+ length -= count;
874+ }
875+ }
876+ } else
877+ err = source_name.GetError();
878+ if (err)
879+ Delete();
880+ return err == 0;
881+}
882+
883+// ZZZ: Filesystem browsing list that lets user actually navigate directories...
884+class w_directory_browsing_list : public w_list<dir_entry>
885+{
886+public:
887+ w_directory_browsing_list(const FileSpecifier& inStartingDirectory, dialog* inParentDialog)
888+ : w_list<dir_entry>(entries, 400, 15, 0), parent_dialog(inParentDialog), current_directory(inStartingDirectory), sort_order(sort_by_name)
889+ {
890+ refresh_entries();
891+ }
892+
893+
894+ w_directory_browsing_list(const FileSpecifier& inStartingDirectory, dialog* inParentDialog, const string& inStartingFile)
895+ : w_list<dir_entry>(entries, 400, 15, 0), parent_dialog(inParentDialog), current_directory(inStartingDirectory)
896+ {
897+ refresh_entries();
898+ if(entries.size() != 0)
899+ select_entry(inStartingFile, false);
900+ }
901+
902+
903+ void set_directory_changed_callback(action_proc inCallback, void* inArg = NULL)
904+ {
905+ directory_changed_proc = inCallback;
906+ directory_changed_proc_arg = inArg;
907+ }
908+
909+
910+ void draw_item(vector<dir_entry>::const_iterator i, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const
911+ {
912+ y += font->get_ascent();
913+ set_drawing_clip_rectangle(0, x, s->h, x + width);
914+
915+ if(i->is_directory)
916+ {
917+ string theName = i->name + "/";
918+ draw_text(s, theName.c_str (), x, y, selected ? get_theme_color (ITEM_WIDGET, ACTIVE_STATE) : get_theme_color (ITEM_WIDGET, DEFAULT_STATE), font, style, true);
919+ }
920+ else
921+ {
922+ char date[256];
923+ tm *time_info = localtime(&i->date);
924+
925+ if (time_info)
926+ {
927+ strftime(date, 256, "%x %R", time_info);
928+ int date_width = text_width(date, font, style);
929+ draw_text(s, date, x + width - date_width, y, selected ? get_theme_color(ITEM_WIDGET, ACTIVE_STATE) : get_theme_color(ITEM_WIDGET, DEFAULT_STATE), font, style);
930+ set_drawing_clip_rectangle(0, x, s->h, x + width - date_width - 4);
931+ }
932+ draw_text(s, FileSpecifier::HideExtension(i->name).c_str (), x, y, selected ? get_theme_color (ITEM_WIDGET, ACTIVE_STATE) : get_theme_color (ITEM_WIDGET, DEFAULT_STATE), font, style, true);
933+ }
934+
935+ set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
936+ }
937+
938+
939+ bool can_move_up_a_level()
940+ {
941+ string base;
942+ string part;
943+ current_directory.SplitPath(base, part);
944+ return (part != string());
945+ }
946+
947+
948+ void move_up_a_level()
949+ {
950+ string base;
951+ string part;
952+ current_directory.SplitPath(base, part);
953+ if(part != string())
954+ {
955+ FileSpecifier parent_directory(base);
956+ if(parent_directory.Exists())
957+ {
958+ current_directory = parent_directory;
959+ refresh_entries();
960+ select_entry(part, true);
961+ announce_directory_changed();
962+ }
963+ }
964+ }
965+
966+
967+ void item_selected(void)
968+ {
969+ current_directory.AddPart(entries[selection].name);
970+
971+ if(entries[selection].is_directory)
972+ {
973+ refresh_entries();
974+ announce_directory_changed();
975+ }
976+ else if (file_selected)
977+ {
978+ file_selected(entries[selection].name);
979+ }
980+ }
981+
982+ enum SortOrder {
983+ sort_by_name,
984+ sort_by_date,
985+ };
986+
987+ void sort_by(SortOrder order)
988+ {
989+ sort_order = order;
990+ refresh_entries();
991+ }
992+
993+
994+ const FileSpecifier& get_file() { return current_directory; }
995+
996+ boost::function<void(const std::string&)> file_selected;
997+
998+private:
999+ vector<dir_entry> entries;
1000+ dialog* parent_dialog;
1001+ FileSpecifier current_directory;
1002+ action_proc directory_changed_proc;
1003+ void* directory_changed_proc_arg;
1004+ SortOrder sort_order;
1005+
1006+ struct most_recent
1007+ {
1008+ bool operator()(const dir_entry& a, const dir_entry& b)
1009+ {
1010+ return a.date > b.date;
1011+ }
1012+ };
1013+
1014+ void refresh_entries()
1015+ {
1016+ if(current_directory.ReadDirectory(entries))
1017+ {
1018+ if (sort_order == sort_by_name)
1019+ {
1020+ sort(entries.begin(), entries.end());
1021+ }
1022+ else
1023+ {
1024+ sort(entries.begin(), entries.end(), most_recent());
1025+ }
1026+ }
1027+ num_items = entries.size();
1028+ new_items();
1029+ }
1030+
1031+ void select_entry(const string& inName, bool inIsDirectory)
1032+ {
1033+ dir_entry theEntryToFind(inName, NONE /* length - ignored for our purpose */, inIsDirectory);
1034+ vector<dir_entry>::iterator theEntry = find(entries.begin(), entries.end(), theEntryToFind);
1035+ if(theEntry != entries.end())
1036+ set_selection(theEntry - entries.begin());
1037+ }
1038+
1039+ void announce_directory_changed()
1040+ {
1041+ if(directory_changed_proc != NULL)
1042+ directory_changed_proc(directory_changed_proc_arg);
1043+ }
1044+};
1045+
1046+const char* sort_by_labels[] = {
1047+ "名前",
1048+ "更新日時",
1049+ 0
1050+};
1051+
1052+// common functionality for read and write dialogs
1053+class FileDialog {
1054+public:
1055+ FileDialog() {
1056+ }
1057+
1058+ bool Run() {
1059+ Layout();
1060+
1061+ bool result = false;
1062+ if (m_dialog.run() == 0)
1063+ {
1064+ result = true;
1065+ }
1066+
1067+ if (get_game_state() == _game_in_progress) update_game_window();
1068+ return result;
1069+ }
1070+
1071+protected:
1072+ void Init(const FileSpecifier& dir, w_directory_browsing_list::SortOrder default_order, std::string filename) {
1073+ m_sort_by_w = new w_select(static_cast<size_t>(default_order), sort_by_labels);
1074+ m_sort_by_w->set_selection_changed_callback(boost::bind(&FileDialog::on_change_sort_order, this));
1075+ m_up_button_w = new w_button("上の階層へ", boost::bind(&FileDialog::on_up, this));
1076+ if (filename.empty())
1077+ {
1078+ m_list_w = new w_directory_browsing_list(dir, &m_dialog);
1079+ }
1080+ else
1081+ {
1082+ m_list_w = new w_directory_browsing_list(dir, &m_dialog, filename);
1083+ }
1084+ m_list_w->sort_by(default_order);
1085+ m_list_w->set_directory_changed_callback(boost::bind(&FileDialog::on_directory_changed, this));
1086+
1087+ dir.GetName(temporary);
1088+ m_directory_name_w = new w_static_text(temporary);
1089+ }
1090+
1091+ dialog m_dialog;
1092+ w_select* m_sort_by_w;
1093+ w_button* m_up_button_w;
1094+ w_static_text* m_directory_name_w;
1095+ w_directory_browsing_list* m_list_w;
1096+
1097+private:
1098+ virtual void Layout() = 0;
1099+
1100+
1101+ void on_directory_changed() {
1102+ m_list_w->get_file().GetName(temporary);
1103+ m_directory_name_w->set_text(temporary);
1104+
1105+ m_up_button_w->set_enabled(m_list_w->can_move_up_a_level());
1106+
1107+ m_dialog.draw();
1108+ }
1109+
1110+ void on_change_sort_order() {
1111+ m_list_w->sort_by(static_cast<w_directory_browsing_list::SortOrder>(m_sort_by_w->get_selection()));
1112+ }
1113+
1114+ void on_up() {
1115+ m_list_w->move_up_a_level();
1116+ }
1117+
1118+};
1119+
1120+class ReadFileDialog : public FileDialog
1121+{
1122+public:
1123+ ReadFileDialog(FileSpecifier dir, Typecode type, const char* prompt) : FileDialog(), m_prompt(prompt) {
1124+ w_directory_browsing_list::SortOrder default_order = w_directory_browsing_list::sort_by_name;
1125+
1126+ if (!m_prompt)
1127+ {
1128+ switch(type)
1129+ {
1130+ case _typecode_savegame:
1131+ m_prompt = "ゲーム再開";
1132+ break;
1133+ case _typecode_film:
1134+ m_prompt = "フィルムを再生";
1135+ break;
1136+ default:
1137+ m_prompt = "ファイルを開く";
1138+ break;
1139+ }
1140+ }
1141+
1142+ std::string filename;
1143+ switch (type)
1144+ {
1145+ case _typecode_savegame:
1146+ dir.SetToSavedGamesDir();
1147+ default_order = w_directory_browsing_list::sort_by_date;
1148+ break;
1149+ case _typecode_film:
1150+ dir.SetToRecordingsDir();
1151+ break;
1152+ case _typecode_scenario:
1153+ case _typecode_netscript:
1154+ {
1155+ // Go to most recently-used directory
1156+ DirectorySpecifier theDirectory;
1157+ dir.SplitPath(theDirectory, filename);
1158+ dir.FromDirectory(theDirectory);
1159+ if (!dir.Exists())
1160+ dir.SetToLocalDataDir();
1161+ }
1162+ break;
1163+ default:
1164+ dir.SetToLocalDataDir();
1165+ break;
1166+ }
1167+
1168+ Init(dir, default_order, filename);
1169+
1170+ m_list_w->file_selected = boost::bind(&ReadFileDialog::on_file_selected, this);
1171+ }
1172+
1173+ void Layout() {
1174+ vertical_placer* placer = new vertical_placer;
1175+ placer->dual_add(new w_title(m_prompt), m_dialog);
1176+ placer->add(new w_spacer, true);
1177+
1178+ placer->dual_add(m_directory_name_w, m_dialog);
1179+
1180+ placer->add(new w_spacer(), true);
1181+
1182+ horizontal_placer* top_row_placer = new horizontal_placer;
1183+
1184+ top_row_placer->dual_add(m_sort_by_w->label("ソ\ート順:"), m_dialog);
1185+ top_row_placer->dual_add(m_sort_by_w, m_dialog);
1186+ top_row_placer->add_flags(placeable::kFill);
1187+ top_row_placer->add(new w_spacer, true);
1188+ top_row_placer->add_flags();
1189+ top_row_placer->dual_add(m_up_button_w, m_dialog);
1190+
1191+ placer->add_flags(placeable::kFill);
1192+ placer->add(top_row_placer, true);
1193+ placer->add_flags();
1194+
1195+ placer->dual_add(m_list_w, m_dialog);
1196+ placer->add(new w_spacer, true);
1197+
1198+ horizontal_placer* button_placer = new horizontal_placer;
1199+ button_placer->dual_add(new w_button("キャンセル", dialog_cancel, &m_dialog), m_dialog);
1200+
1201+ placer->add(button_placer, true);
1202+
1203+ m_dialog.activate_widget(m_list_w);
1204+ m_dialog.set_widget_placer(placer);
1205+ }
1206+
1207+ FileSpecifier GetFile() {
1208+ return m_list_w->get_file();
1209+ }
1210+
1211+private:
1212+ void on_file_selected() {
1213+ m_dialog.quit(0);
1214+ }
1215+
1216+ const char* m_prompt;
1217+ std::string m_filename;
1218+};
1219+
1220+bool FileSpecifier::ReadDialog(Typecode type, const char *prompt)
1221+{
1222+ ReadFileDialog d(*this, type, prompt);
1223+ if (d.Run())
1224+ {
1225+ *this = d.GetFile();
1226+ return true;
1227+ }
1228+ else
1229+ {
1230+ return false;
1231+ }
1232+}
1233+
1234+class w_file_name : public w_text_entry {
1235+public:
1236+ w_file_name(dialog *d, const char *initial_name = NULL) : w_text_entry(31, initial_name), parent(d) {}
1237+ ~w_file_name() {}
1238+
1239+ void event(SDL_Event & e)
1240+ {
1241+ // Return = close dialog
1242+ if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RETURN)
1243+ parent->quit(0);
1244+ w_text_entry::event(e);
1245+ }
1246+
1247+private:
1248+ dialog *parent;
1249+};
1250+
1251+class WriteFileDialog : public FileDialog
1252+{
1253+public:
1254+ WriteFileDialog(FileSpecifier dir, Typecode type, const char* prompt, const char* default_name) : FileDialog(), m_prompt(prompt), m_default_name(default_name), m_extension(0) {
1255+ if (!m_prompt)
1256+ {
1257+ switch (type)
1258+ {
1259+ case _typecode_savegame:
1260+ prompt = "ゲーム保存";
1261+ break;
1262+ case _typecode_film:
1263+ prompt = "フィルム保存";
1264+ break;
1265+ default:
1266+ prompt = "ファイル保存";
1267+ break;
1268+ }
1269+ }
1270+
1271+ switch (type)
1272+ {
1273+ case _typecode_savegame:
1274+ m_extension = ".sgaA";
1275+ break;
1276+ case _typecode_film:
1277+ m_extension = ".filA";
1278+ break;
1279+ default:
1280+ break;
1281+ }
1282+
1283+ if (m_extension && boost::algorithm::ends_with(m_default_name, m_extension))
1284+ {
1285+ m_default_name.resize(m_default_name.size() - strlen(m_extension));
1286+ }
1287+
1288+ w_directory_browsing_list::SortOrder default_order = w_directory_browsing_list::sort_by_name;
1289+ switch (type) {
1290+ case _typecode_savegame:
1291+ {
1292+ string base;
1293+ string part;
1294+ dir.SplitPath(base, part);
1295+ if (part != string())
1296+ {
1297+ dir = base;
1298+ }
1299+ default_order = w_directory_browsing_list::sort_by_date;
1300+ }
1301+ break;
1302+ case _typecode_film:
1303+ dir.SetToRecordingsDir();
1304+ break;
1305+ default:
1306+ dir.SetToLocalDataDir();
1307+ break;
1308+ }
1309+
1310+ Init(dir, default_order, m_default_name);
1311+
1312+ m_list_w->file_selected = boost::bind(&WriteFileDialog::on_file_selected, this, _1);
1313+ }
1314+
1315+ void Layout() {
1316+ vertical_placer* placer = new vertical_placer;
1317+ placer->dual_add(new w_title(m_prompt), m_dialog);
1318+ placer->add(new w_spacer, true);
1319+
1320+ placer->dual_add(m_directory_name_w, m_dialog);
1321+
1322+ placer->add(new w_spacer(), true);
1323+
1324+ horizontal_placer* top_row_placer = new horizontal_placer;
1325+
1326+ top_row_placer->dual_add(m_sort_by_w->label("ソ\ート順:"), m_dialog);
1327+ top_row_placer->dual_add(m_sort_by_w, m_dialog);
1328+ top_row_placer->add_flags(placeable::kFill);
1329+ top_row_placer->add(new w_spacer, true);
1330+ top_row_placer->add_flags();
1331+ top_row_placer->dual_add(m_up_button_w, m_dialog);
1332+
1333+ placer->add_flags(placeable::kFill);
1334+ placer->add(top_row_placer, true);
1335+ placer->add_flags();
1336+
1337+ placer->dual_add(m_list_w, m_dialog);
1338+ placer->add(new w_spacer, true);
1339+
1340+ placer->add_flags(placeable::kFill);
1341+
1342+ horizontal_placer* file_name_placer = new horizontal_placer;
1343+ m_name_w = new w_file_name(&m_dialog, m_default_name.c_str());
1344+ file_name_placer->dual_add(m_name_w->label("ファイル名:"), m_dialog);
1345+ file_name_placer->add_flags(placeable::kFill);
1346+ file_name_placer->dual_add(m_name_w, m_dialog);
1347+
1348+ placer->add_flags(placeable::kFill);
1349+ placer->add(file_name_placer, true);
1350+ placer->add_flags();
1351+ placer->add(new w_spacer, true);
1352+
1353+ horizontal_placer* button_placer = new horizontal_placer;
1354+ button_placer->dual_add(new w_button("OK", dialog_ok, &m_dialog), m_dialog);
1355+ button_placer->dual_add(new w_button("キャンセル", dialog_cancel, &m_dialog), m_dialog);
1356+
1357+ placer->add(button_placer, true);
1358+
1359+ m_dialog.activate_widget(m_name_w);
1360+ m_dialog.set_widget_placer(placer);
1361+ }
1362+
1363+ FileSpecifier GetPath() {
1364+ FileSpecifier dir = m_list_w->get_file();
1365+ std::string base;
1366+ std::string part;
1367+ dir.SplitPath(base, part);
1368+
1369+ std::string filename = GetFilename();
1370+ if (part == filename)
1371+ {
1372+ dir = base;
1373+ }
1374+
1375+ if (m_extension && !boost::algorithm::ends_with(filename, m_extension))
1376+ {
1377+ filename += m_extension;
1378+ }
1379+ dir.AddPart(filename);
1380+ return dir;
1381+ }
1382+
1383+ std::string GetFilename() {
1384+ return m_name_w->get_text();
1385+ }
1386+
1387+private:
1388+ void on_file_selected(const std::string& filename) {
1389+ m_name_w->set_text(filename.c_str());
1390+ m_dialog.quit(0);
1391+ }
1392+
1393+ const char* m_prompt;
1394+ std::string m_default_name;
1395+ const char* m_extension;
1396+ w_file_name* m_name_w;
1397+};
1398+
1399+static bool confirm_save_choice(FileSpecifier & file);
1400+
1401+bool FileSpecifier::WriteDialog(Typecode type, const char *prompt, const char *default_name)
1402+{
1403+again:
1404+ WriteFileDialog d(*this, type, prompt, default_name);
1405+ bool result = false;
1406+ if (d.Run())
1407+ {
1408+ if (d.GetFilename().empty())
1409+ {
1410+ play_dialog_sound(DIALOG_ERROR_SOUND);
1411+ goto again;
1412+ }
1413+
1414+ *this = d.GetPath();
1415+
1416+ if (!confirm_save_choice(*this))
1417+ {
1418+ goto again;
1419+ }
1420+
1421+ result = true;
1422+ }
1423+
1424+ return result;
1425+
1426+}
1427+
1428+bool FileSpecifier::WriteDialogAsync(Typecode type, char *prompt, char *default_name)
1429+{
1430+ return FileSpecifier::WriteDialog(type, prompt, default_name);
1431+}
1432+
1433+static bool confirm_save_choice(FileSpecifier & file)
1434+{
1435+ // If the file doesn't exist, everything is alright
1436+ if (!file.Exists())
1437+ return true;
1438+
1439+ // Construct message
1440+ char name[256];
1441+ file.GetName(name);
1442+ char message[512];
1443+ sprintf(message, "'%sは、すでに存在しています。", name);
1444+
1445+ // Create dialog
1446+ dialog d;
1447+ vertical_placer *placer = new vertical_placer;
1448+ placer->dual_add(new w_static_text(message), d);
1449+ placer->dual_add(new w_static_text("上書きしてもよろしいですか?"), d);
1450+ placer->add(new w_spacer(), true);
1451+
1452+ horizontal_placer *button_placer = new horizontal_placer;
1453+ w_button *default_button = new w_button("はい", dialog_ok, &d);
1454+ button_placer->dual_add(default_button, d);
1455+ button_placer->dual_add(new w_button("いいえ", dialog_cancel, &d), d);
1456+
1457+ placer->add(button_placer, true);
1458+
1459+ d.activate_widget(default_button);
1460+
1461+ d.set_widget_placer(placer);
1462+
1463+ // Run dialog
1464+ return d.run() == 0;
1465+}
1466+
1467+ScopedSearchPath::ScopedSearchPath(const DirectorySpecifier& dir)
1468+{
1469+ data_search_path.insert(data_search_path.begin(), dir);
1470+}
1471+
1472+ScopedSearchPath::~ScopedSearchPath()
1473+{
1474+ data_search_path.erase(data_search_path.begin());
1475+}
1476+
1477+#endif
Show on old repository browser