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