import { WWAData , WWAConsts } from "../wwa_data" ; import { WWADataWithWorldNameStatus } from "./common" ; var SAVE_COMPRESS_ID = { MAP : "map" , MAP_OBJECT : "mapObject" , SYSTEM_MESSAGE : "systemMessage" , USER_VAR : "userVar" } ; var NOT_COMPRESS_ID = { "mapAttribute" : true , "objectAttribute" : true , "message" : true , } ; export class FirstChangedMapUint8Table { public map : Uint8Array ; public mapObject : Uint8Array ; } export default class WWACompress { private static _usingByteFlag = false ; private static MIN_LOOP_COUNT = 3 ; private static _restartData : WWAData = void 0 ; private static _mapByteLength : number = 0 ; private static _firstRandomMapObjectUtf8Table : FirstChangedMapUint8Table = new FirstChangedMapUint8Table ( ) ; param wwaData todo public static compress ( wwaData ) { var saveObject = { } ; var key , value ; for ( key in wwaData ) { if ( NOT_COMPRESS_ID [ key ] ) { continue ; } value = wwaData [ key ] ; switch ( typeof value ) { case "number" : case "string" : case "boolean" : if ( this . _restartData [ key ] === value && key !== "worldName" ) { continue ; } break ; case "object" : if ( value === null ) { if ( this . _restartData [ key ] === value ) { continue ; } } else { value = this . compressObject ( key , value , this . _restartData [ key ] ) ; if ( value === undefined ) { continue ; } } break ; case "undefined" : break ; case "function" : continue ; } saveObject [ key ] = value ; } return saveObject ; } private static compressObject ( key , wwaObject , restartObject ) { var saveObject , mapY , restartMapY , writeMapY ; switch ( key ) { case SAVE_COMPRESS_ID . MAP : case SAVE_COMPRESS_ID . MAP_OBJECT : var newValue , oldValue , addValue , x , y , x_number , id , allIdTableX , allIdTableY , idTableX , idTableY , idText , xList , yList ; saveObject = { } ; allIdTableY = { } ; allIdTableX = { } ; for ( y in wwaObject ) { mapY = wwaObject [ y ] ; restartMapY = restartObject [ y ] ; writeMapY = { } ; oldValue = - 1 ; for ( x in mapY ) { id = mapY [ x ] ; if ( id !== restartMapY [ x ] ) { writeMapY [ x ] = id ; if ( allIdTableY [ id ] === undefined ) { allIdTableY [ id ] = { } ; } idTableY = allIdTableY [ id ] ; if ( idTableY [ y ] === undefined ) { idTableY [ y ] = [ ] ; } xList = idTableY [ y ] ; xList . push ( Number ( x ) ) ; } } } for ( idText in allIdTableY ) { idTableY = allIdTableY [ idText ] ; if ( allIdTableX [ idText ] === undefined ) { allIdTableX [ idText ] = { } ; } idTableX = allIdTableX [ idText ] ; if ( saveObject [ idText ] === undefined ) { saveObject [ idText ] = [ ] ; } oldValue = - 1 ; for ( y in idTableY ) { xList = idTableY [ y ] ; if ( ( xList . length === 1 ) ) { x_number = xList [ 0 ] ; if ( idTableX [ x_number ] === undefined ) { idTableX [ x_number ] = [ ] ; } yList = idTableX [ x_number ] ; yList . push ( Number ( y ) ) ; } else { newValue = Number ( y ) ; addValue = newValue - oldValue - 1 ; oldValue = newValue ; saveObject [ idText ] . push ( this . getCompressArray ( xList ) , addValue ) ; } } } for ( idText in allIdTableX ) { id = Number ( idText ) ; idTableX = allIdTableX [ idText ] ; oldValue = - 1 ; for ( x in idTableX ) { yList = idTableX [ x ] ; newValue = Number ( x ) ; addValue = newValue - oldValue - 1 ; oldValue = newValue ; if ( ( yList . length === 1 ) ) { saveObject [ idText ] . push ( addValue , yList [ 0 ] ) ; } else { saveObject [ idText ] . push ( addValue , this . getCompressArray ( yList ) ) ; } } } var saveList = [ ] ; oldValue = - 1 ; for ( idText in saveObject ) { newValue = Number ( idText ) ; addValue = newValue - oldValue - 1 ; oldValue = newValue ; saveList . push ( addValue , saveObject [ idText ] ) ; } if ( this . _usingByteFlag ) { if ( JSON . stringify ( saveList ) . length >= this . _mapByteLength ) { return this . compressMapAllObject ( wwaObject , restartObject , this . _firstRandomMapObjectUtf8Table [ key ] ) ; } } return saveList ; case SAVE_COMPRESS_ID . SYSTEM_MESSAGE : saveObject = { } ; var key , value ; for ( key in wwaObject ) { value = wwaObject [ key ] ; if ( restartObject [ key ] === value ) { continue ; } saveObject [ key ] = value ; } break ; case SAVE_COMPRESS_ID . USER_VAR : return this . compressUserVars ( wwaObject as number [ ] ) ; default : return wwaObject ; } if ( Object . keys ( saveObject ) . length === 0 ) { return undefined ; } return saveObject ; } /**
     * 0 でないユーザ変数を取り出して、添字とのペアでタプルを作る
     * userVars[10] が 1, userVars[128] が 10 でその他が全部 0 なら
     *  [[10, 1], [128, 10]] となる。
     */ private static compressUserVars ( userVars ) { return userVars . map ( ( value , index ) => ( [ index , value ] ) ) . filter ( ( [ _ , value ] ) => value !== 0 ) ; } param wwaObject param restartObject private static compressMapAllObject ( wwaObject , restartObject , firstRandomMapObjectUtf8Array ) { var x , y , bit , position , idText , lastPosition , count , id ; var mapWidth = this . _restartData . mapWidth ; var idList , indexList , compressClassList , indexText , indexTable , idClassTable , index , indexCount ; var uint8Array = new Uint8Array ( this . _mapByteLength ) ; var startIndex = - 1 ; var len ; bit = 0 ; position = 0 ; lastPosition = 0 ; count = 0 ; indexCount = 0 ; compressClassList = [ ] ; idClassTable = { } ; idList = [ ] ; indexList = [ ] ; for ( y = 0 ; y < mapWidth ; y ++ ) { for ( x = 0 ; x < mapWidth ; x ++ ) { id = wwaObject [ y ] [ x ] ; if ( id !== restartObject [ y ] [ x ] ) { if ( startIndex === - 1 ) { startIndex = position ; } uint8Array [ position ] = uint8Array [ position ] | ( 1 << bit ) ; lastPosition = position ; if ( idClassTable [ id ] === undefined ) { idClassTable [ id ] = new WWACompressIndexTable ( id , indexCount ++ ) ; } idClassTable [ id ] . count ++ ; } bit ++ ; if ( bit === 8 ) { bit = 0 ; position ++ ; } } } for ( idText in idClassTable ) { index = idClassTable [ idText ] . index ; compressClassList [ index ] = idClassTable [ idText ] ; } compressClassList . sort ( this . idSort ) ; indexTable = { } ; for ( indexText in compressClassList ) { id = compressClassList [ indexText ] . id ; indexTable [ id ] = Number ( indexText ) ; idList [ indexText ] = id ; } for ( y = 0 ; y < mapWidth ; y ++ ) { for ( x = 0 ; x < mapWidth ; x ++ ) { id = wwaObject [ y ] [ x ] ; if ( id !== restartObject [ y ] [ x ] ) { indexList [ count ++ ] = indexTable [ id ] ; } } } if ( firstRandomMapObjectUtf8Array ) { len = uint8Array . length ; startIndex = - 1 ; lastPosition = 0 ; for ( position = 0 ; position < len ; position ++ ) { uint8Array [ position ] = uint8Array [ position ] & ( ~ firstRandomMapObjectUtf8Array [ position ] ) ; if ( uint8Array [ position ] !== 0 ) { if ( startIndex === - 1 ) { startIndex = position ; } lastPosition = position ; } } if ( startIndex === - 1 ) { startIndex = 0 ; lastPosition = 0 ; } } return [ startIndex , uint8Array . subarray ( startIndex , lastPosition + 1 ) , idList , this . indexListCompress ( indexList , idList . length ) ] ; } private static indexListCompress ( indexList , idLength ) { var newIndexList = [ ] ; var i , n , s , len , index , nextIndex , repeatCount ; len = indexList . length ; n = 0 ; for ( i = 0 ; i < len ; i ++ ) { index = indexList [ i ] ; repeatCount = 0 ; for ( s = i + 1 ; s < len ; s ++ ) { nextIndex = indexList [ s ] ; if ( index === nextIndex ) { repeatCount ++ ; i = s ; } else { break ; } } switch ( repeatCount ) { case 0 : newIndexList [ n ++ ] = index ; break ; case 1 : newIndexList [ n ++ ] = index ; newIndexList [ n ++ ] = index ; break ; default : newIndexList [ n ++ ] = index ; newIndexList [ n ++ ] = idLength + repeatCount ; break ; } } return newIndexList ; } private static idSort ( a , b ) { return b . count - a . count ; } param list private static getCompressArray ( list ) { var newList = [ ] ; var oldValue , addValue , newValue , i , len ; var k , loopCount , n ; oldValue = - 1 ; len = list . length ; for ( k = 0 , i = 0 ; i < len ; i ++ ) { newValue = list [ i ] ; addValue = newValue - oldValue ; loopCount = 0 ; n = i ; while ( ( n < len - 1 ) && ( list [ n ] + 1 === list [ n + 1 ] ) ) { n ++ ; loopCount ++ ; } if ( loopCount < this . MIN_LOOP_COUNT ) { newList [ k ++ ] = addValue ; } else { i = n ; newValue += loopCount ; newList [ k ++ ] = 0 ; newList [ k ++ ] = addValue ; newList [ k ++ ] = loopCount - this . MIN_LOOP_COUNT ; } oldValue = newValue ; } return newList ; } param list private static getDecompressArray ( list ) { var newList = [ ] ; var oldValue , addValue , newValue , i , len ; var lastValue , k ; oldValue = - 1 ; len = list . length ; for ( i = 0 , k = 0 ; i < len ; i ++ ) { addValue = list [ i ] ; if ( addValue === 0 ) { addValue = list [ ++ i ] ; newValue = oldValue + addValue ; lastValue = newValue + list [ ++ i ] + this . MIN_LOOP_COUNT ; for ( ; newValue <= lastValue ; newValue ++ ) { newList [ k ++ ] = newValue ; } oldValue = lastValue ; } else { newValue = oldValue + addValue ; newList [ k ++ ] = newValue ; oldValue = newValue ; } } return newList ; } param saveObject returns public static decompress ( saveObject ) { var newData ; newData = < WWAData > JSON . parse ( JSON . stringify ( this . _restartData ) ) ; var key , value ; for ( key in saveObject ) { value = saveObject [ key ] ; switch ( typeof value ) { case "number" : case "string" : case "boolean" : break ; case "object" : if ( value !== null ) { value = this . decompressObject ( key , value , newData [ key ] ) ; } break ; case "undefined" : break ; case "function" : continue ; } newData [ key ] = value ; } return [ newData , { isWorldNameEmpty : ( saveObject as WWAData ) . worldName === undefined } ] ; } private static decompressObject ( key , loadObject , newObject ) { var saveObject ; var key ; switch ( key ) { case SAVE_COMPRESS_ID . MAP : case SAVE_COMPRESS_ID . MAP_OBJECT : var newValue , oldValue , addValue , x , y , id , idText ; saveObject = { } ; oldValue = - 1 ; var i , len ; var loadArray = < object [ ] > loadObject ; len = loadArray . length ; if ( len === 4 ) { if ( ( typeof loadArray [ 0 ] === "number" ) && ( ( loadArray [ 1 ] instanceof Uint8Array ) || ( loadArray [ 1 ] instanceof Array ) ) && ( loadArray [ 2 ] instanceof Array ) && ( loadArray [ 3 ] instanceof Array ) ) { this . decompressAllMapObject ( loadArray , newObject , this . _firstRandomMapObjectUtf8Table [ key ] ) ; return newObject ; } } for ( i = 0 ; i < len ; i += 2 ) { addValue = Number ( loadArray [ i ] ) ; newValue = oldValue + addValue + 1 ; oldValue = newValue ; id = newValue ; saveObject [ newValue ] = loadArray [ i + 1 ] ; } var idTableX , idTableY ; for ( idText in saveObject ) { id = Number ( idText ) ; loadArray = < object [ ] > saveObject [ idText ] ; len = loadArray . length ; idTableX = [ ] ; idTableY = [ ] ; for ( i = 0 ; i < len ; i += 2 ) { var xData = loadArray [ i ] ; var yData = loadArray [ i + 1 ] ; if ( xData instanceof Array ) { xData = this . getDecompressArray ( xData ) ; } if ( yData instanceof Array ) { yData = this . getDecompressArray ( yData ) ; } if ( typeof xData === "object" ) { idTableX . push ( { x : xData , y : yData } ) ; } else { idTableY . push ( { x : xData , y : yData } ) ; } } var code ; oldValue = - 1 ; for ( code in idTableX ) { addValue = Number ( idTableX [ code ] . y ) ; newValue = oldValue + addValue + 1 ; oldValue = newValue ; xData = idTableX [ code ] . x ; y = String ( newValue ) ; if ( xData instanceof Array ) { loadArray = < object [ ] > xData ; len = loadArray . length ; for ( i = 0 ; i < len ; i ++ ) { x = String ( loadArray [ i ] ) ; newObject [ y ] [ x ] = id ; } } else { x = String ( xData ) ; newObject [ y ] [ x ] = id ; } } oldValue = - 1 ; for ( code in idTableY ) { addValue = Number ( idTableY [ code ] . x ) ; newValue = oldValue + addValue + 1 ; oldValue = newValue ; yData = idTableY [ code ] . y ; x = String ( newValue ) ; if ( yData instanceof Array ) { loadArray = < object [ ] > yData ; len = loadArray . length ; for ( i = 0 ; i < len ; i ++ ) { y = String ( loadArray [ i ] ) ; newObject [ y ] [ x ] = id ; } } else { y = String ( yData ) ; newObject [ y ] [ x ] = id ; } } } return newObject ; case SAVE_COMPRESS_ID . USER_VAR : return this . decompressUserVars ( loadObject as number [ ] [ ] ) ; case SAVE_COMPRESS_ID . SYSTEM_MESSAGE : default : if ( newObject ) { } else { if ( loadObject ) { if ( newObject instanceof Array ) { newObject = [ ] ; } else { newObject = { } ; } } else { return undefined ; } } for ( key in loadObject ) { newObject [ key ] = loadObject [ key ] ; } return newObject ; } } /**
     * ユーザ変数と添字とのペアからなるタプルからユーザ変数の配列に復元する。
     * [[10, 1], [128, 10]] が与えられた場合、
     * userVars[10] が 1, userVars[128] が 10 でその他が全部 0 の 256要素の配列となる。
     */ private static decompressUserVars ( compressedUserVars ) { return compressedUserVars . reduce ( ( acc , [ index , value ] ) => { acc [ index ] = value ; return acc ; } , this . generateEmptyUserVars ( ) ) ; } private static generateEmptyUserVars ( ) { const userVars = new Array ( WWAConsts . USER_VAR_NUM ) ; for ( let i = 0 ; i < WWAConsts . USER_VAR_NUM ; i ++ ) { userVars [ i ] = 0 ; } return userVars ; } private static decompressAllMapObject ( loadArray , newObject , firstRandomMapObjectUtf8Array ) { var x , y , id , bit , position , count , id ; var mapWidth = this . _restartData . mapWidth ; var loadIndexList , indexList , idList , index , indexCount ; var uint8Array = new Uint8Array ( this . _mapByteLength ) ; var x , y , bit , position , len , count ; var startIndex = 0 ; startIndex = loadArray [ 0 ] ; idList = loadArray [ 2 ] ; loadIndexList = loadArray [ 3 ] ; var uintCopy8Array = < Uint8Array > loadArray [ 1 ] ; uint8Array . set ( uintCopy8Array , startIndex ) ; if ( firstRandomMapObjectUtf8Array ) { len = uint8Array . length ; for ( position = 0 ; position < len ; position ++ ) { uint8Array [ position ] = uint8Array [ position ] | firstRandomMapObjectUtf8Array [ position ] ; } } indexList = [ ] ; var idLength = idList . length ; var len , i , n , repeatCount , k , indexLog ; len = loadIndexList . length ; for ( i = 0 , n = 0 ; i < len ; i ++ ) { index = loadIndexList [ i ] ; if ( index >= idLength ) { repeatCount = index - idLength ; for ( k = 0 ; k < repeatCount ; k ++ ) { indexList [ n ++ ] = indexLog ; } } else { indexList [ n ++ ] = index ; } indexLog = index ; } bit = 0 ; position = 0 ; count = 0 ; indexCount = 0 ; len = uint8Array . length ; for ( position = 0 ; position < len ; position ++ ) { for ( bit = 0 ; bit < 8 ; bit ++ ) { if ( ( uint8Array [ position ] & ( 1 << bit ) ) !== 0 ) { index = indexList [ indexCount ++ ] ; if ( index === undefined ) { return false ; } id = idList [ index ] ; x = count % mapWidth ; y = ( count / mapWidth ) | 0 ; newObject [ y ] [ x ] = id ; } count ++ ; } } return true ; } public static setRestartData ( restartData , firstFrameData ) { this . _restartData = restartData ; this . _mapByteLength = Math . ceil ( restartData . mapWidth * restartData . mapWidth / 8 ) ; this . _firstRandomMapObjectUtf8Table [ SAVE_COMPRESS_ID . MAP_OBJECT ] = this . getChangedUint8Array ( firstFrameData [ SAVE_COMPRESS_ID . MAP_OBJECT ] , restartData [ SAVE_COMPRESS_ID . MAP_OBJECT ] ) ; } private static getChangedUint8Array ( wwaObject , restartObject ) { var x , y , bit , position ; var mapWidth = this . _restartData . mapWidth ; var uint8Array = new Uint8Array ( this . _mapByteLength ) ; var startIndex = - 1 ; bit = 0 ; position = 0 ; for ( y = 0 ; y < mapWidth ; y ++ ) { for ( x = 0 ; x < mapWidth ; x ++ ) { if ( wwaObject [ y ] [ x ] !== restartObject [ y ] [ x ] ) { if ( startIndex === - 1 ) { startIndex = position ; } uint8Array [ position ] = uint8Array [ position ] | ( 1 << bit ) ; } bit ++ ; if ( bit === 8 ) { bit = 0 ; position ++ ; } } } return uint8Array ; } public static getStartWWAData ( resumeSaveTextData ) { if ( ! resumeSaveTextData ) { return this . _restartData ; } try { var resumeSaveData = JSON . parse ( resumeSaveTextData ) ; } catch ( e ) { return this . _restartData ; } if ( resumeSaveData ) { var wwaData ; try { [ wwaData ] = this . decompress ( resumeSaveData ) ; if ( wwaData ) { return wwaData ; } } catch ( e ) { } } return this . _restartData ; } param _usingByteFlag public static usingByte ( _usingByteFlag ) { this . _usingByteFlag = _usingByteFlag ; } } ; export class WWACompressIndexTable { public id : number = 0 ; public index : number = 0 ; public count : number = 0 ; public constructor ( id , index ) { this . id = id ; this . index = index ; } }