WriteToFile($fp);
* fclose($fp);
*
* // This is how to create a "Pick One" style database
* // that does NOT contain chance information
* $R = new DndHelper_Random();
* $R->AddEntry("This is my first item");
* $R->AddEntry("This is my second item");
* $R->AddEntry("All entries will have an equal chance of being displayed");
* // Add to the $pdb
* $pdb->AddSection($R->Freeze());
*
* // This is how to create a "Pick One" style database
* // that does contain chance information
* $R = new DndHelper_Random();
* $R->AddEntry("This is my first item. It will appear rarely.", 0x0100);
* $R->AddEntry("This is my second item. This will appear equally " .
* "as rare as the first.", 0x0200);
* $R->AddEntry("The \"chance\" attribute should always get bigger " .
* "with each entry you add." 0x0300);
* $R->AddEntry("This one will appear the most. Since this is the " .
* "last item, it should also have 0xFFFF as the " .
* "chance.", 0xFFFF);
* // Add to the $pdb
* $pdb->AddSection($R->Freeze());
*
* // This is how to generate a letter pair section for builing
* // words, names, and other things.
* $R = new DndHelper_LetterPairs();
* foreach ($wordlist as $word) {
* $R->AddEntry($word);
* }
* // If you do not want chance data, uncomment this line
* // $R->UseChances = false;
* // Add to the $pdb
* $pdb->AddSection($R->Freeze());
*
* // If you want to generate Phrase Structure Rules, you would first
* // need the rules. This is a very simple example.
* $P = new DndHelper_PSR();
* // Add the data (This assumes no chance data)
* $P->AddEntry('S', 'The [N] [V].'); // The first one is the main entry
* $P->AddEntry('N', 'boy'); // Add all of the N possibilities
* $P->AddEntry('N', 'cat');
* $P->AddEntry('N', 'girl');
* $P->AddEntry('N', 'dog');
* $P->AddEntry('V', 'walks', 2); // Add all of the V possibilities
* $P->AddEntry('V', 'sits', 1); // These have chances associated
* $P->AddEntry('V', 'eats', 1); // "walks" is twice as likely to show up
* $P->AddEntry('V', 'drinks', 4); // "drinks" should appear 1/2 the time
* // Add to the PDB
* $pdb->AddSection($R->Freeze());
* // Rule names are CASE SENSITIVE
* // "[[]" is translated to "[", and "[]]" turns into "]"
* // "[#]" is translated into "#", so you can have a # at the beginning
* // of a line.
* // "[*]" is likewise changed into "*".
* // "\n" (two characters) translates into a newline.
* // There is no verification that the rules properly expand to anything,
* // so be careful with spelling and test it out thoroughly.
*/
class PalmDndHelper extends PalmDB {
public function PalmDndHelper($Title = '', $Multi = false) {
PalmDB::PalmDB('DATA', 'Fid9', $Title);
$oldrec = $this->GoToRecord(0);
$this->DeleteRecord();
$this->AppendInt16(1); // Version of database structure
$flags = 0;
if ($Multi)$flags |= 0x0001; // Generate multiple entries, not just one
$this->AppendInt16($flags); // Flags
$this->GoToRecord($oldrec);
}
/* Adds a section to the database.
* $pdb->AddSection($section->Freeze());
* Data is passed in as an array of records. */
function AddSection($data) {
if (! is_array($data)) {
return;
}
$this->GoToRecord(0);
$this->AppendInt16(count($data));
while (count($data)) {
$this->GoToRecord($this->GetRecordCount());
$this->AppendCurrent(array_shift($data));
}
}
// File reading not supported
}
class DndHelper_Random {
public
public $Data = array(); // Array of different things to pick from
public
public $Chances = array();
/* Chance data (if any)
* Adds a random entry
* Make sure the last one's chance (if you are using them) is 0xFFFF */
public function AddEntry($Str, $chance = false) {
$this->Data[] = $Str;
if ($chance !== false) {
$this->Chances[] = $chance;
}
}
// Creates the encoded data
public function Freeze() {
$data = array();
$Rec = PalmDB::Int16(0); // Type 0 = pick one random
$Flags = 0;
$ChanceOffsetSize = 2;
if (count($this->Chances) == count($this->Data)) {
$Flags |= 0x0001; // use chances
$ChanceOffsetSize = 4;
}
$Rec .= PalmDB::Int16($Flags); // Flags
$Rec .= PalmDB::Int16(count($this->Data)); // Number of items
$data[] = $Rec;
$EntriesProcessed = 0;
// Now build the rest of the records for the database.
while (count($this->Data)) {
$a = array();
$len = 0;
while (count($this->Data) && ($len + strlen($this->Data[0]) + 2 + count($a) * $ChanceOffsetSize < 4096 || $len == 0)) {
$d = array_shift($this->Data);
$len += strlen($d) + 1;
$a[] = $d;
}
$EntriesProcessed += count($a);
$Rec = PalmDB::Int16($EntriesProcessed);
$Offset = 2 + count($a) * 2;
if ($Flags & 0x0001)$Offset = 2 + count($a) * 4;
for ($i = 0; $i < count($a); $i ++) {
if ($Flags & 0x0001)$Rec .= PalmDB::Int16(array_shift($this->Chances));
$Rec .= PalmDB::Int16($Offset);
$Offset += strlen($a[$i]) + 1;
}
while (count($a)) {
$Rec .= PalmDB::String(array_shift($a));
$Rec .= PalmDB::Int8(0);
}
$data[] = $Rec;
}
return $data;
}
}
class DndHelper_LetterPairs {
public
public $Data = array();
/* Array of different letter pairs, key = first letter
* ... array of next letter, key = second letter
* ... chances, key = third letter
* $Data['q']['u']['i'] = 31; // or something similar. */
public
public $StartPairs = array(); // special pairs stuck in the header record
public
public $MaxLen = 12; // Maximum length of the word
public
public $UseChances = true;
/* Use chance data (more accurate, larger db)
* Adds a random entry, dissecting it into letter pairs and incrementing
* the appropriate counters. */
public function AddEntry($Word) {
// Must be at least 2 characters long
if (strlen($Word) < 2)return;
$chunk = substr($Word, 0, 2);
if (! isset($this->StartPairs[$chunk]))$this->StartPairs[$chunk] = 0;
$this->StartPairs[$chunk]++;
$first = ord(substr($Word, 0, 1));
$second = ord(substr($Word, 0, 1));
$third = 100;
$Word = substr($Word, 2);
while ($third != 0) {
$third = substr($Word, 0, 1);
if ($third == '')$third = 0;
else $third = ord($third);
$Word = substr($Word, 1);
if (! isset($this->Data[$first]))$this->Data[$first] = array();
if (! isset($this->Data[$first][$second]))$this->Data[$first][$second] = array();
if (! isset($this->Data[$first][$second][$third]))$this->Data[$first][$second][$third] = 0;
$this->Data[$first][$second][$third]++;
$first = $second;
$second = $third;
}
}
// Returns the encoded data to be added into the PHP-PDB class
function Freeze() {
$data = array();
// Header
$Rec = PalmDB::Int16(1); // Type 1 = letter pairs
$Flags = 0;
if ($this->UseChances)$Flags |= 0x0001; // Use chances
$Rec .= PalmDB::Int16($Flags);
$Rec .= PalmDB::Int8($this->MaxLen);
/* Maximum length
* Add the starting pairs */
$pairs = array_keys($this->StartPairs);
sort($pairs);
if ($this->UseChances) {
$TotalChance = 0;
foreach ($this->StartPairs as $k => $v) {
$TotalChance += $v;
}
$lastChance = 0;
$lastPair = array_pop($pairs);
foreach ($pairs as $p) {
$chance = ($this->StartPairs[$p] / $TotalChance) * 0xFFFF;
settype($chance, 'integer');
$Rec .= PalmDB::Int16($chance + $lastChance);
$Rec .= PalmDB::String($p);
$lastChance += $chance;
}
$Rec .= PalmDB::Int16(0xFFFF) . PalmDB::String($lastPair);
} else {
foreach ($pairs as $p) {
$Rec .= PalmDB::String($p);
}
}
$data[] = $Rec;
// Now do each letter pair record
ksort($this->Data);
foreach ($this->Data as $first => $d) {
$Rec = PalmDB::Int8($first);
$Rec .= PalmDB::Int8(count($d));
ksort($d);
foreach ($d as $second => $t) {
$Rec .= PalmDB::Int8($second);
$num = count($t);
if ($this->UseChances && isset($t[0]))$num --;
$Rec .= PalmDB::Int8($num);
}
foreach ($d as $second => $t) {
if ($this->UseChances) {
$lastChance = 0;
$TotalChance = 0;
foreach ($t as $third => $count) {
$TotalChance += $count;
}
}
$AddNULL = false;
foreach ($t as $third => $count) {
if ($third != 0) {
if ($this->UseChances) {
$lastChance += $count;
$chance = $lastChance / $TotalChance;
$chance *= 0xFFFF;
settype($chance, 'integer');
$Rec .= PalmDB::Int16($chance);
}
$Rec .= PalmDB::Int8($third);
} else {
$AddNULL = true;
}
}
if ($AddNULL && ! $this->UseChances) {
$Rec .= PalmDB::Int8(0);
}
}
$data[] = $Rec;
}
// Return the array of encoded records
return $data;
}
}
class DndHelper_PSR {
public
public $Data = array(
false
); // Array of rules, first one is the header
public
public $RuleInfo = array(); // Rule names to index numbers, etc.
public
public $Chances = array();
/* Chance data (if any) (not used yet)
* Adds a random entry */
public function AddEntry($RuleIndex, $RuleData, $Chance = false) {
if (! isset($this->RuleInfo[$RuleIndex])) {
$this->RuleInfo[$RuleIndex] = array();
// The ID number in the Data array
$this->RuleInfo[$RuleIndex]['id'] = count($this->Data);
// Number of times this rule is referenced
$this->RuleInfo[$RuleIndex]['ref'] = 0;
$this->RuleInfo[$RuleIndex]['raw data'] = array();
$this->RuleInfo[$RuleIndex]['origin'] = 'Not auto-created by a rule';
$this->Data[$this->RuleInfo[$RuleIndex]['id']] = array();
$this->Chances[$this->RuleInfo[$RuleIndex]['id']] = array();
}
$this->RuleInfo[$RuleIndex]['raw data'][] = $RuleData;
$this->Data[$this->RuleInfo[$RuleIndex]['id']][] = $this->ProcessRuleText($RuleData);
if ($Chance !== false) {
$this->Chances[$this->RuleInfo[$RuleIndex]['id']][] = $Chance;
}
}
public function ProcessRuleText($str) {
// Replace all instances of "[RULE]" with "\x01 FLAGS RULE_ID"
$bits = preg_split('/\\[([\\^]?[^\\]]*)\\]/', $str, - 1, PREG_SPLIT_DELIM_CAPTURE);
$out = array_shift($bits);
while (count($bits)) {
$RuleName = array_shift($bits);
$Flags = 0x01;
if (substr($RuleName, 0, 1) == '^') {
$Flags += 0x02;
$RuleName = substr($RuleName, 1);
}
if ($RuleName == '[' || $RuleName == ']' || $RuleName == '*' || $RuleName == '#') {
$out .= $RuleName;
} else {
if (! isset($this->RuleInfo[$RuleName])) {
$this->RuleInfo[$RuleName] = array();
// The ID number in the Data array
$this->RuleInfo[$RuleName]['id'] = count($this->Data);
// Number of times this rule is referenced
$this->RuleInfo[$RuleName]['ref'] = 0;
$this->RuleInfo[$RuleName]['data'] = array();
$this->RuleInfo[$RuleName]['origin'] = $str;
$this->Data[] = array();
}
$this->RuleInfo[$hits[1]]['ref']++;
$out .= "\x01" . chr($Flags) . chr($this->RuleInfo[$RuleName]['id']);
}
$out .= array_shift($bits);
}
return $out;
}
// Returns an array of encoded data for PHP-PDB
public function Freeze() {
$data = array();
$Flags = 0;
$Rec = PalmDB::Int16(2);
$Rec .= PalmDB::Int16($Flags);
$data[] = $Rec;
$RuleNameLookup = array();
foreach ($this->RuleInfo as $k => $v) {
$RuleNameLookup[$v['id']] = $k;
}
if (isset($RuleNameLookup[256])) {
echo "PROBLEM: Rule file has too many rules.
\n";
}
foreach (array_keys($this->Data) as $i) {
$UseChances = 0;
$Flags = 0;
if ($this->Data[$i] !== false) {
$ChancesAllOnes = true;
if (is_array($this->Chances[$i])) {
foreach ($this->Chances[$i] as $v) {
if ($v > 1)$ChancesAllOnes = false;
}
}
if (count($this->Chances[$i]) == count($this->Data[$i]) && count($this->Data[$i]) > 0 && $ChancesAllOnes == false) {
$Flags += 1; // Use chance data
$UseChances = 1; // Simple flag to check when writing data
}
$Rec = PalmDB::Int16($Flags);
$Rec .= PalmDB::Int16(count($this->Data[$i])); // # of items
if (count($this->Data[$i]) == 0) {
echo 'Error - record ' . $RuleNameLookup[$i] . " has 0 expansion possibilities.
\n";
echo nl2br(htmlspecialchars(print_r($this->RuleInfo[$RuleNameLookup[$i]], true))) . "
\n";
return array();
} else {
if ($UseChances) {
$ChanceTotal = 0;
foreach ($this->Chances[$i] as $v) {
$ChanceTotal += $v;
}
$Accumulator = 0;
}
foreach (array_keys($this->Data[$i]) as $v) {
if ($UseChances) {
$Accumulator += $this->Chances[$i][$v];
$chance = $Accumulator / $ChanceTotal;
$chance *= 0xFFFF;
settype($chance, 'integer');
$Rec .= PalmDB::Int16($chance);
}
$Rec .= PalmDB::String($this->Data[$i][$v]);
$Rec .= PalmDB::Int8(0);
}
}
$data[] = $Rec;
}
}
return $data;
}
}