World of Goo – hidden profile file and parsing data
I thought I’d have lost my up-to-date World of Goo profile and thus savegame.
In the end, it was located in Local Settings\Application Data\2DBoy\WorldOfGoo instead of _%APPDATA%_2DBoy\WorldOfGoo.
I already started on a php script to parse, later edit and write the profile file again, after decrypting and afterwards encrypting the pers2.dat file with GooTool.
Anyway, I gladly don’t have to finish it.
Here it is…:
<?php
class WorldOfGooPers2Dat
{
public $countrycode; // 11,countrycode2,DE
public $fullscreen; // 10,fullscreen4,true | 10,fullscreen4,false
public $mostRecentProfile; // 4,mrpp1,0 | 0 = 0..2
public $profiles;
public function __construct($countrycode, $fullscreen, $mostRecentProfile, $profiles)
{
$this->countrycode = $countrycode;
$this->fullscreen = $fullscreen;
$this->mostRecentProfile = $mostRecentProfile;
$this->profiles = $profiles;
}
public static function createFromString($str)
{
// drop "11,countrycode2,", read lang-code (eg EN, DE, etc)
$countrycode = substr($str, 16, 2);
$str = substr($str, 18);
// drop "10,fullscreen4,", read fullscreen boolean
$fullscreen = (boolean)substr($str, 15, 4);
$str = substr($str, 19);
// drop "10,fullscreen4,", read profile id 0..2
$mostRecentProfile = intval(substr($str, 8, 1));
$str = substr($str, 9);
$profiles = array();
while(substr($str, 0, 3) != ',0.')
{
$id = intval(substr($str, 10, 1));
$str = substr($str, 11);
$commapos = strpos($str, ',');
$len = intval(substr($str, 0, $commapos));
$profiledata = substr($str, $commapos+1, $len);
$str = substr($str, $commapos+1+$len);
$profiles[] = Profile::createFromString($id, $profiledata);
}
return new self($countrycode, $fullscreen, $mostRecentProfile, $profiles);
}
public function toString()
{
$txt = ' - countrycode: ' . $this->countrycode . '<br/>'
. ' - fullscreen: ' . $this->fullscreen . '<br/>'
. ' - mostRecentProfile: ' . $this->mostRecentProfile . '<br/>'
. ' - profiles:<br/>';
foreach($this->profiles AS $profile)
{
$txt .= $profile->toString("\t") . '<br/>';
}
return $txt;
}
}
class Profile
{
public $id; // 9,profile_0 | 0 = 0..2
public $playername; //
/**
* bit-combined integer:
* 1 online play is enabled
* 2 World of Goo Corporation has been unlocked
* 4 World of Goo Corporation has been destroyed
* 8 the whistle has been found
* 16 the Terms & Conditions have been accepted (this unlocks Deliverance)
*/
public $playerflags; //
public $playtime; // in seconds
public $numberOfLevelsPC; // number of levels played/completed
public $levelInformations; // list of level information
public $onlinePlayerKey;
public $newBalls;
function __construct($id, $playername, $playerflags, $playtime, $numberOfLevelsPC, $levelInformations, $onlinePlayerKey, $newBalls)
{
$this->id = $id;
$this->playername = $playername;
$this->playerflags = $playerflags;
$this->playtime = $playtime;
$this->numberOfLevelsPC = $numberOfLevelsPC;
$this->levelInformations = $levelInformations;
}
public static function createFromString($id, $str)
{
$data = split(',', $str);
$playername = $data[0];
$playerflags = $data[1];
$playtime = $data[2];
$numberOfLevelsPC = $data[3];
$levelInformations = array();
$i = 4;
for( ; $data[$i] != '0'; $i += 4 )
{
$levelInformations[] = new LevelInformation($data[$i], $data[$i+1], $data[$i+2], $data[$i+3]);
}
$i++;
// Goo Corp Data
$i++;
$onlinePlayerKey = $data[$i];
$i++;
$newBalls = $data[$i];
return new self($id, $playername, $playerflags, $playtime, $numberOfLevelsPC, $levelInformations, $onlinePlayerKey, $newBalls);
}
public function toString($prefix='')
{
$txt = $prefix . ' - id: ' . $this->id . '<br/>'
. $prefix . ' - playername: ' . $this->playername . '<br/>'
. $prefix . ' - player flags: ' . $this->playerflags . '<br/>'
. $prefix . ' - playtime: ' . $this->playtime . '<br/>'
. $prefix . ' - # levels completed: ' . $this->numberOfLevelsPC . '<br/>'
. $prefix . ' - online player key: ' . $this->onlinePlayerKey . '<br/>'
. $prefix . ' - new balls: ' . $this->newBalls . '<br/>'
. $prefix . ' - level information:<br/>';
foreach($this->levelInformations AS $info)
{
$txt .= $info->toString("\t\t") . '<br/>';
}
return $txt;
}
}
class LevelInformation
{
public $name;
public $maxballscollected;
public $leastmoves;
public $leasttime;
function __construct($name, $maxballscollected, $leastmoves, $leasttime)
{
$this->name = $name;
$this->maxballscollected = $maxballscollected;
$this->leastmoves = $leastmoves;
$this->leasttime = $leasttime;
}
public function toString($prefix='')
{
return $prefix . $this->name . ' ...';
}
}
class WorldOfGooCorporation
{
public $balls;
public $strands;
}
class Ball
{
public $type;
public $pos_x;
public $pos_y;
public $vel_x;
public $vel_y;
}
class Strand
{
public $type;
public $startball;
public $endball;
public $strength; // unknown
public $length; // unknown
public $hasAbsorbedBall;
function __construct($type, $startball, $endball, $strength, $length, $hasAbsorbedBall)
{
$this->type = $type;
$this->startball = $startball;
$this->endball = $endball;
$this->strength = $strength;
$this->length = $length;
$this->hasAbsorbedBall = $hasAbsorbedBall;
}
public static function createFromString($string)
{
$values = split(':', $string);
$type = $values[0];
$startball = $values[1];
$endball = $values[2];
$strength = $values[3];
$length = $values[4];
$hasAbsorbedBall = $values[5];
return Strand($type, $startball, $endball, $strength, $length, $hasAbsorbedBall);
}
}
$fdata = file_get_contents('pers2.dat.xml');
$dat = WorldOfGooPers2Dat::createFromString($fdata);
echo '<pre>' . $dat->toString() . '</pre>';
?>