1 /*
2 * Seven Kingdoms: Ancient Adversaries
3 *
4 * Copyright 1997,1998 Enlight Software Ltd.
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 2 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 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 //Filename : ODYNARRB.CPP
22 //Description : Object Dynamic Array Version B
23
24 #include <OINFO.h>
25 #include <ODYNARRB.h>
26 #include <dbglog.h>
27 #include <file_io_visitor.h>
28
29 using namespace FileIOVisitor;
30
31 DBGLOG_DEFAULT_CHANNEL(DynArray);
32
33 //----------------------------------------------------------//
34 //
35 // Version B is different from Version A as :
36 //
37 // - when linkout() it doesn't not physically shift the memory
38 // upwards.
39 //
40 // - when linkin() it will search for empty rooms in the array
41 // before appending space at the end of the array.
42 //
43 //----------------------------------------------------------//
44
45
46 #define EMPTY_ROOM_ALLOC_STEP 5
47
48
49 //--------- BEGIN OF FUNCTION DynArrayB::DynArrayB -------//
50 //
51 // <int> eleSize = size of each element
52 // [int] blockNum = number of entity of each block of element
53 // increased ( default : 30 )
54 // [int] reuseIntervalDays = no. of game days deleted records needed to be kept before reusing them.
55 // (default: 0)
56 //
DynArrayB(int eleSize,int blockNum,int reuseIntervalDays)57 DynArrayB::DynArrayB(int eleSize,int blockNum,int reuseIntervalDays) : DynArray(eleSize, blockNum)
58 {
59 empty_room_array = NULL;
60 empty_room_num = 0;
61 empty_room_count = 0;
62
63 reuse_interval_days = reuseIntervalDays;
64 }
65 //----------- END OF FUNCTION DynArrayB::DynArrayB -----//
66
67
68 //--------- BEGIN OF FUNCTION DynArrayB::~DynArrayB -------//
69 //
~DynArrayB()70 DynArrayB::~DynArrayB()
71 {
72 if( empty_room_array )
73 mem_del( empty_room_array );
74 }
75 //----------- END OF FUNCTION DynArrayB::DynArrayB -----//
76
77
78 //---------- BEGIN OF FUNCTION DynArrayB::linkin -----------//
79 //
80 // <void*> ent
81 // <int>
82 //
83 // - when linkin() it will search for empty rooms in the array
84 // before appending space at the end of the array.
85 //
86 // - If found, then it will use that room
87 //
88 // - Otherwise, it will link a record at the END of the array
89 //
90 // WARNING : After calling linkin() all pointers to the linklist body
91 // should be updated, because mem_resize() will move the body memory
92 //
linkin(const void * ent)93 void DynArrayB::linkin(const void* ent)
94 {
95 //------- detect for empty rooms --------//
96
97 int reusedFlag=0;
98
99 if( empty_room_count > 0 )
100 {
101 if( reuse_interval_days )
102 {
103 //------ first in, first out approach -----//
104
105 if( info.game_date >= empty_room_array[0].deleted_game_date + reuse_interval_days )
106 {
107 cur_pos = empty_room_array[0].recno;
108
109 memmove( empty_room_array, empty_room_array+1, sizeof(empty_room_array[0]) * (empty_room_count-1) );
110
111 empty_room_count--;
112 reusedFlag = 1;
113 }
114 }
115 else
116 {
117 //------ last in, first out approach -----//
118
119 cur_pos = empty_room_array[empty_room_count-1].recno;
120
121 empty_room_count--;
122 reusedFlag = 1;
123 }
124 }
125
126 if( !reusedFlag )
127 {
128 last_ele++;
129 cur_pos=last_ele;
130 }
131
132 //---------- regular link in -----------//
133
134 if ( last_ele > ele_num ) // not enough empty element left to hold the new entity
135 resize( ele_num + block_num );
136
137 if ( ent )
138 memcpy(body_buf+(cur_pos-1)*ele_size, ent, ele_size );
139 else
140 *(body_buf+(cur_pos-1)*ele_size) = '\0';
141 }
142 //---------- END OF FUNCTION DynArrayB::linkin ------------//
143
144
145
146 //----------- BEGIN OF FUNCTION DynArrayB::linkout ---------//
147 //
148 // - when linkout() it doesn't not physically shift the memory
149 // upwards.
150 //
151 // - it record the address of this empty room at empty room list
152 //
153 // [int] delPos = the position (recno) of the item to be deleted
154 // ( default : recno() current record no. )
155 //
linkout(int delPos)156 void DynArrayB::linkout(int delPos)
157 {
158 if( delPos < 0 )
159 delPos = cur_pos;
160
161 if( delPos == 0 || delPos > last_ele )
162 return;
163
164 //-------- add to the empty room list ---------//
165
166 if( ++empty_room_count > empty_room_num )
167 {
168 empty_room_array = (EmptyRoom*) mem_resize( empty_room_array,
169 (empty_room_num+EMPTY_ROOM_ALLOC_STEP) * sizeof(*empty_room_array) );
170
171 empty_room_num += EMPTY_ROOM_ALLOC_STEP;
172 }
173
174 empty_room_array[empty_room_count-1].recno = delPos;
175 empty_room_array[empty_room_count-1].deleted_game_date = info.game_date;
176
177 memset( body_buf+(delPos-1)*ele_size, 0, ele_size );
178 }
179 //------------ END OF FUNCTION DynArrayB::linkout ----------//
180
181 template <typename Visitor>
visit_dyn_array_b(Visitor * v,DynArrayB * dab)182 static void visit_dyn_array_b(Visitor *v, DynArrayB *dab)
183 {
184 /* DynArray */
185 visit<int32_t>(v, &dab->ele_num);
186 visit<int32_t>(v, &dab->block_num);
187 visit<int32_t>(v, &dab->cur_pos);
188 visit<int32_t>(v, &dab->last_ele);
189 visit<int32_t>(v, &dab->ele_size);
190 visit<int32_t>(v, &dab->sort_offset);
191 visit<int8_t>(v, &dab->sort_type);
192 v->skip(4); /* dab->body_buf */
193
194 /* Not reading DynArrayB members */
195 }
196
197 enum { DYN_ARRAY_B_RECORD_SIZE = 29 };
198
199 //---------- Begin of function DynArrayB::write_file -------------//
200 //
201 // Write current dynamic array into file,
202 // read_file() can be used to retrieve it.
203 //
204 // <File*> writeFile = the pointer to the writing file
205 //
206 // Return : 1 - write successfully
207 // 0 - writing error
208 //
write_file(File * filePtr)209 int DynArrayB::write_file(File* filePtr)
210 {
211 if (!write_with_record_size(filePtr, this, &visit_dyn_array_b<FileWriterVisitor>,
212 DYN_ARRAY_B_RECORD_SIZE))
213 return 0;
214
215 //---------- write body_buf ---------//
216
217 if( last_ele > 0 )
218 {
219 if( !filePtr->file_write( body_buf, ele_size*last_ele ) )
220 return 0;
221 }
222
223 //---------- write empty_room_array ---------//
224
225 write_empty_room(filePtr);
226
227 return 1;
228 }
229 //------------- End of function DynArrayB::write_file --------------//
230
231
232 //---------- Begin of function DynArrayB::read_file -------------//
233 //
234 // Read a saved dynamic array from file, it must be saved with write_file()
235 //
236 // <File*> readFile = the pointer to the writing file
237 //
238 // Return : 1 - read successfully
239 // 0 - writing error
240 //
read_file(File * filePtr)241 int DynArrayB::read_file(File* filePtr)
242 {
243 if (!read_with_record_size(filePtr, this, &visit_dyn_array_b<FileReaderVisitor>,
244 DYN_ARRAY_B_RECORD_SIZE))
245 return 0;
246
247 //---------- read body_buf ---------//
248
249 this->body_buf = mem_resize(this->body_buf, this->ele_num*this->ele_size);
250
251 if( last_ele > 0 )
252 {
253 if( !filePtr->file_read( body_buf, ele_size*last_ele ) )
254 return 0;
255 }
256
257 //---------- read empty_room_array ---------//
258
259 read_empty_room(filePtr);
260
261 //------------------------------------------//
262
263 start(); // go top
264
265 return 1;
266 }
267 //------------- End of function DynArrayB::read_file --------------//
268
269
270 //---------- Begin of function DynArrayB::write_empty_room -------------//
271 //
272 // Write current dynamic array into file,
273 // read_file() can be used to retrieve it.
274 //
275 // <File*> writeFile = the pointer to the writing file
276 //
277 // Return : 1 - write successfully
278 // 0 - writing error
279 //
write_empty_room(File * filePtr)280 int DynArrayB::write_empty_room(File* filePtr)
281 {
282 filePtr->file_put_short( empty_room_count );
283
284 //---------- write empty_room_array ---------//
285
286 if( empty_room_count > 0 )
287 {
288 if( !filePtr->file_write( empty_room_array,
289 sizeof(EmptyRoom) * empty_room_count ) )
290 {
291 return 0;
292 }
293 }
294
295 return 1;
296 }
297 //------------- End of function DynArrayB::write_empty_room --------------//
298
299
300 //---------- Begin of function DynArrayB::read_empty_room -------------//
301 //
302 // Read a saved dynamic array from file, it must be saved with write_file()
303 //
304 // <File*> readFile = the pointer to the writing file
305 //
306 // Return : 1 - read successfully
307 // 0 - writing error
308 //
read_empty_room(File * filePtr)309 int DynArrayB::read_empty_room(File* filePtr)
310 {
311 empty_room_num = empty_room_count = filePtr->file_get_short(); // set both to the same
312
313 //---------- read empty_room_array ---------//
314
315 if( empty_room_count > 0 )
316 {
317 empty_room_array = (EmptyRoom*) mem_resize( empty_room_array,
318 sizeof(EmptyRoom) * empty_room_count );
319
320 if( !filePtr->file_read( empty_room_array,
321 sizeof(*empty_room_array) * empty_room_count ) )
322 {
323 return 0;
324 }
325 }
326 else // when empty_room_count == 0
327 {
328 if( empty_room_array )
329 {
330 mem_del( empty_room_array );
331 empty_room_array = NULL;
332 }
333 }
334
335 //------------------------------------------//
336
337 return 1;
338 }
339 //------------- End of function DynArrayB::read_empty_room --------------//
340
341
342 //---------- Begin of function DynArrayB::packed_recno -------------//
343 //
344 // Given the recno unpacked, it returns the recno packed.
345 //
346 // packed_recno() is the recno when the array is packed
347 // (deleted record are actually removed)
348 //
349 // <int> recNo = given the recno unpacked
350 //
351 // return : <int> the recno when packed
352 //
packed_recno(int recNo) const353 int DynArrayB::packed_recno(int recNo) const
354 {
355 int i, packedRecno = recNo;
356
357 for( i=0 ; i<empty_room_count ; i++ )
358 {
359 if( empty_room_array[i].recno < recNo )
360 packedRecno--;
361 }
362
363 return packedRecno;
364 }
365 //------------- End of function DynArrayB::packed_recno --------------//
366
367
368 //---------- Begin of function DynArrayB::zap -------------//
369 //
370 // Zap the whole dynamic array, clear all elements
371 //
zap()372 void DynArrayB::zap()
373 {
374 DynArray::zap();
375
376 empty_room_count=0; // reset empty rooms
377 }
378 //------------- End of function DynArrayB::zap --------------//
379
380
381 //-------- Start of function DynArrayB::write_ptr_array --------//
382 //
383 // Write a DynArrayB with pointer data elements.
384 //
385 // <File*> filePtr - pointer to the file object.
386 // <int> objectSize - size of the objects pointed to by the pointers.
387 //
write_ptr_array(File * filePtr,int objectSize)388 int DynArrayB::write_ptr_array(File* filePtr, int objectSize)
389 {
390 int i;
391 char* elePtr;
392
393 filePtr->file_put_short( size() );
394
395 for( i=1; i<=size() ; i++ )
396 {
397 elePtr = (char*) get_ptr(i);
398
399 //----- write 0 if the monster is deleted -----//
400
401 if( !elePtr ) // the monster is deleted
402 {
403 filePtr->file_put_short(0);
404 }
405 else
406 {
407 filePtr->file_put_short(1); // the monster exists
408
409 if( !filePtr->file_write(elePtr, objectSize) )
410 return 0;
411 }
412 }
413
414 //------- write empty room array --------//
415
416 write_empty_room(filePtr);
417
418 return 1;
419 }
420 //--------- End of function DynArrayB::write_ptr_array ---------------//
421
422
423 //-------- Start of function DynArrayB::read_ptr_array -------------//
424 //
425 // Read a DynArrayB with pointer data elements previously saved by
426 // write_ptr_array().
427 //
428 // <File*> filePtr - pointer to the file object.
429 // <int> objectSize - size of the objects pointed to by the pointers.
430 // <CreateEleFP> createEleFunc - function for creating a blank object.
431 //
read_ptr_array(File * filePtr,int objectSize,CreateEleFP createEleFunc)432 int DynArrayB::read_ptr_array(File* filePtr, int objectSize, CreateEleFP createEleFunc)
433 {
434 int i;
435 char* elePtr;
436
437 int eleCount = filePtr->file_get_short(); // get no. of monsters from file
438
439 for( i=1 ; i<=eleCount ; i++ )
440 {
441 if( filePtr->file_get_short()==0 ) // the monster has been deleted
442 {
443 add_blank(1); // it's a DynArrayB function
444 }
445 else
446 {
447 elePtr = (*createEleFunc)();
448
449 if( !filePtr->file_read(elePtr, objectSize) )
450 return 0;
451 }
452 }
453
454 //-------- linkout() those record added by add_blank() ----------//
455 //-- So they will be marked deleted in DynArrayB and can be -----//
456 //-- undeleted and used when a new record is going to be added --//
457
458 for( i=size() ; i>0 ; i-- )
459 {
460 DynArrayB::go(i); // since DynArrayB has its own go() which will call GroupArray::go()
461
462 if( get_ptr() == NULL ) // add_blank() record
463 linkout();
464 }
465
466 //------- read empty room array --------//
467
468 read_empty_room(filePtr);
469
470 return 1;
471 }
472 //--------- End of function DynArrayB::read_ptr_array ---------------//
473