Type) private $EndOffset; // Calculated RecordSize according columns type private $RecordSize; private $RecordCount; private $StringBlockSize; private $FieldCount; public function OpenFile($FileName, $ColumnFormat = array()) { parent::OpenFile($FileName); $this->ColumnFormat = $ColumnFormat; if ($this->ReadUint() != DBC_SIGNATURE) die(NOT_DBC_FILE); $this->RecordCount = $this->ReadUint(); $this->FieldCount = $this->ReadUint(); $this->RecordSize = $this->ReadUint(); $this->StringBlockSize = $this->ReadUint(); $this->GenerateOffsetTable(); if ($this->EndOffset != $this->RecordSize) die(RECORD_SIZE_NOT_MATCH.$this->EndOffset.' <> '.$this->RecordSize); } public function CreateFile($FileName, $ColumnFormat = array()) { parent::CreateFile($FileName); $this->WriteUint(DBC_SIGNATURE); $this->StringList = array(); $this->StringOffset = 1; $this->ColumnFormat = $ColumnFormat; $this->FieldCount = 0; $this->GenerateOffsetTable(); $this->RecordCount = 0; $this->RecordSize = $this->EndOffset; $this->StringBlockSize = 0; $this->WriteUint($this->RecordCount); $this->WriteUint($this->FieldCount); $this->WriteUint($this->RecordSize); $this->WriteUint($this->StringBlockSize); } private function GenerateOffsetTable() { // Preallocate array if ($this->FieldCount > 0) $this->Offsets = array_fill(0, $this->FieldCount, 0); else $this->Offsets = array(); $Offset = 0; $I = 0; while ($I < $this->FieldCount) { $this->Offsets[$I] = $Offset; if (array_key_exists($I, $this->ColumnFormat)) $Format = $this->ColumnFormat[$I]; else $Format = FORMAT_UINT32; switch ($Format) { case FORMAT_BYTE: $Offset += 1; break; case FORMAT_UINT32: case FORMAT_SINT32: case FORMAT_SINGLE: case FORMAT_STRING: $Offset += 4; break; } $I++; } $this->EndOffset = $Offset; } private function CellPos($Row, $Column) { return $this->HeaderSize + $Row * $this->RecordSize + $this->Offsets[$Column]; } public function GetByte($Row, $Column) { $this->SetPosition($this->CellPos($Row, $Column)); return $this->ReadByte(); } public function GetUInt($Row, $Column) { $this->SetPosition($this->CellPos($Row, $Column)); return $this->ReadUint(); } public function GetInt($Row, $Column) { $this->SetPosition($this->CellPos($Row, $Column)); return $this->ReadInt(); } public function GetFloat($Row, $Column) { $this->SetPosition($this->CellPos($Row, $Column)); return $this->ReadFloat(); } public function SetByte($Row, $Column, $Value) { $this->SetPosition($this->CellPos($Row, $Column)); $this->WriteByte($Value); } public function SetUint($Row, $Column, $Value) { $this->SetPosition($this->CellPos($Row, $Column)); $this->WriteUint($Value); } public function SetInt($Row, $Column, $Value) { $this->SetPosition($this->CellPos($Row, $Column)); $this->WriteInt($Value); } public function SetFloat($Row, $Column, $Value) { $this->SetPosition($this->CellPos($Row, $Column)); $this->WriteFloat($Value); } public function GetString($Row, $Column) { $Offset = $this->GetUint($Row, $Column); $Position = $this->HeaderSize + $this->RecordCount * $this->RecordSize + $Offset; if ($Position >= $this->GetSize()) return ''; $this->SetPosition($Position); $String = ''; while (($Char = $this->ReadChar()) != "\0") { $String .= $Char; } return $String; } public function SetString($Row, $Column, $Value) { if (in_array($Value, $this->StringList)) { $this->SetUint($Row, $Column, $this->StringListOffset[array_search($Value, $this->StringList)]); } else { $this->SetUint($Row, $Column, $this->StringOffset); $this->StringList[] = $Value; $this->StringListOffset[] = $this->StringOffset; $this->StringOffset += strlen($Value) + 1; } } public function Commit() { $this->SetSize($this->HeaderSize + $this->RecordSize * $this->RecordCount); // Preallocate file $this->SetPosition(0); $this->WriteUint(DBC_SIGNATURE); $this->WriteUint($this->RecordCount); $this->WriteUint($this->FieldCount); $this->WriteUint($this->RecordSize); $this->WriteUint($this->StringOffset); $this->SetPosition($this->HeaderSize + $this->RecordCount * $this->RecordSize); $this->WriteByte(0); foreach ($this->StringList as $Index => $Item) { $this->WriteString($Item."\0"); } } public function GetLine($Row) { // Cache record data $Record = new MemoryStream(); $this->SetPosition($this->CellPos($Row, 0)); $Record->Data = $this->ReadBlock($this->RecordSize); // Preallocate array if ($this->FieldCount > 0) $Line = array_fill(0, $this->FieldCount, 0); else $Line = array(); for ($I = 0; $I < $this->FieldCount; $I++) { if (array_key_exists($I, $this->ColumnFormat)) $Format = $this->ColumnFormat[$I]; else $Format = FORMAT_UINT32; $Record->SetPosition($this->Offsets[$I]); switch ($Format) { case FORMAT_BYTE: $Line[$I] = $Record->ReadByte(); break; case FORMAT_UINT32: $Line[$I] = $Record->ReadUInt(); break; case FORMAT_SINT32: $Line[$I] = $Record->ReadInt(); break; case FORMAT_SINGLE: $Line[$I] = $Record->ReadFloat(); break; case FORMAT_STRING: $Offset = $Record->ReadUint(); $Position = $this->HeaderSize + $this->RecordCount * $this->RecordSize + $Offset; if ($Position >= $this->GetSize()) $String = ''; else { $this->SetPosition($Position); $String = ''; while (($Char = $this->ReadChar()) != "\0") $String .= $Char; } $Line[$I] = $String; break; default: break; } } return $Line; } public function SetLine($Row, $Line) { // Cache record data $Record = new MemoryStream(); for ($I = 0; $I < $this->FieldCount; $I++) { if (array_key_exists($I, $this->ColumnFormat)) $Format = $this->ColumnFormat[$I]; else $Format = FORMAT_UINT32; $Record->SetPosition($this->Offsets[$I]); switch ($Format) { case FORMAT_BYTE: $Record->WriteByte($Line[$I]); break; case FORMAT_UINT32: $Record->WriteUint($Line[$I]); break; case FORMAT_SINT32: $Record->WriteInt($Line[$I]); break; case FORMAT_SINGLE: $Record->WriteFloat($Line[$I]); break; case FORMAT_STRING: if (in_array($Line[$I], $this->StringList)) { $Record->WriteUint($this->StringListOffset[array_search($Line[$I], $this->StringList)]); } else { $Record->WriteUint($this->StringOffset); $this->StringList[] = $Line[$I]; $this->StringListOffset[] = $this->StringOffset; $this->StringOffset += strlen($Line[$I]) + 1; } break; default: break; } } $this->SetPosition($this->CellPos($Row, 0)); $this->WriteBlock($Record->Data, $this->RecordSize); return $Line; } public function GetRecordCount() { return $this->RecordCount; } public function SetRecordCount($Value) { $this->RecordCount = $Value; } public function GetFieldCount() { return $this->FieldCount; } public function SetFieldCount($Value) { $this->FieldCount = $Value; $this->GenerateOffsetTable(); $this->RecordSize = $this->EndOffset; } }