Example #1
0
 function loadgame()
 {
     // 4.2 [GameName]
     $this->game['name'] = '';
     for ($i = 0; $this->data[$i] != chr(0); $i++) {
         $this->game['name'] .= $this->data[$i];
     }
     $this->data = substr($this->data, $i + 2);
     // 0-byte ending the string + 1 unknown byte
     // 4.3 [Encoded String]
     $temp = '';
     for ($i = 0; $this->data[$i] != chr(0); $i++) {
         if ($i % 8 == 0) {
             $mask = ord($this->data[$i]);
         } else {
             $temp .= chr(ord($this->data[$i]) - !($mask & 1 << $i % 8));
         }
     }
     $this->data = substr($this->data, $i + 1);
     // 4.4 [GameSettings]
     $this->game['speed'] = convert_speed(ord($temp[0]));
     if (ord($temp[1]) & 1) {
         $this->game['visibility'] = convert_visibility(0);
     } else {
         if (ord($temp[1]) & 2) {
             $this->game['visibility'] = convert_visibility(1);
         } else {
             if (ord($temp[1]) & 4) {
                 $this->game['visibility'] = convert_visibility(2);
             } else {
                 if (ord($temp[1]) & 8) {
                     $this->game['visibility'] = convert_visibility(3);
                 }
             }
         }
     }
     $this->game['observers'] = convert_observers(((ord($temp[1]) & 16) == true) + 2 * ((ord($temp[1]) & 32) == true));
     $this->game['teams_together'] = convert_bool(ord($temp[1]) & 64);
     $this->game['lock_teams'] = convert_bool(ord($temp[2]));
     $this->game['full_shared_unit_control'] = convert_bool(ord($temp[3]) & 1);
     $this->game['random_hero'] = convert_bool(ord($temp[3]) & 2);
     $this->game['random_races'] = convert_bool(ord($temp[3]) & 4);
     if (ord($temp[3]) & 64) {
         $this->game['observers'] = convert_observers(4);
     }
     $temp = substr($temp, 13);
     // 5 unknown bytes + checksum
     // 4.5 [Map&CreatorName]
     $temp = explode(chr(0), $temp);
     $this->game['creator'] = $temp[1];
     $this->game['map'] = $temp[0];
     // 4.6 [PlayerCount]
     $temp = unpack('Vslots', $this->data);
     $this->data = substr($this->data, 4);
     $this->game['slots'] = $temp['slots'];
     // 4.7 [GameType]
     $this->game['type'] = convert_game_type(ord($this->data[0]));
     $this->game['private'] = convert_bool(ord($this->data[1]));
     $this->data = substr($this->data, 8);
     // 2 bytes are unknown and 4.8 [LanguageID] is useless
     // 4.9 [PlayerList]
     while (ord($this->data[0]) == 0x16) {
         $this->loadplayer();
         $this->data = substr($this->data, 4);
     }
     // 4.10 [GameStartRecord]
     $temp = unpack('Crecord_id/vrecord_length/Cslot_records', $this->data);
     $this->data = substr($this->data, 4);
     $this->game = array_merge($this->game, $temp);
     $slot_records = $temp['slot_records'];
     // 4.11 [SlotRecord]
     for ($i = 0; $i < $slot_records; $i++) {
         if ($this->header['major_v'] >= 7) {
             $temp = unpack('Cplayer_id/x1/Cslot_status/Ccomputer/Cteam/Ccolor/Crace/Cai_strength/Chandicap', $this->data);
             $this->data = substr($this->data, 9);
         } elseif ($this->header['major_v'] >= 3) {
             $temp = unpack('Cplayer_id/x1/Cslot_status/Ccomputer/Cteam/Ccolor/Crace/Cai_strength', $this->data);
             $this->data = substr($this->data, 8);
         } else {
             $temp = unpack('Cplayer_id/x1/Cslot_status/Ccomputer/Cteam/Ccolor/Crace', $this->data);
             $this->data = substr($this->data, 7);
         }
         $temp['color'] = convert_color($temp['color']);
         $temp['race'] = convert_race($temp['race']);
         $temp['ai_strength'] = convert_ai($temp['ai_strength']);
         if ($temp['slot_status'] == 2) {
             // do not add empty slots
             $this->players[$temp['player_id']] = array_merge($this->players[$temp['player_id']], $temp);
             // Tome of Retraining
             $this->players[$temp['player_id']]['retraining_time'] = 0;
         }
     }
     // 4.12 [RandomSeed]
     $temp = unpack('Vrandom_seed/Cselect_mode/Cstart_spots', $this->data);
     $this->data = substr($this->data, 6);
     $this->game['random_seed'] = $temp['random_seed'];
     $this->game['select_mode'] = convert_select_mode($temp['select_mode']);
     if ($temp['start_spots'] != 0xcc) {
         // tournament replays from battle.net website don't have this info
         $this->game['start_spots'] = $temp['start_spots'];
     }
 }
Example #2
0
 function parseactions($actionblock, $data_length)
 {
     $block_length = 0;
     $action = 0;
     while ($data_length) {
         if ($block_length) {
             $actionblock = substr($actionblock, $block_length);
         }
         $temp = unpack('Cplayer_id/vlength', $actionblock);
         $player_id = $temp['player_id'];
         $block_length = $temp['length'] + 3;
         $data_length -= $block_length;
         $was_deselect = false;
         $was_subupdate = false;
         $was_subgroup = false;
         $n = 3;
         while ($n < $block_length) {
             $prev = $action;
             $action = ord($actionblock[$n]);
             switch ($action) {
                 // Unit/building ability (no additional parameters)
                 // here we detect the races, heroes, units, items, buildings,
                 // upgrades
                 case 0x10:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 13) {
                         $n++;
                         // ability flag is one byte longer
                     }
                     $itemid = strrev(substr($actionblock, $n + 2, 4));
                     $value = convert_itemid($itemid);
                     if (!$value) {
                         $this->players[$player_id]['actions_details'][convert_action('ability')]++;
                         // handling Destroyers
                         if (ord($actionblock[$n + 2]) == 0x33 && ord($actionblock[$n + 3]) == 0x2) {
                             $name = substr(convert_itemid('ubsp'), 2);
                             $this->players[$player_id]['units']['order'][$this->time] = $this->players[$player_id]['units_multiplier'] . ' ' . $name;
                             $this->players[$player_id]['units'][$name]++;
                             $name = substr(convert_itemid('uobs'), 2);
                             $this->players[$player_id]['units'][$name]--;
                         }
                     } else {
                         $this->players[$player_id]['actions_details'][convert_action('buildtrain')]++;
                         if (!$this->players[$player_id]['race_detected']) {
                             if ($race_detected = convert_race($itemid)) {
                                 $this->players[$player_id]['race_detected'] = $race_detected;
                             }
                         }
                         $name = substr($value, 2);
                         switch ($value[0]) {
                             case 'u':
                                 // preventing duplicated units
                                 if ($this->time - $this->players[$player_id]['units_time'] > ACTION_DELAY || $itemid != $this->players[$player_id]['last_itemid'] || ($itemid == 'hpea' || $itemid == 'ewsp' || $itemid == 'opeo' || $itemid == 'uaco') && $this->time - $this->players[$player_id]['units_time'] > 0) {
                                     $this->players[$player_id]['units_time'] = $this->time;
                                     $this->players[$player_id]['units']['order'][$this->time] = $this->players[$player_id]['units_multiplier'] . ' ' . $name;
                                     $this->players[$player_id]['units'][$name] += $this->players[$player_id]['units_multiplier'];
                                 }
                                 break;
                             case 'b':
                                 $this->players[$player_id]['buildings']['order'][$this->time] = $name;
                                 $this->players[$player_id]['buildings'][$name]++;
                                 break;
                             case 'h':
                                 $this->players[$player_id]['heroes']['order'][$this->time] = $name;
                                 $this->players[$player_id]['heroes'][$name]['revivals']++;
                                 break;
                             case 'a':
                                 list($hero, $ability) = explode(':', $name);
                                 $retraining_time = $this->players[$player_id]['retraining_time'];
                                 if (!$this->players[$player_id]['heroes'][$hero]['retraining_time']) {
                                     $this->players[$player_id]['heroes'][$hero]['retraining_time'] = 0;
                                 }
                                 // preventing too high levels (avoiding duplicated actions)
                                 // the second condition is mainly for games with random heroes
                                 // the third is for handling Tome of Retraining usage
                                 if (($this->time - $this->players[$player_id]['heroes'][$hero]['ability_time'] > ACTION_DELAY || !$this->players[$player_id]['heroes'][$hero]['ability_time'] || $this->time - $retraining_time < RETRAINING_TIME) && $this->players[$player_id]['heroes'][$hero]['abilities'][$retraining_time][$ability] < 3) {
                                     if ($this->time - $retraining_time > RETRAINING_TIME) {
                                         $this->players[$player_id]['heroes'][$hero]['ability_time'] = $this->time;
                                         $this->players[$player_id]['heroes'][$hero]['level']++;
                                         $this->players[$player_id]['heroes'][$hero]['abilities'][$this->players[$player_id]['heroes'][$hero]['retraining_time']][$ability]++;
                                     } else {
                                         $this->players[$player_id]['heroes'][$hero]['retraining_time'] = $retraining_time;
                                         $this->players[$player_id]['heroes'][$hero]['abilities']['order'][$retraining_time] = 'Retraining';
                                         $this->players[$player_id]['heroes'][$hero]['abilities'][$retraining_time][$ability]++;
                                     }
                                     $this->players[$player_id]['heroes'][$hero]['abilities']['order'][$this->time] = $ability;
                                 }
                                 break;
                             case 'i':
                                 $this->players[$player_id]['items']['order'][$this->time] = $name;
                                 $this->players[$player_id]['items'][$name]++;
                                 if ($itemid == 'tret') {
                                     $this->players[$player_id]['retraining_time'] = $this->time;
                                 }
                                 break;
                             case 'p':
                                 // preventing duplicated upgrades
                                 if ($this->time - $this->players[$player_id]['upgrades_time'] > ACTION_DELAY || $itemid != $this->players[$player_id]['last_itemid']) {
                                     $this->players[$player_id]['upgrades_time'] = $this->time;
                                     $this->players[$player_id]['upgrades']['order'][$this->time] = $name;
                                     $this->players[$player_id]['upgrades'][$name]++;
                                 }
                                 break;
                             default:
                                 $this->errors[$this->time] = 'Unknown ItemID at ' . convert_time($this->time) . ': ' . $value;
                                 break;
                         }
                         $this->players[$player_id]['last_itemid'] = $itemid;
                     }
                     if ($this->header['major_v'] >= 7) {
                         $n += 14;
                     } else {
                         $n += 6;
                     }
                     break;
                     // Unit/building ability (with target position)
                 // Unit/building ability (with target position)
                 case 0x11:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 13) {
                         $n++;
                         // ability flag
                     }
                     if (ord($actionblock[$n + 2]) <= 0x19 && ord($actionblock[$n + 3]) == 0x0) {
                         // basic commands
                         $this->players[$player_id]['actions_details'][convert_action('basic')]++;
                     } else {
                         $this->players[$player_id]['actions_details'][convert_action('ability')]++;
                     }
                     $value = strrev(substr($actionblock, $n + 2, 4));
                     if ($value = convert_buildingid($value)) {
                         $this->players[$player_id]['buildings']['order'][$this->time] = $value;
                         $this->players[$player_id]['buildings'][$value]++;
                     }
                     if ($this->header['major_v'] >= 7) {
                         $n += 22;
                     } else {
                         $n += 14;
                     }
                     break;
                     // Unit/building ability (with target position and target object ID)
                 // Unit/building ability (with target position and target object ID)
                 case 0x12:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 13) {
                         $n++;
                         // ability flag
                     }
                     if (ord($actionblock[$n + 2]) == 0x3 && ord($actionblock[$n + 3]) == 0x0) {
                         // rightclick
                         $this->players[$player_id]['actions_details'][convert_action('rightclick')]++;
                     } elseif (ord($actionblock[$n + 2]) <= 0x19 && ord($actionblock[$n + 3]) == 0x0) {
                         // basic commands
                         $this->players[$player_id]['actions_details'][convert_action('basic')]++;
                     } else {
                         $this->players[$player_id]['actions_details'][convert_action('ability')]++;
                     }
                     if ($this->header['major_v'] >= 7) {
                         $n += 30;
                     } else {
                         $n += 22;
                     }
                     break;
                     // Give item to Unit / Drop item on ground
                 // Give item to Unit / Drop item on ground
                 case 0x13:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 13) {
                         $n++;
                         // ability flag
                     }
                     $this->players[$player_id]['actions_details'][convert_action('item')]++;
                     if ($this->header['major_v'] >= 7) {
                         $n += 38;
                     } else {
                         $n += 30;
                     }
                     break;
                     // Unit/building ability (with two target positions and two item IDs)
                 // Unit/building ability (with two target positions and two item IDs)
                 case 0x14:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 13) {
                         $n++;
                         // ability flag
                     }
                     if (ord($actionblock[$n + 2]) == 0x3 && ord($actionblock[$n + 3]) == 0x0) {
                         // rightclick
                         $this->players[$player_id]['actions_details'][convert_action('rightclick')]++;
                     } elseif (ord($actionblock[$n + 2]) <= 0x19 && ord($actionblock[$n + 3]) == 0x0) {
                         // basic commands
                         $this->players[$player_id]['actions_details'][convert_action('basic')]++;
                     } else {
                         $this->players[$player_id]['actions_details'][convert_action('ability')]++;
                     }
                     if ($this->header['major_v'] >= 7) {
                         $n += 43;
                     } else {
                         $n += 35;
                     }
                     break;
                     // Change Selection (Unit, Building, Area)
                 // Change Selection (Unit, Building, Area)
                 case 0x16:
                     $temp = unpack('Cmode/vnum', substr($actionblock, $n + 1, 3));
                     if ($temp['mode'] == 0x2 || !$was_deselect) {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $this->players[$player_id]['actions_details'][convert_action('select')]++;
                     }
                     $was_deselect = $temp['mode'] == 0x2;
                     $this->players[$player_id]['units_multiplier'] = $temp['num'];
                     $n += 4 + $temp['num'] * 8;
                     break;
                     // Assign Group Hotkey
                 // Assign Group Hotkey
                 case 0x17:
                     $this->players[$player_id]['actions'][] = $this->time;
                     $this->players[$player_id]['actions_details'][convert_action('assignhotkey')]++;
                     $temp = unpack('Cgroup/vnum', substr($actionblock, $n + 1, 3));
                     $this->players[$player_id]['hotkeys'][$temp['group']]['assigned']++;
                     $this->players[$player_id]['hotkeys'][$temp['group']]['last_totalitems'] = $temp['num'];
                     $n += 4 + $temp['num'] * 8;
                     break;
                     // Select Group Hotkey
                 // Select Group Hotkey
                 case 0x18:
                     $this->players[$player_id]['actions'][] = $this->time;
                     // Won't show up in APM stats, if we comment this line
                     $this->players[$player_id]['actions_details'][convert_action('selecthotkey')]++;
                     $this->players[$player_id]['hotkeys'][ord($actionblock[$n + 1])]['used']++;
                     $this->players[$player_id]['units_multiplier'] = $this->players[$player_id]['hotkeys'][ord($actionblock[$n + 1])]['last_totalitems'];
                     $n += 3;
                     break;
                     // Select Subgroup
                 // Select Subgroup
                 case 0x19:
                     // OR is for torunament reps which don't have build_v
                     if ($this->header['build_v'] >= 6040 || $this->header['major_v'] > 14) {
                         if ($was_subgroup) {
                             // can't think of anything better (check action 0x1A)
                             $this->players[$player_id]['actions'][] = $this->time;
                             $this->players[$player_id]['actions_details'][convert_action('subgroup')]++;
                             // I don't have any better idea what to do when somebody binds buildings
                             // of more than one type to a single key and uses them to train units
                             // TODO: this is rarely executed, maybe it should go after if ($was_subgroup) {}?
                             $this->players[$player_id]['units_multiplier'] = 1;
                         }
                         $n += 13;
                     } else {
                         if (ord($actionblock[$n + 1]) != 0 && ord($actionblock[$n + 1]) != 0xff && !$was_subupdate) {
                             $this->players[$player_id]['actions'][] = $this->time;
                             $this->players[$player_id]['actions_details'][convert_action('subgroup')]++;
                         }
                         $was_subupdate = ord($actionblock[$n + 1]) == 0xff;
                         $n += 2;
                     }
                     break;
                     // some subaction holder?
                     // version < 14b: Only in scenarios, maybe a trigger-related command
                 // some subaction holder?
                 // version < 14b: Only in scenarios, maybe a trigger-related command
                 case 0x1a:
                     // OR is for torunament reps which don't have build_v
                     if ($this->header['build_v'] >= 6040 || $this->header['major_v'] > 14) {
                         $n += 1;
                         $was_subgroup = $prev == 0x19 || $prev == 0;
                         //0 is for new blocks which start with 0x19
                     } else {
                         $n += 10;
                     }
                     break;
                     // Only in scenarios, maybe a trigger-related command
                     // version < 14b: Select Ground Item
                 // Only in scenarios, maybe a trigger-related command
                 // version < 14b: Select Ground Item
                 case 0x1b:
                     // OR is for torunament reps which don't have build_v
                     if ($this->header['build_v'] >= 6040 || $this->header['major_v'] > 14) {
                         $n += 10;
                     } else {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $n += 10;
                     }
                     break;
                     // Select Ground Item
                     // version < 14b: Cancel hero revival (new in 1.13)
                 // Select Ground Item
                 // version < 14b: Cancel hero revival (new in 1.13)
                 case 0x1c:
                     // OR is for torunament reps which don't have build_v
                     if ($this->header['build_v'] >= 6040 || $this->header['major_v'] > 14) {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $n += 10;
                     } else {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $n += 9;
                     }
                     break;
                     // Cancel hero revival
                     // Remove unit from building queue
                 // Cancel hero revival
                 // Remove unit from building queue
                 case 0x1d:
                 case 0x1e:
                     // OR is for torunament reps which don't have build_v
                     if (($this->header['build_v'] >= 6040 || $this->header['major_v'] > 14) && $action != 0x1e) {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $n += 9;
                     } else {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $this->players[$player_id]['actions_details'][convert_action('removeunit')]++;
                         $value = convert_itemid(strrev(substr($actionblock, $n + 2, 4)));
                         $name = substr($value, 2);
                         switch ($value[0]) {
                             case 'u':
                                 // preventing duplicated units cancellations
                                 if ($this->time - $this->players[$player_id]['runits_time'] > ACTION_DELAY || $value != $this->players[$player_id]['runits_value']) {
                                     $this->players[$player_id]['runits_time'] = $this->time;
                                     $this->players[$player_id]['runits_value'] = $value;
                                     $this->players[$player_id]['units']['order'][$this->time] = '-1 ' . $name;
                                     $this->players[$player_id]['units'][$name]--;
                                 }
                                 break;
                             case 'b':
                                 $this->players[$player_id]['buildings'][$name]--;
                                 break;
                             case 'h':
                                 $this->players[$player_id]['heroes'][$name]['revivals']--;
                                 break;
                             case 'p':
                                 // preventing duplicated upgrades cancellations
                                 if ($this->time - $this->players[$player_id]['rupgrades_time'] > ACTION_DELAY || $value != $this->players[$player_id]['rupgrades_value']) {
                                     $this->players[$player_id]['rupgrades_time'] = $this->time;
                                     $this->players[$player_id]['rupgrades_value'] = $value;
                                     $this->players[$player_id]['upgrades'][$name]--;
                                 }
                                 break;
                         }
                         $n += 6;
                     }
                     break;
                     // Found in replays with patch version 1.04 and 1.05.
                 // Found in replays with patch version 1.04 and 1.05.
                 case 0x21:
                     $n += 9;
                     break;
                     // Change ally options
                 // Change ally options
                 case 0x50:
                     $n += 6;
                     break;
                     // Transfer resources
                 // Transfer resources
                 case 0x51:
                     $n += 10;
                     break;
                     // Map trigger chat command (?)
                 // Map trigger chat command (?)
                 case 0x60:
                     $n += 9;
                     while ($actionblock[$n] != "") {
                         $n++;
                     }
                     ++$n;
                     break;
                     // ESC pressed
                 // ESC pressed
                 case 0x61:
                     $this->players[$player_id]['actions'][] = $this->time;
                     $this->players[$player_id]['actions_details'][convert_action('esc')]++;
                     ++$n;
                     break;
                     // Scenario Trigger
                 // Scenario Trigger
                 case 0x62:
                     if ($this->header['major_v'] >= 7) {
                         $n += 13;
                     } else {
                         $n += 9;
                     }
                     break;
                     // Enter select hero skill submenu for WarCraft III patch version <= 1.06
                 // Enter select hero skill submenu for WarCraft III patch version <= 1.06
                 case 0x65:
                     $this->players[$player_id]['actions'][] = $this->time;
                     $this->players[$player_id]['actions_details'][convert_action('heromenu')]++;
                     ++$n;
                     break;
                     // Enter select hero skill submenu
                     // Enter select building submenu for WarCraft III patch version <= 1.06
                 // Enter select hero skill submenu
                 // Enter select building submenu for WarCraft III patch version <= 1.06
                 case 0x66:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 7) {
                         $this->players[$player_id]['actions_details'][convert_action('heromenu')]++;
                     } else {
                         $this->players[$player_id]['actions_details'][convert_action('buildmenu')]++;
                     }
                     $n += 1;
                     break;
                     // Enter select building submenu
                     // Minimap signal (ping) for WarCraft III patch version <= 1.06
                 // Enter select building submenu
                 // Minimap signal (ping) for WarCraft III patch version <= 1.06
                 case 0x67:
                     if ($this->header['major_v'] >= 7) {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $this->players[$player_id]['actions_details'][convert_action('buildmenu')]++;
                         $n += 1;
                     } else {
                         $n += 13;
                     }
                     break;
                     // Minimap signal (ping)
                     // Continue Game (BlockB) for WarCraft III patch version <= 1.06
                 // Minimap signal (ping)
                 // Continue Game (BlockB) for WarCraft III patch version <= 1.06
                 case 0x68:
                     if ($this->header['major_v'] >= 7) {
                         $n += 13;
                     } else {
                         $n += 17;
                     }
                     break;
                     // Continue Game (BlockB)
                     // Continue Game (BlockA) for WarCraft III patch version <= 1.06
                 // Continue Game (BlockB)
                 // Continue Game (BlockA) for WarCraft III patch version <= 1.06
                 case 0x69:
                     // Continue Game (BlockA)
                 // Continue Game (BlockA)
                 case 0x6a:
                     $this->continue_game = 1;
                     $n += 17;
                     break;
                     // Pause game
                 // Pause game
                 case 0x1:
                     $this->pause = true;
                     $temp = '';
                     $temp['time'] = $this->time;
                     $temp['text'] = convert_chat_mode(0xfe, $this->players[$player_id]['name']);
                     $this->chat[] = $temp;
                     $n += 1;
                     break;
                     // Resume game
                 // Resume game
                 case 0x2:
                     $temp = '';
                     $this->pause = false;
                     $temp['time'] = $this->time;
                     $temp['text'] = convert_chat_mode(0xff, $this->players[$player_id]['name']);
                     $this->chat[] = $temp;
                     $n += 1;
                     break;
                     // Increase game speed in single player game (Num+)
                 // Increase game speed in single player game (Num+)
                 case 0x4:
                     // Decrease game speed in single player game (Num-)
                 // Decrease game speed in single player game (Num-)
                 case 0x5:
                     $n += 1;
                     break;
                     // Set game speed in single player game (options menu)
                 // Set game speed in single player game (options menu)
                 case 0x3:
                     $n += 2;
                     break;
                     // Save game
                 // Save game
                 case 0x6:
                     $i = 1;
                     while ($actionblock[$n] != "") {
                         $n++;
                     }
                     $n += 1;
                     break;
                     // Save game finished
                 // Save game finished
                 case 0x7:
                     $n += 5;
                     break;
                     // Only in scenarios, maybe a trigger-related command
                 // Only in scenarios, maybe a trigger-related command
                 case 0x75:
                     $n += 2;
                     break;
                 default:
                     $temp = '';
                     for ($i = 3; $i < $n; $i++) {
                         // first 3 bytes are player ID and length
                         $temp .= sprintf('%02X', ord($actionblock[$i])) . ' ';
                     }
                     $temp .= '[' . sprintf('%02X', ord($actionblock[$n])) . '] ';
                     for ($i = 1; $n + $i < strlen($actionblock); $i++) {
                         $temp .= sprintf('%02X', ord($actionblock[$n + $i])) . ' ';
                     }
                     $this->errors[] = 'Unknown action at ' . convert_time($this->time) . ': 0x' . sprintf('%02X', $action) . ', prev: 0x' . sprintf('%02X', $prev) . ', dump: ' . $temp;
                     // skip to the next CommandBlock
                     // continue 3, not 2 because of http://php.net/manual/en/control-structures.continue.php#68193
                     // ('Current functionality treats switch structures as looping in regards to continue.')
                     continue 3;
             }
         }
         $was_deselect = $action == 0x16;
         $was_subupdate = $action == 0x19;
     }
 }