the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "SparseDataStorage.h"
3
4// Note: See header for an overview of this class
5
6int SparseDataStorage::deleteQueueIndex;
7XLockFreeStack <unsigned char> SparseDataStorage::deleteQueue[3];
8
9void SparseDataStorage::staticCtor()
10{
11 for( int i = 0; i < 3; i++ )
12 {
13 deleteQueue[i].Initialize();
14 }
15}
16
17// Initialise data storage, with very limited compression - the very first plane is stored as either compressed to be "all 0", and the rest of the planes aren't compressed at all.
18// The reason behind this is to keep the total allocation as a round number of 4K (small) pages, ie 16K.
19// By doing this, and doing this "special" allocation as a XPhysicalAlloc rather than a malloc, we can help ensure that this full allocation gets cleaned up properly when the first
20// proper compression is done on this storage. If it were just allocated with malloc, then the memory management system would have a large number of 16512 allocations to free, and
21// it seems from experimentation that these basically don't make it back to the system as free pages.
22// Note - the other approach here would be to allocate *no* actual storage for the data at the ctor stage. However, as chunks are created then this creates an awful lot of intermediate
23// stages as each line of data is added, so it is actually much cleaner to just allocate almost fully here & then attempt to do a single compression pass over the data later on.
24SparseDataStorage::SparseDataStorage()
25{
26 // Allocate using physical alloc. As this will (by default) return memory from the pool of 4KB pages, the address will in the range of MM_PHYSICAL_4KB_BASE upwards. We can use
27 // this fact to identify the allocation later, and so free it with the corresponding call to XPhysicalFree.
28#ifdef _XBOX
29 unsigned char *planeIndices = (unsigned char *)XPhysicalAlloc(128 * 128, MAXULONG_PTR, 4096, PAGE_READWRITE);
30#else
31 unsigned char *planeIndices = (unsigned char *)malloc(128 * 128);
32#endif
33 unsigned char *data = planeIndices + 128;
34 planeIndices[0] = ALL_0_INDEX;
35 for( int i = 1; i < 128; i++ )
36 {
37 planeIndices[i] = i - 1;
38 }
39 XMemSet(data, 0, 128 * 127);
40
41 // Data and count packs together the pointer to our data and the count of planes allocated - 127 planes allocated in this case
42#pragma warning ( disable : 4826 )
43 dataAndCount = 0x007F000000000000L | (( (__int64) planeIndices ) & 0x0000ffffffffffffL);
44#pragma warning ( default : 4826 )
45#ifdef DATA_COMPRESSION_STATS
46 count = 128;
47#endif
48}
49
50SparseDataStorage::SparseDataStorage(bool isUpper)
51{
52 // Allocate using physical alloc. As this will (by default) return memory from the pool of 4KB pages, the address will in the range of MM_PHYSICAL_4KB_BASE upwards. We can use
53 // this fact to identify the allocation later, and so free it with the corresponding call to XPhysicalFree.
54 unsigned char *planeIndices = (unsigned char *)malloc(128);
55 for( int i = 0; i < 128; i++ )
56 {
57 planeIndices[i] = ALL_0_INDEX;
58 }
59
60 // Data and count packs together the pointer to our data and the count of planes allocated - 127 planes allocated in this case
61#pragma warning ( disable : 4826 )
62 dataAndCount = 0x0000000000000000L | (( (__int64) planeIndices ) & 0x0000ffffffffffffL);
63#pragma warning ( default : 4826 )
64#ifdef DATA_COMPRESSION_STATS
65 count = 128;
66#endif
67}
68
69SparseDataStorage::~SparseDataStorage()
70{
71 unsigned char *indicesAndData = (unsigned char *)(dataAndCount & 0x0000ffffffffffff);
72 // Determine correct means to free this data - could have been allocated either with XPhysicalAlloc or malloc
73
74#ifdef _XBOX
75 if( (unsigned int)indicesAndData >= MM_PHYSICAL_4KB_BASE )
76 {
77 XPhysicalFree(indicesAndData);
78 }
79 else
80#endif
81 {
82 free(indicesAndData);
83 }
84// printf("Free (in dtor) 0x%x\n", indicesAndData);
85}
86
87SparseDataStorage::SparseDataStorage(SparseDataStorage *copyFrom)
88{
89 // Extra details of source storage
90 __int64 sourceDataAndCount = copyFrom->dataAndCount;
91 unsigned char *sourceIndicesAndData = (unsigned char *)(sourceDataAndCount & 0x0000ffffffffffff);
92 int sourceCount = (sourceDataAndCount >> 48 ) & 0xffff;
93
94 // Allocate & copy indices ( 128 bytes ) and any allocated planes (128 * count)
95 unsigned char *destIndicesAndData = (unsigned char *)malloc( sourceCount * 128 + 128 );
96
97 // AP - I've moved this to be before the memcpy because of a very strange bug on vita. Sometimes dataAndCount wasn't valid in time when ::get was called.
98 // This should never happen and this isn't a proper solution but fixes it for now.
99#pragma warning ( disable : 4826 )
100 dataAndCount = ( sourceDataAndCount & 0xffff000000000000L ) | ( ((__int64) destIndicesAndData ) & 0x0000ffffffffffffL );
101#pragma warning ( default : 4826 )
102
103 XMemCpy( destIndicesAndData, sourceIndicesAndData, sourceCount * 128 + 128 );
104
105#ifdef DATA_COMPRESSION_STATS
106 count = sourceCount;
107#endif
108}
109
110// Set all data values from a data array of length 16384 (128 x 16 x 16 x 0.5). Source data must have same order as original java game
111void SparseDataStorage::setData(byteArray dataIn, unsigned int inOffset)
112{
113 // Original order is defined as:
114 // pos = (x << 11 | z << 7 | y);
115 // slot = pos >> 1;
116 // part = pos & 1;
117 // if ( part == 0 ) value = data[slot] & 0xf
118 // else value = (data[slot] >> 4) & 0xf
119
120 // Two passed through the data. First pass sets up plane indices, and counts number of planes that we actually need to allocate
121 int allocatedPlaneCount = 0;
122 unsigned char _planeIndices[128];
123
124 //unsigned char *lastDataPointer = (unsigned char *)(dataAndCount & 0x0000ffffffffffff);
125
126 for( int y = 0; y < 128; y++ )
127 {
128 bool all0 = true;
129
130 for( int xz = 0; xz < 256; xz++ ) // 256 in loop as 16 x 16 separate bytes need checked
131 {
132 int pos = ( xz << 7 ) | y;
133 int slot = pos >> 1;
134 int part = pos & 1;
135 unsigned char value = ( dataIn[slot + inOffset] >> (part * 4) ) & 15;
136 if( value != 0 ) all0 = false;
137 }
138 if( all0 )
139 {
140 _planeIndices[y] = ALL_0_INDEX;
141 }
142 else
143 {
144 _planeIndices[y] = allocatedPlaneCount++;
145 }
146 }
147
148 // Allocate required storage
149 unsigned char *planeIndices = (unsigned char *)malloc(128 * allocatedPlaneCount + 128);
150 unsigned char *data = planeIndices + 128;
151 XMemCpy(planeIndices, _planeIndices, 128);
152
153 // Second pass through to actually copy the data in to the storage allocated for the required planes
154 unsigned char *pucOut = data;
155 for( int y = 0; y < 128 ; y++ )
156 {
157 // Index will be < 128 if we allocated storage for it and it has a valid index. No need to actually check the index as
158 // we know they were sequentially allocated above.
159 if( planeIndices[y] < 128 )
160 {
161 int part = y & 1;
162 //int shift = 4 * part;
163 unsigned char *pucIn = &dataIn[ (y >> 1) + inOffset];
164
165 for( int xz = 0; xz < 128; xz++ ) // 128 ( 16 x 16 x 0.5 ) in loop as packing 2 values into each destination byte
166 {
167 *pucOut = ( ( *pucIn ) >> ( part * 4 ) ) & 15;
168 pucIn += 64;
169
170 *pucOut |= ( ( ( *pucIn ) >> ( part * 4 ) ) & 15 ) << 4;
171 pucIn += 64;
172 pucOut++;
173 }
174 }
175 }
176
177 // Get new data and count packed info
178#pragma warning ( disable : 4826 )
179 __int64 newDataAndCount = ((__int64) planeIndices) & 0x0000ffffffffffffL;
180#pragma warning ( default : 4826 )
181 newDataAndCount |= ((__int64)allocatedPlaneCount) << 48;
182
183 updateDataAndCount( newDataAndCount );
184}
185
186// Gets all data values into an array of length 16384. Destination data will have same order as original java game.
187void SparseDataStorage::getData(byteArray retArray, unsigned int retOffset)
188{
189 XMemSet(retArray.data + + retOffset, 0, 16384);
190 unsigned char *planeIndices, *data;
191 getPlaneIndicesAndData(&planeIndices, &data);
192
193 // Original order is defined as:
194 // pos = (x << 11 | z << 7 | y);
195 // slot = pos >> 1;
196 // part = pos & 1;
197 // if ( part == 0 ) value = data[slot] & 0xf
198 // else value = (data[slot] >> 4) & 0xf
199
200 for( int y = 0; y < 128; y++ )
201 {
202 if( planeIndices[y] == ALL_0_INDEX )
203 {
204 // No need to do anything in this case as retArray is initialised to zero
205 }
206 else
207 {
208 int part = y & 1;
209 int shift = 4 * part;
210 unsigned char *pucOut = &retArray.data[ (y >> 1) + + retOffset];
211 unsigned char *pucIn = &data[ planeIndices[ y ] * 128 ];
212 for( int xz = 0; xz < 128; xz++ ) // 128 in loop (16 x 16 x 0.5) as input data is being treated in pairs of nybbles that are packed in the same byte
213 {
214 unsigned char value = (*pucIn) & 15;
215 *pucOut |= ( value << shift );
216 pucOut += 64;
217
218 value = ((*pucIn) >> 4 ) & 15;
219 *pucOut |= ( value << shift );
220 pucOut += 64;
221
222 pucIn++;
223 }
224 }
225 }
226}
227
228// Get an individual data value
229int SparseDataStorage::get(int x, int y, int z)
230{
231 unsigned char *planeIndices, *data;
232 getPlaneIndicesAndData(&planeIndices, &data);
233
234 if( planeIndices[y] == ALL_0_INDEX )
235 {
236 return 0;
237 }
238 else
239 {
240 int planeIndex = x * 16 + z; // Index within this xz plane
241 int byteIndex = planeIndex / 2; // Byte index within the plane (2 tiles stored per byte)
242 int shift = ( planeIndex & 1 ) * 4; // Bit shift within the byte
243 int retval = ( data[ planeIndices[y] * 128 + byteIndex ] >> shift ) & 15;
244
245 return retval;
246 }
247}
248
249// Set an individual data value
250void SparseDataStorage::set(int x, int y, int z, int val)
251{
252 unsigned char *planeIndices, *data;
253 getPlaneIndicesAndData(&planeIndices, &data);
254
255 // If this plane isn't yet allocated, then we might have some extra work to do
256 if( planeIndices[y] >= ALL_0_INDEX )
257 {
258 // No data allocated. Early out though if we are storing what is already represented by our special index.
259 if( ( val == 0 ) && ( planeIndices[y] == ALL_0_INDEX ) )
260 {
261 return;
262 }
263
264 // Reallocate the storage for planes to accomodate one extra
265 addNewPlane(y);
266
267 // Get pointers again as these may have moved
268 getPlaneIndicesAndData(&planeIndices, &data);
269 }
270
271 // Either data was already allocated, or we've just done that. Now store our value into the right place.
272
273 int planeIndex = x * 16 + z; // Index within this xz plane
274 int byteIndex = planeIndex / 2; // Byte index within the plane (2 tiles stored per byte)
275 int shift = ( planeIndex & 1 ) * 4; // Bit shift within the byte
276 int mask = 0xf0 >> shift;
277
278 int idx = planeIndices[y] * 128 + byteIndex;
279 data[idx] = ( data[idx] & mask ) | ( val << shift );
280
281}
282
283// Sets a region of data values with the data at offset position in the array dataIn - external ordering compatible with java DataLayer
284// Note - when data was extracted from the original data layers by LevelChunk::getBlocksAndData, y0 had to have even alignment and y1 - y0 also
285// needed to be even as data was packed in nyblles in this dimension, and the code didn't make any attempt to unpack it. This behaviour is copied
286// here for compatibility even though our source data isn't packed this way.
287// Returns size of data copied.
288int SparseDataStorage::setDataRegion(byteArray dataIn, int x0, int y0, int z0, int x1, int y1, int z1, int offset, tileUpdatedCallback callback, void *param, int yparam)
289{
290 // Actual setting of data happens when calling set method so no need to lock here
291 unsigned char *pucIn = &dataIn.data[offset];
292 if( callback )
293 {
294 for( int x = x0; x < x1; x++ )
295 {
296 for( int z = z0; z < z1; z++ )
297 {
298 // Emulate how data was extracted from DataLayer... see comment above
299 int yy0 = y0 & 0xfffffffe;
300 int len = ( y1 - y0 ) / 2;
301 for( int i = 0; i < len; i++ )
302 {
303 int y = yy0 + ( i * 2 );
304
305 int toSet = (*pucIn) & 15;
306 if( get(x, y, z) != toSet )
307 {
308 set(x, y, z, toSet );
309 callback(x, y, z, param, yparam);
310 }
311 toSet = ((*pucIn) >> 4 ) & 15;
312 if( get(x, y + 1, z) != toSet )
313 {
314 set(x, y + 1, z, toSet );
315 callback(x, y + 1, z, param, yparam);
316 }
317 pucIn++;
318 }
319 }
320 }
321 }
322 else
323 {
324 for( int x = x0; x < x1; x++ )
325 {
326 for( int z = z0; z < z1; z++ )
327 {
328 // Emulate how data was extracted from DataLayer... see comment above
329 int yy0 = y0 & 0xfffffffe;
330 int len = ( y1 - y0 ) / 2;
331 for( int i = 0; i < len; i++ )
332 {
333 int y = yy0 + ( i * 2 );
334
335 set(x, y, z, (*pucIn) & 15 );
336 set(x, y + 1, z, ((*pucIn) >> 4 ) & 15 );
337 pucIn++;
338 }
339 }
340 }
341 }
342 ptrdiff_t count = pucIn - &dataIn.data[offset];
343
344 return (int)count;
345}
346
347// Updates the data at offset position dataInOut with a region of data information - external ordering compatible with java DataLayer
348// Note - when data was placed in the original data layers by LevelChunk::setBlocksAndData, y0 had to have even alignment and y1 - y0 also
349// needed to be even as data was packed in nyblles in this dimension, and the code didn't make any attempt to unpack it. This behaviour is copied
350// here for compatibility even though our source data isn't packed this way
351// Returns size of data copied.
352int SparseDataStorage::getDataRegion(byteArray dataInOut, int x0, int y0, int z0, int x1, int y1, int z1, int offset)
353{
354 unsigned char *pucOut = &dataInOut.data[offset];
355 for( int x = x0; x < x1; x++ )
356 {
357 for( int z = z0; z < z1; z++ )
358 {
359 // Emulate how data was extracted from DataLayer... see comment above
360 int yy0 = y0 & 0xfffffffe;
361 int len = ( y1 - y0 ) / 2;
362 for( int i = 0; i < len; i++ )
363 {
364 int y = yy0 + ( i * 2 );
365
366 *pucOut = get( x, y, z);
367 *pucOut |= get( x, y + 1, z) << 4;
368 pucOut++;
369 }
370 }
371 }
372 ptrdiff_t count = pucOut - &dataInOut.data[offset];
373
374 return (int)count;
375}
376
377void SparseDataStorage::addNewPlane(int y)
378{
379 bool success = false;
380 do
381 {
382 // Get last packed data pointer & count
383 __int64 lastDataAndCount = dataAndCount;
384
385 // Unpack count & data pointer
386 int lastLinesUsed = (int)(( lastDataAndCount >> 48 ) & 0xffff);
387 unsigned char *lastDataPointer = (unsigned char *)(lastDataAndCount & 0x0000ffffffffffff);
388
389 // Find out what to prefill the newly allocated line with
390 unsigned char planeIndex = lastDataPointer[y];
391
392 if( planeIndex < ALL_0_INDEX ) return; // Something has already allocated this line - we're done
393
394 int linesUsed = lastLinesUsed + 1;
395
396 // Allocate new memory storage, copy over anything from old storage, and initialise remainder
397 unsigned char *dataPointer = (unsigned char *)malloc(linesUsed * 128 + 128);
398 XMemCpy( dataPointer, lastDataPointer, 128 * lastLinesUsed + 128);
399 XMemSet( dataPointer + ( 128 * lastLinesUsed ) + 128, 0, 128 );
400 dataPointer[y] = lastLinesUsed;
401
402 // Get new data and count packed info
403#pragma warning ( disable : 4826 )
404 __int64 newDataAndCount = ((__int64) dataPointer) & 0x0000ffffffffffffL;
405#pragma warning ( default : 4826 )
406 newDataAndCount |= ((__int64)linesUsed) << 48;
407
408 // Attempt to update the data & count atomically. This command will Only succeed if the data stored at
409 // dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place
410 __int64 lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount );
411
412 if( lastDataAndCount2 == lastDataAndCount )
413 {
414 success = true;
415 // Queue old data to be deleted
416 queueForDelete( lastDataPointer );
417// printf("Marking for delete 0x%x\n", lastDataPointer);
418#ifdef DATA_COMPRESSION_STATS
419 count = linesUsed;
420#endif
421 }
422 else
423 {
424 // If we didn't succeed, queue data that we made to be deleted, and try again
425 queueForDelete( dataPointer );
426// printf("Marking for delete (fail) 0x%x\n", dataPointer);
427 }
428 } while( !success );
429}
430
431void SparseDataStorage::getPlaneIndicesAndData(unsigned char **planeIndices, unsigned char **data)
432{
433 unsigned char *indicesAndData = (unsigned char *)(dataAndCount & 0x0000ffffffffffff);
434
435 *planeIndices = indicesAndData;
436 *data = indicesAndData + 128;
437
438}
439
440void SparseDataStorage::queueForDelete(unsigned char *data)
441{
442 // Add this into a queue for deleting. This shouldn't be actually deleted until tick has been called twice from when
443 // the data went into the queue.
444 deleteQueue[deleteQueueIndex].Push( data );
445}
446
447void SparseDataStorage::tick()
448{
449 // We have 3 queues for deleting. Always delete from the next one after where we are writing to, so it should take 2 ticks
450 // before we ever delete something, from when the request to delete it came in
451 int freeIndex = ( deleteQueueIndex + 1 ) % 3;
452
453// printf("Free queue: %d, %d\n",deleteQueue[freeIndex].GetEntryCount(),deleteQueue[freeIndex].GetAllocated());
454 unsigned char *toFree = NULL;
455 do
456 {
457 toFree = deleteQueue[freeIndex].Pop();
458// if( toFree ) printf("Deleting 0x%x\n", toFree);
459 // Determine correct means to free this data - could have been allocated either with XPhysicalAlloc or malloc
460#ifdef _XBOX
461 if( (unsigned int)toFree >= MM_PHYSICAL_4KB_BASE )
462 {
463 XPhysicalFree(toFree);
464 }
465 else
466#endif
467 {
468 free(toFree);
469 }
470 } while( toFree );
471
472 deleteQueueIndex = ( deleteQueueIndex + 1 ) % 3;
473}
474
475// Update storage with a new values for dataAndCount, repeating as necessary if other simultaneous writes happen.
476void SparseDataStorage::updateDataAndCount(__int64 newDataAndCount)
477{
478 // Now actually assign this data to the storage. Just repeat until successful, there isn't any useful really that we can merge the results of this
479 // with any other simultaneous writes that might be happening.
480 bool success = false;
481 do
482 {
483 __int64 lastDataAndCount = dataAndCount;
484 unsigned char *lastDataPointer = (unsigned char *)(lastDataAndCount & 0x0000ffffffffffff);
485
486 // Attempt to update the data & count atomically. This command will Only succeed if the data stored at
487 // dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place
488 __int64 lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount );
489
490 if( lastDataAndCount2 == lastDataAndCount )
491 {
492 success = true;
493 // Queue old data to be deleted
494// printf("Marking for delete 0x%x (full replace)\n", lastDataPointer);
495 queueForDelete( lastDataPointer );
496 }
497 } while( !success);
498
499#ifdef DATA_COMPRESSION_STATS
500 count = ( newDataAndCount >> 48 ) & 0xffff;
501#endif
502
503}
504
505// Attempt to compress the stored data. This method makes no guarantee of success - if it fails due to something else writing to the storage whilst this is running, then it won't actually do anything.
506int SparseDataStorage::compress()
507{
508 unsigned char _planeIndices[128];
509 bool needsCompressed = false;
510
511 __int64 lastDataAndCount = dataAndCount;
512
513 unsigned char *planeIndices = (unsigned char *)(lastDataAndCount & 0x0000ffffffffffff);
514 unsigned char *data = planeIndices + 128;
515
516 int planesToAlloc = 0;
517 for( int i = 0; i < 128; i++ )
518 {
519 if( planeIndices[i] == ALL_0_INDEX )
520 {
521 _planeIndices[i] = ALL_0_INDEX;
522 }
523 else
524 {
525 unsigned char *pucData = &data[ 128 * planeIndices[i] ];
526 bool all0 = true;
527 for( int j = 0; j < 128; j++ ) // 16 x 16 x 4-bits
528 {
529 if( *pucData != 0 ) all0 = false;
530 pucData++;
531 }
532 if( all0 )
533 {
534 _planeIndices[i] = ALL_0_INDEX;
535 needsCompressed = true;
536 }
537 else
538 {
539 _planeIndices[i] = planesToAlloc++;
540 }
541 }
542 }
543
544 if( needsCompressed )
545 {
546 unsigned char *newIndicesAndData = (unsigned char *)malloc( 128 + 128 * planesToAlloc );
547 unsigned char *pucData = newIndicesAndData + 128;
548 XMemCpy( newIndicesAndData, _planeIndices, 128 );
549
550 for( int i = 0; i < 128; i++ )
551 {
552 if( newIndicesAndData[i] < ALL_0_INDEX )
553 {
554 XMemCpy( pucData, &data[ 128 * planeIndices[i] ], 128 );
555 pucData += 128;
556 }
557 }
558
559 // Get new data and count packed info
560#pragma warning ( disable : 4826 )
561 __int64 newDataAndCount = ((__int64) newIndicesAndData) & 0x0000ffffffffffffL;
562#pragma warning ( default : 4826 )
563 newDataAndCount |= ((__int64)planesToAlloc) << 48;
564
565 // Attempt to update the data & count atomically. This command will Only succeed if the data stored at
566 // dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place
567 __int64 lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount );
568
569 if( lastDataAndCount2 != lastDataAndCount )
570 {
571 // Failed to write. Don't bother trying again... being very conservative here.
572// printf("Marking for delete 0x%x (compress fail)\n", newIndicesAndData);
573 queueForDelete( newIndicesAndData );
574 }
575 else
576 {
577 // Success
578 queueForDelete( planeIndices );
579// printf("Successfully compressed to %d planes, to delete 0x%x\n", planesToAlloc, planeIndices);
580#ifdef DATA_COMPRESSION_STATS
581 count = planesToAlloc;
582#endif
583 }
584
585 return planesToAlloc;
586 }
587 else
588 {
589 return (int)((lastDataAndCount >> 48 ) & 0xffff);
590 }
591}
592
593
594bool SparseDataStorage::isCompressed()
595{
596
597 int count = ( dataAndCount >> 48 ) & 0xffff;
598 return (count < 127);
599
600
601}
602
603void SparseDataStorage::write(DataOutputStream *dos)
604{
605 int count = ( dataAndCount >> 48 ) & 0xffff;
606 dos->writeInt(count);
607 unsigned char *dataPointer = (unsigned char *)(dataAndCount & 0x0000ffffffffffff);
608 byteArray wrapper(dataPointer, count * 128 + 128);
609 dos->write(wrapper);
610}
611
612void SparseDataStorage::read(DataInputStream *dis)
613{
614 int count = dis->readInt();
615 unsigned char *dataPointer = (unsigned char *)malloc(count * 128 + 128);
616 byteArray wrapper(dataPointer, count * 128 + 128);
617 dis->readFully(wrapper);
618
619#pragma warning ( disable : 4826 )
620 __int64 newDataAndCount = ((__int64) dataPointer) & 0x0000ffffffffffffL;
621#pragma warning ( default : 4826 )
622 newDataAndCount |= ((__int64)count) << 48;
623
624 updateDataAndCount(newDataAndCount);
625}