void EncodeClassName( char *name, char *identifier ) { RakNet::BitStream bitStream; int index = 0; unsigned char byte; while ( index < MAXIMUM_CLASS_IDENTIFIER_LENGTH - 1 ) { if ( name[ index ] == 0 ) break; // This should generate a unique identifier for any realistic class name that is 5/8th the length of the actual name and weakly encrypts and compresses it if ( name[ index ] >= 'a' && name[ index ] <= 'z' ) byte = name[ index ] - 'a'; else if ( name[ index ] >= 'A' && name[ index ] <= 'Z' ) byte = name[ index ] - 'A'; else if ( name[ index ] >= '0' && name[ index ] <= '9' ) byte = name[ index ] - '0'; else byte = name[ index ] << 3; bitStream.WriteBits( ( unsigned char* ) & byte, 5 ); index++; } #ifdef _DEBUG memset( identifier, 0, MAXIMUM_CLASS_IDENTIFIER_LENGTH ); #endif identifier[ 0 ] = ( char ) ( bitStream.GetNumberOfBytesUsed() ); memcpy( identifier + 1, bitStream.GetData(), bitStream.GetNumberOfBytesUsed() ); }
void Server::initialSync(const RakNet::SystemAddress &sa,running_machine *machine) { unsigned char checksum = 0; waitingForClientCatchup=true; machine->osd().pauseAudio(true); int syncBytes; { RakNet::BitStream uncompressedStream; uncompressedStream.Write(startupTime); uncompressedStream.Write(globalCurtime); if(getSecondsBetweenSync()) { while(memoryBlocksLocked) { ; } memoryBlocksLocked=true; cout << "IN CRITICAL SECTION\n"; cout << "SERVER: Sending initial snapshot\n"; int numBlocks = int(blocks.size()); cout << "NUMBLOCKS: " << numBlocks << endl; uncompressedStream.Write(numBlocks); // NOTE: The server must send stale data to the client for the first time // So that future syncs will be accurate for(int blockIndex=0; blockIndex<int(initialBlocks.size()); blockIndex++) { //cout << "BLOCK SIZE FOR INDEX " << blockIndex << ": " << staleBlocks[blockIndex].size << endl; uncompressedStream.Write(initialBlocks[blockIndex].size); cout << "BLOCK " << blockIndex << ": "; for(int a=0; a<staleBlocks[blockIndex].size; a++) { checksum = checksum ^ staleBlocks[blockIndex].data[a]; //cout << int(staleBlocks[blockIndex].data[a]) << ' '; unsigned char value = initialBlocks[blockIndex].data[a] ^ staleBlocks[blockIndex].data[a]; uncompressedStream.WriteBits(&value,8); } cout << int(checksum) << endl; } } for( map<int,vector< string > >::iterator it = peerInputs.begin(); it != peerInputs.end(); it++ ) { uncompressedStream.Write(it->first); uncompressedStream.Write(int(oldPeerInputs[it->first].size()) + int(it->second.size())); for(int a=0; a<(int)it->second.size(); a++) { uncompressedStream.Write(int(it->second[a].length())); uncompressedStream.WriteBits((const unsigned char*)it->second[a].c_str(),it->second[a].length()*8); for(int b=0;b<it->second[a].length();b++) { checksum = checksum ^ it->second[a][b]; } } for(int a=0; a<int(oldPeerInputs[it->first].size()); a++) { uncompressedStream.Write(int(oldPeerInputs[it->first][a].length())); uncompressedStream.WriteBits((const unsigned char*)oldPeerInputs[it->first][a].c_str(),oldPeerInputs[it->first][a].length()*8); for(int b=0;b<oldPeerInputs[it->first][a].length();b++) { checksum = checksum ^ oldPeerInputs[it->first][a][b]; } } } uncompressedStream.Write(int(-1)); uncompressedStream.Write(checksum); cout << "CHECKSUM: " << int(checksum) << endl; if(uncompressedBufferSize<uncompressedStream.GetNumberOfBytesUsed()+sizeof(int)) { uncompressedBufferSize = uncompressedStream.GetNumberOfBytesUsed()+sizeof(int); free(uncompressedBuffer); uncompressedBuffer = (unsigned char*)malloc(uncompressedBufferSize); if(!uncompressedBuffer) { cout << __FILE__ << ":" << __LINE__ << " OUT OF MEMORY\n"; exit(1); } } syncBytes = uncompressedStream.GetNumberOfBytesUsed(); memcpy(uncompressedBuffer,uncompressedStream.GetData(),uncompressedStream.GetNumberOfBytesUsed()); int uncompressedStateSize = (int)uncompressedStream.GetNumberOfBytesUsed(); printf("INITIAL UNCOMPRESSED STATE SIZE: %d\n",uncompressedStateSize); } cout << "PUTTING NVRAM AT LOCATION " << syncBytes << endl; // open the file; if it exists, call everyone to read from it emu_file file(machine->options().nvram_directory(), OPEN_FLAG_READ); if (file.open(machine->basename(), ".nv") == FILERR_NONE && file.size()<=1024*1024*64) //Don't bother sending huge NVRAM's { int nvramSize = file.size(); if(uncompressedBufferSize<syncBytes+sizeof(int)+nvramSize) { uncompressedBufferSize = syncBytes+sizeof(int)+nvramSize; free(uncompressedBuffer); uncompressedBuffer = (unsigned char*)malloc(uncompressedBufferSize); if(!uncompressedBuffer) { cout << __FILE__ << ":" << __LINE__ << " OUT OF MEMORY\n"; exit(1); } } cout << "SENDING NVRAM OF SIZE: " << nvramSize << endl; memcpy(uncompressedBuffer+syncBytes,&nvramSize,sizeof(int)); file.read(uncompressedBuffer+syncBytes+sizeof(int),nvramSize); file.close(); syncBytes += sizeof(int) + nvramSize; } else { int dummy=0; memcpy(uncompressedBuffer+syncBytes,&dummy,sizeof(int)); syncBytes += sizeof(int); } cout << "BYTES USED: " << syncBytes << endl; //The application should ensure that this value be at least (sourceLen 1.001) + 12. //JJG: Doing more than that to account for header and provide some padding //vector<unsigned char> compressedInitialSyncBuffer( //sizeof(int)*2 + lzmaGetMaxCompressedSize(uncompressedStream.GetNumberOfBytesUsed()), '\0'); //JJG: Take a risk and assume the compressed size will be smaller than the uncompressed size int newCompressedSize = max(1024*1024,int(sizeof(int)*2 + lzmaGetMaxCompressedSize(syncBytes))); if(compressedBufferSize < newCompressedSize ) { compressedBufferSize = newCompressedSize; cout << "NEW COMPRESSED BUFFER SIZE: " << compressedBufferSize << endl; free(compressedBuffer); compressedBuffer = (unsigned char*)malloc(compressedBufferSize); if(!compressedBuffer) { cout << __FILE__ << ":" << __LINE__ << " OUT OF MEMORY\n"; exit(1); } } int compressedSizeLong = compressedBufferSize; //FILE *stateptr = fopen("initialState.dat","wb"); //fwrite(uncompressedStream.GetData(),uncompressedStream.GetNumberOfBytesUsed(),1,stateptr); //fclose(stateptr); lzmaCompress( compressedBuffer+sizeof(int)+sizeof(int), compressedSizeLong, uncompressedBuffer, syncBytes, 6 ); int uncompressedSize = (int)syncBytes; memcpy(compressedBuffer,&uncompressedSize,sizeof(int)); int compressedSize = (int)compressedSizeLong; memcpy(compressedBuffer+sizeof(int),&compressedSize,sizeof(int)); printf("INITIAL UNCOMPRESSED SIZE: %d\n",uncompressedSize); printf("INITIAL COMPRESSED SIZE: %d\n",compressedSize); /* rakInterface->Send( (char*)(&compressedInitialSyncBuffer[0]), compressedSize+1+sizeof(int), HIGH_PRIORITY, RELIABLE_ORDERED, ORDERING_CHANNEL_SYNC, guid, false ); */ // unsigned char *sendPtr = compressedBuffer; int sizeRemaining = compressedSize+sizeof(int)+sizeof(int); printf("INITIAL COMPRESSED SIZE: %dKB\n",sizeRemaining/1024); int packetSize = max(256,min(1024,sizeRemaining/100)); oldInputTime.seconds = oldInputTime.attoseconds = 0; while(sizeRemaining>packetSize) { RakNet::BitStream bitStreamPart(65536); unsigned char header = ID_INITIAL_SYNC_PARTIAL; bitStreamPart.WriteBits((const unsigned char*)&header,8*sizeof(unsigned char)); bitStreamPart.WriteBits((const unsigned char*)sendPtr,8*packetSize); sizeRemaining -= packetSize; sendPtr += packetSize; rakInterface->Send( &bitStreamPart, HIGH_PRIORITY, RELIABLE_ORDERED, ORDERING_CHANNEL_SYNC, sa, false ); ui_update_and_render(*machine, &machine->render().ui_container()); machine->osd().update(false); RakSleep(10); } { RakNet::BitStream bitStreamPart(65536); unsigned char header = ID_INITIAL_SYNC_COMPLETE; bitStreamPart.WriteBits((const unsigned char*)&header,8*sizeof(unsigned char)); bitStreamPart.WriteBits((const unsigned char*)sendPtr,8*sizeRemaining); rakInterface->Send( &bitStreamPart, HIGH_PRIORITY, RELIABLE_ORDERED, ORDERING_CHANNEL_SYNC, sa, false ); ui_update_and_render(*machine, &machine->render().ui_container()); machine->osd().update(false); RakSleep(10); } // cout << "FINISHED SENDING BLOCKS TO CLIENT\n"; cout << "SERVER: Done with initial snapshot\n"; cout << "OUT OF CRITICAL AREA\n"; cout.flush(); memoryBlocksLocked=false; }