diff --git a/src/Exceptions/IncompatibleReaderException.php b/src/Exceptions/IncompatibleReaderException.php new file mode 100644 index 0000000..a44401c --- /dev/null +++ b/src/Exceptions/IncompatibleReaderException.php @@ -0,0 +1,10 @@ + Readers\Pairtwo6::class + ]; + + + /** + * Creates a reader for $type + * + * @param string $type + * @return ReaderInterface + */ + public static function createReader(string $type): ReaderInterface + { + if (!isset(self::$readers[$type])) { + throw new LibpairtwoException("Cannot read type $type"); + } + + // create reader class + $readerClass = self::$readers[$type]; + $reader = new $readerClass; + + return $reader; + } +} diff --git a/src/Interfaces/ReaderInterface.php b/src/Interfaces/ReaderInterface.php new file mode 100644 index 0000000..cfbd53b --- /dev/null +++ b/src/Interfaces/ReaderInterface.php @@ -0,0 +1,9 @@ + */ -class Tournament +abstract class Tournament { /** @var string */ private $Name; diff --git a/src/Models/Sws.php b/src/Readers/Models/Pairtwo6.php similarity index 94% rename from src/Models/Sws.php rename to src/Readers/Models/Pairtwo6.php index 4f3eeee..305f9e0 100644 --- a/src/Models/Sws.php +++ b/src/Readers/Models/Pairtwo6.php @@ -1,10 +1,10 @@ . + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +namespace JeroenED\Libpairtwo\Readers; + +use JeroenED\Libpairtwo\Enums\Title; +use JeroenED\Libpairtwo\Enums\Gender; +use JeroenED\Libpairtwo\Enums\Color; +use JeroenED\Libpairtwo\Enums\Result; +use JeroenED\Libpairtwo\Exceptions\IncompatibleReaderException; +use JeroenED\Libpairtwo\Tournament; +use JeroenED\Libpairtwo\Player; +use JeroenED\Libpairtwo\Round; +use JeroenED\Libpairtwo\Pairing; +use JeroenED\Libpairtwo\Interfaces\ReaderInterface; +use JeroenED\Libpairtwo\Readers\Models\Pairtwo6 as Pairtwo6Model; +use JeroenED\Libpairtwo\Enums\TournamentSystem; +use DateTime; + +/** + * This class reads a SWS file + * + * @author Jeroen De Meerleer + */ +class Pairtwo6 extends Pairtwo6Model implements ReaderInterface +{ + private const PT_DAYFACTOR = 32; + private const PT_MONTHFACTOR = 16; + private const PT_YEARFACTOR = 512; + private const PT_PASTOFFSET = 117; + + /** + * Reads out $swsfile and returns a Pairtwo6 class object + * + * @param string $filename + * @return Pairtwo6 + */ + public function read($filename) + { + $swshandle = fopen($filename, 'rb'); + $swscontents = fread($swshandle, filesize($filename)); + fclose($swshandle); + + $offset = 0; + + + $length = 4; + $this->setRelease($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + if (substr($this->getRelease(), 0, 2) !== "6.") { + throw new IncompatibleReaderException("This file was not created with Pairtwo 6 or higher"); + } + + $this->setTournament(new Tournament()); + + // UserCountry + $length = 4; + $this->setBinaryData("UserCountry", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // SavedOffset + $length = 4; + $this->setBinaryData("SavedOffset", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // NewPlayer + $length = 4; + $this->setBinaryData("NewPlayer", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // AmericanHandicap + $length = 4; + $this->setBinaryData("AmericanHandicap", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // LowOrder + $length = 4; + $this->setBinaryData("LowOrder", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // PairingMethod + $length = 4; + $this->setBinaryData("PairingMethod", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // AmericanPresence + $length = 4; + $this->setBinaryData("AmericanPresence", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // CheckSameClub + $length = 4; + $this->setBinaryData("CheckSameClub", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // NoColorCheck + $length = 4; + $this->setBinaryData("NoColorCheck", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // SeparateCategories + $length = 4; + $this->setBinaryData("SeparateCategories", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // EloUsed + $length = 4; + $this->setBinaryData("EloUsed", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // AlternateColors + $length = 4; + $this->setBinaryData("AlternateColors", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // MaxMeetings + $length = 4; + $this->setBinaryData("MaxMeetings", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // MaxDistance + $length = 4; + $this->setBinaryData("MaxDistance", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // MinimizeKeizer + $length = 4; + $this->setBinaryData("MinimizeKeizer", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // MinRoundsMeetings + $length = 4; + $this->setBinaryData("MinRoundsMeetings", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // MaxRoundsAbsent + $length = 4; + $this->setBinaryData("MaxRoundsAbsent", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // SpecialPoints + $length = 4 * 6; + $this->setBinaryData("SpecialPoints", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // NewNamePos + $length = 4; + $this->setBinaryData("NewNamePos", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // CurrentRound + $length = 4; + $this->setBinaryData("CurrentRound", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // CreatedRounds + $length = 4; + $this->setBinaryData("CreatedRounds", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // CreatedPlayers + $length = 4; + $this->setBinaryData("CreatedPlayers", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // MaxSelection + $length = 4; + $this->setBinaryData("MaxSelection", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // NumberOfRounds + $length = 4; + $this->setBinaryData("NumberOfRounds", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // NumberOfPairings + $length = 4; + $this->setBinaryData("NumberOfPairings", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // CreatedPairings + $length = 4; + $this->setBinaryData("CreatedPairings", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // PairingElems + $length = 4; + $this->setBinaryData("PairingElems", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // RandomSeed + $length = 4; + $this->setBinaryData("RandomSeed", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // TieOrder + $length = 4 * 5; + $this->setBinaryData("TieOrder", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // Categorie + $length = 4 * 10; + $this->setBinaryData("Categorie", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // ExtraPoints + $length = 4 * 20; + $this->setBinaryData("ExtraPoints", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // SelectP + $length = 4 * 20; + $this->setBinaryData("SelectP", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // Players + for ($i = 0; $i < $this->getBinaryData("NewPlayer"); $i++) { + $player = new Player(); + + $length = 4; + $player->SetRank($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 4; + $this->setBinaryData("Players($i)_NamePos", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 4; + $player->SetFideId($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 4; + $player->SetExtraPts($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 4; + $player->SetKbsbElo($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 4; + $player->SetDateOfBirth($this->readData('Date', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 4; + $player->setKbsbID($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 4; + $player->setPoints($this->readData('Int', substr($swscontents, $offset, $length)) / 2); + $offset += $length; + + $length = 4; + $player->setClubNr($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 4; + $player->setScoreBucholtz($this->readData('Int', substr($swscontents, $offset, $length)) / 2); + $offset += $length; + + $length = 4; + $player->setScoreAmerican($this->readData('Int', substr($swscontents, $offset, $length)) / 2); + $offset += $length; + + $length = 4; + $this->setBinaryData("Players($i)_HelpValue", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 4; + $player->setFideElo($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 1; + $this->setBinaryData("Players($i)_NameLength", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 3; + $player->setNation($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 1; + $player->setCategory($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 1; + switch ($this->readData('Int', substr($swscontents, $offset, $length))) { + case 1: + $title = Title::ELO; + break; + case 2: + $title = Title::NM; + break; + case 3: + $title = Title::WCM; + break; + case 4: + $title = Title::WFM; + break; + case 5: + $title = Title::CM; + break; + case 6: + $title = Title::WIM; + break; + case 7: + $title = Title::FM; + break; + case 8: + $title = Title::WGM; + break; + case 9: + $title = Title::HM; + break; + case 10: + $title = Title::IM; + break; + case 11: + $title = Title::HG; + break; + case 12: + $title = Title::GM; + break; + case 0: + default: + $title = Title::NONE; + break; + } + $player->setTitle(new Title($title)); + $offset += $length; + + $length = 1; + switch ($this->readData('Int', substr($swscontents, $offset, $length))) { + case 1: + $gender = Gender::Male; + break; + case 2: + $gender = Gender::Female; + break; + default: + $gender = Gender::Neutral; + break; + } + $player->setGender(new Gender($gender)); + $offset += $length; + + $length = 1; + $player->setNumberOfTies($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 1; + $player->setAbsent($this->readData('Bool', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 1; + $this->setBinaryData("Players($i)_ColorDiff", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 1; + $this->setBinaryData("Players($i)_ColorPref", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 1; + $this->setBinaryData("Players($i)_Paired", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 1; + $this->setBinaryData("Players($i)_Float", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 1; + $this->setBinaryData("Players($i)_FloatPrev", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 1; + $this->setBinaryData("Players($i)_FloatBefore", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $length = 1; + $this->setBinaryData("Players($i)_TieMatch", $this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + $this->getTournament()->addPlayer($player); + } + // PlayerNames + $length = (Integer)$this->getBinaryData("NewNamePos") + 0; + $this->setBinaryData("PlayerNames", substr($swscontents, $offset, $length)); + $offset += $length; + + for ($i = 0; $i < $this->getBinaryData("NewPlayer"); $i++) { + $namelength = $this->getBinaryData("Players($i)_NameLength"); + $nameoffset = $this->getBinaryData("Players($i)_NamePos"); + $player = $this->getTournament()->getPlayerById($i); + $player->setName($this->readData("String", substr($this->getBinaryData("PlayerNames"), $nameoffset, $namelength))); + + $this->getTournament()->updatePlayer($i, $player); + } + + // TournamentName + $length = 80; + $this->getTournament()->setName($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + // TournamentOrganiser + $length = 50; + $this->getTournament()->setOrganiser($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + // TournamentTempo + $length = 50; + $this->getTournament()->setTempo($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + // TournamentCountry + $length = 32; + $this->getTournament()->setOrganiserCountry($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + // Arbiters + $length = 128; + $this->getTournament()->setArbiter($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + // Rounds + $length = 4; + $this->getTournament()->setNoOfRounds($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // Participants + $length = 4; + $this->getTournament()->setParticipants($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // Fidehomol + $length = 4; + $this->getTournament()->setFideHomol($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // StartDate + $length = 4; + $this->getTournament()->setStartDate($this->readData('Date', substr($swscontents, $offset, $length))); + $offset += $length; + + // EndDate + $length = 4; + $this->getTournament()->setEndDate($this->readData('Date', substr($swscontents, $offset, $length))); + $offset += $length; + + // Place + $length = 36; + $this->getTournament()->setOrganiserPlace($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + // First period + $length = 32; + $this->getTournament()->setFirstPeriod($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + // Second period + $length = 32; + $this->getTournament()->setSecondPeriod($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + // Unrated Elo + $length = 4; + $this->getTournament()->setNonRatedElo($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // Type + $length = 4; + switch ($this->readData('Int', substr($swscontents, $offset, $length))) { + case 2: + $system = TournamentSystem::Closed; + break; + case 4: + $system = TournamentSystem::American; + break; + case 6: + $system = TournamentSystem::Imperial; + break; + case 0: + default: + $system = TournamentSystem::Swiss; + break; + } + $this->getTournament()->setSystem(new TournamentSystem($system)); + $offset += $length; + + // Federation + $length = 12; + $this->getTournament()->setFederation($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + // Soustype + /* + * 32 Bits: + * 1 bit = Libre? + * 6 bits = First round sent to FIDE + * 6 bits = First round sent to FRBE-KBSB + * 6 bits = Last round sent to FIDE + * 6 bits = Last round sent to FRBE-KBSB + * 6 bits = Number of the First board + * 1 bit = Double round robin + */ + $length = 4; + $this->setBinaryData('SousType', $this->readData('Hex', substr($swscontents, $offset, $length))); + $offset += $length; + + // Organising club no + $length = 4; + $this->getTournament()->setOrganiserClubNo($this->readData('String', substr($swscontents, $offset, $length), 0)); + $offset += $length; + + // Organising club + $length = 8; + $this->getTournament()->setOrganiserClub($this->readData('String', substr($swscontents, $offset, $length))); + $offset += $length; + + // Tournament year + $length = 4; + $this->getTournament()->setYear($this->readData('Int', substr($swscontents, $offset, $length))); + $offset += $length; + + // Round dates + for ($i = 0; $i < $this->getTournament()->getNoOfRounds(); $i++) { + $length = 4; + $round = new Round(); + $round->setRoundNo($i); + $round->setDate($this->readData('Date', substr($swscontents, $offset, $length))); + $this->getTournament()->addRound($round); + $offset += $length; + } + + if ($this->getBinaryData("CurrentRound") > 0) { + for ($i = 0; $i < $this->getBinaryData("NewPlayer"); $i++) { + for ($x = 0; $x < $this->getBinaryData("CreatedRounds"); $x++) { + $pairing = new Pairing(); + + $pairing->setPlayer($this->getTournament()->getPlayerById($i)); + + $length = 4; + $opponent = $this->readData('Int', substr($swscontents, $offset, $length)); + if ($opponent != 4294967295) { + $pairing->setOpponent($this->getTournament()->getPlayerById($opponent)); + } + $offset += $length; + + $length = 1; + + switch ($this->readData('Int', substr($swscontents, $offset, $length))) { + case 255: + case 253: + $color = Color::black; + break; + case 1: + case 3: + $color = Color::white; + break; + case 0: + default: + $color = Color::none; + break; + } + $pairing->setColor(new Color($color)); + $offset += $length; + + $length = 1; + switch ($this->readData('Int', substr($swscontents, $offset, $length))) { + case 1: + $result = Result::lost; + break; + case 2: + $result = Result::absent; + break; + case 3: + $result = Result::adjourned; + break; + case 4: + $result = Result::bye; + break; + case 6: + $result = Result::draw; + break; + case 8: + $result = Result::drawadjourned; + break; + case 11: + $result = Result::won; + break; + case 12: + $result = Result::wonforfait; + break; + case 13: + $result = Result::wonadjourned; + break; + case 14: + $result = Result::wonbye; + break; + case 0: + default: + $result = Result::none; + break; + } + $pairing->setResult(new Result($result)); + $offset += $length; + + $pairing->setRound($x); + $offset += 2; + + if ($x < $this->getBinaryData("CurrentRound")) { + $this->getTournament()->addPairing($pairing); + } + } + } + } + + $this->getTournament()->pairingsToRounds(); + return $this; + } + + /** + * Converts $data to $type and defaults to $default if given + * + * Possible types for $type are: + * * String (UTF-8 String representation of $data. Default: empty string '') + * * Hex (Capitalized Hex Value of $data. Default: 00) + * * Int (Unsigned Integer value of $data Default: 0) + * * Bool (Boolean representation of $data. Default: false) + * * Date (Date representation of $data. Default: 1902/01/01) + * + * @param string $type + * @param string $data + * @param mixed $default + * @return bool|DateTime|int|string + */ + private function readData(string $type, string $data, $default = null) + { + switch ($type) { + case 'String': + $data = trim($data); + if ($data == '') { + return (is_null($default)) ? '' : $default; + } + return iconv('windows-1252', 'utf-8', $data); + break; + case 'Hex': + case 'Int': + case 'Bool': + case 'Date': + $hex = implode(unpack("H*", $data)); + $hex = array_reverse(str_split($hex, 2)); + + foreach ($hex as $key => $item) { + if ($item == "00") { + $hex[$key] = ""; + } else { + break; + } + } + + $hex = implode($hex); + $hex = ($hex == "") ? "00" : $hex; + if ($type == 'Hex') { + if ($hex == '00') { + return (is_null($default)) ? '00' : $default; + } + return $hex; + } elseif ($type == 'Int') { + if ($hex == '00') { + return (is_null($default)) ? 0 : $default; + } + return hexdec($hex); + } elseif ($type == 'Date') { + if ($hex == '00') { + return (is_null($default)) ? $this->convertUIntToTimestamp(0) : $default; + } + return $this->convertUIntToTimestamp(hexdec($hex)); + } elseif ($type == 'Bool') { + return ($hex == "01") ? true : false; + } + break; + default: + throw new \InvalidArgumentException("Datatype not known"); + break; + } + + return false; + } + + /** + * Converts integer value to a date representation + * + * @param int $date + * @return bool|DateTime + */ + private function convertUIntToTimestamp(int $date) + { + $curyear = date('Y'); + $yearoffset = $curyear - self::PT_PASTOFFSET; + + // Day + $day = $date % self::PT_DAYFACTOR; + if ($day < 1) { + $day = 1; + } + + // Month + $month = ($date / self::PT_DAYFACTOR) % self::PT_MONTHFACTOR; + if ($month < 1) { + $month = 1; + } + + // Year + $year = ($date / self::PT_YEARFACTOR) + $yearoffset; + + $concat = $month . '/' . $day . '/' . intval($year); + $format = 'm/d/Y'; + + + return DateTime::createFromFormat($format, $concat); + } +} diff --git a/src/Sws.php b/src/Sws.php index 4c242ba..18e6386 100644 --- a/src/Sws.php +++ b/src/Sws.php @@ -26,713 +26,28 @@ namespace JeroenED\Libpairtwo; -use JeroenED\Libpairtwo\Enums\Title; -use JeroenED\Libpairtwo\Enums\Gender; -use JeroenED\Libpairtwo\Enums\Color; -use JeroenED\Libpairtwo\Enums\Result; -use JeroenED\Libpairtwo\Models\Sws as SwsModel; -use JeroenED\Libpairtwo\Enums\TournamentSystem; -use DateTime; + /** * This class reads a SWS file * + * @deprecated * @author Jeroen De Meerleer */ -class Sws extends SwsModel +class Sws { - private const PT_DAYFACTOR = 32; - private const PT_MONTHFACTOR = 16; - private const PT_YEARFACTOR = 512; - private const PT_PASTOFFSET = 117; - /** - * Reads out $swsfile and returns a Sws class object + * Reads out $swsfile and returns a Pairtwo6 class object * * @param string $swsfile - * @return Sws + * @deprecated */ public static function ReadSws(string $swsfile) { - $swshandle = fopen($swsfile, 'rb'); - $swscontents = fread($swshandle, filesize($swsfile)); - fclose($swshandle); - - $sws = new Sws(); - $offset = 0; - - - $length = 4; - $sws->setRelease(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - $sws->setTournament(new Tournament()); - - // UserCountry - $length = 4; - $sws->setBinaryData("UserCountry", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // SavedOffset - $length = 4; - $sws->setBinaryData("SavedOffset", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // NewPlayer - $length = 4; - $sws->setBinaryData("NewPlayer", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // AmericanHandicap - $length = 4; - $sws->setBinaryData("AmericanHandicap", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // LowOrder - $length = 4; - $sws->setBinaryData("LowOrder", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // PairingMethod - $length = 4; - $sws->setBinaryData("PairingMethod", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // AmericanPresence - $length = 4; - $sws->setBinaryData("AmericanPresence", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // CheckSameClub - $length = 4; - $sws->setBinaryData("CheckSameClub", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // NoColorCheck - $length = 4; - $sws->setBinaryData("NoColorCheck", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // SeparateCategories - $length = 4; - $sws->setBinaryData("SeparateCategories", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // EloUsed - $length = 4; - $sws->setBinaryData("EloUsed", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // AlternateColors - $length = 4; - $sws->setBinaryData("AlternateColors", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // MaxMeetings - $length = 4; - $sws->setBinaryData("MaxMeetings", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // MaxDistance - $length = 4; - $sws->setBinaryData("MaxDistance", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // MinimizeKeizer - $length = 4; - $sws->setBinaryData("MinimizeKeizer", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // MinRoundsMeetings - $length = 4; - $sws->setBinaryData("MinRoundsMeetings", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // MaxRoundsAbsent - $length = 4; - $sws->setBinaryData("MaxRoundsAbsent", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // SpecialPoints - $length = 4 * 6; - $sws->setBinaryData("SpecialPoints", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // NewNamePos - $length = 4; - $sws->setBinaryData("NewNamePos", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // CurrentRound - $length = 4; - $sws->setBinaryData("CurrentRound", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // CreatedRounds - $length = 4; - $sws->setBinaryData("CreatedRounds", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // CreatedPlayers - $length = 4; - $sws->setBinaryData("CreatedPlayers", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // MaxSelection - $length = 4; - $sws->setBinaryData("MaxSelection", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // NumberOfRounds - $length = 4; - $sws->setBinaryData("NumberOfRounds", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // NumberOfPairings - $length = 4; - $sws->setBinaryData("NumberOfPairings", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // CreatedPairings - $length = 4; - $sws->setBinaryData("CreatedPairings", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // PairingElems - $length = 4; - $sws->setBinaryData("PairingElems", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // RandomSeed - $length = 4; - $sws->setBinaryData("RandomSeed", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // TieOrder - $length = 4 * 5; - $sws->setBinaryData("TieOrder", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // Categorie - $length = 4 * 10; - $sws->setBinaryData("Categorie", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // ExtraPoints - $length = 4 * 20; - $sws->setBinaryData("ExtraPoints", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // SelectP - $length = 4 * 20; - $sws->setBinaryData("SelectP", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // Players - for ($i = 0; $i < $sws->getBinaryData("NewPlayer"); $i++) { - $player = new Player(); - - $length = 4; - $player->SetRank(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 4; - $sws->setBinaryData("Players($i)_NamePos", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 4; - $player->SetFideId(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 4; - $player->SetExtraPts(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 4; - $player->SetKbsbElo(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 4; - $player->SetDateOfBirth(self::ReadData('Date', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 4; - $player->setKbsbID(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 4; - $player->setPoints(self::ReadData('Int', substr($swscontents, $offset, $length)) / 2); - $offset += $length; - - $length = 4; - $player->setClubNr(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 4; - $player->setScoreBucholtz(self::ReadData('Int', substr($swscontents, $offset, $length)) / 2); - $offset += $length; - - $length = 4; - $player->setScoreAmerican(self::ReadData('Int', substr($swscontents, $offset, $length)) / 2); - $offset += $length; - - $length = 4; - $sws->setBinaryData("Players($i)_HelpValue", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 4; - $player->setFideElo(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 1; - $sws->setBinaryData("Players($i)_NameLength", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 3; - $player->setNation(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 1; - $player->setCategory(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 1; - switch (self::ReadData('Int', substr($swscontents, $offset, $length))) { - case 1: - $title = Title::ELO; - break; - case 2: - $title = Title::NM; - break; - case 3: - $title = Title::WCM; - break; - case 4: - $title = Title::WFM; - break; - case 5: - $title = Title::CM; - break; - case 6: - $title = Title::WIM; - break; - case 7: - $title = Title::FM; - break; - case 8: - $title = Title::WGM; - break; - case 9: - $title = Title::HM; - break; - case 10: - $title = Title::IM; - break; - case 11: - $title = Title::HG; - break; - case 12: - $title = Title::GM; - break; - case 0: - default: - $title = Title::NONE; - break; - } - $player->setTitle(new Title($title)); - $offset += $length; - - $length = 1; - switch (self::ReadData('Int', substr($swscontents, $offset, $length))) { - case 1: - $gender = Gender::Male; - break; - case 2: - $gender = Gender::Female; - break; - default: - $gender = Gender::Neutral; - break; - } - $player->setGender(new Gender($gender)); - $offset += $length; - - $length = 1; - $player->setNumberOfTies(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 1; - $player->setAbsent(self::ReadData('Bool', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 1; - $sws->setBinaryData("Players($i)_ColorDiff", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 1; - $sws->setBinaryData("Players($i)_ColorPref", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 1; - $sws->setBinaryData("Players($i)_Paired", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 1; - $sws->setBinaryData("Players($i)_Float", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 1; - $sws->setBinaryData("Players($i)_FloatPrev", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 1; - $sws->setBinaryData("Players($i)_FloatBefore", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $length = 1; - $sws->setBinaryData("Players($i)_TieMatch", self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - $sws->getTournament()->addPlayer($player); - } - // PlayerNames - $length = (Integer)$sws->getBinaryData("NewNamePos") + 0; - $sws->setBinaryData("PlayerNames", substr($swscontents, $offset, $length)); - $offset += $length; - - for ($i = 0; $i < $sws->getBinaryData("NewPlayer"); $i++) { - $namelength = $sws->getBinaryData("Players($i)_NameLength"); - $nameoffset = $sws->getBinaryData("Players($i)_NamePos"); - $player = $sws->getTournament()->getPlayerById($i); - $player->setName(self::ReadData("String", substr($sws->getBinaryData("PlayerNames"), $nameoffset, $namelength))); - - $sws->getTournament()->updatePlayer($i, $player); - } - - // TournamentName - $length = 80; - $sws->getTournament()->setName(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - // TournamentOrganiser - $length = 50; - $sws->getTournament()->setOrganiser(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - // TournamentTempo - $length = 50; - $sws->getTournament()->setTempo(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - // TournamentCountry - $length = 32; - $sws->getTournament()->setOrganiserCountry(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - // Arbiters - $length = 128; - $sws->getTournament()->setArbiter(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - // Rounds - $length = 4; - $sws->getTournament()->setNoOfRounds(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // Participants - $length = 4; - $sws->getTournament()->setParticipants(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // Fidehomol - $length = 4; - $sws->getTournament()->setFideHomol(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // StartDate - $length = 4; - $sws->getTournament()->setStartDate(self::ReadData('Date', substr($swscontents, $offset, $length))); - $offset += $length; - - // EndDate - $length = 4; - $sws->getTournament()->setEndDate(self::ReadData('Date', substr($swscontents, $offset, $length))); - $offset += $length; - - // Place - $length = 36; - $sws->getTournament()->setOrganiserPlace(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - // First period - $length = 32; - $sws->getTournament()->setFirstPeriod(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - // Second period - $length = 32; - $sws->getTournament()->setSecondPeriod(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - // Unrated Elo - $length = 4; - $sws->getTournament()->setNonRatedElo(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // Type - $length = 4; - switch (self::ReadData('Int', substr($swscontents, $offset, $length))) { - case 2: - $system = TournamentSystem::Closed; - break; - case 4: - $system = TournamentSystem::American; - break; - case 6: - $system = TournamentSystem::Imperial; - break; - case 0: - default: - $system = TournamentSystem::Swiss; - break; - } - $sws->getTournament()->setSystem(new TournamentSystem($system)); - $offset += $length; - - // Federation - $length = 12; - $sws->getTournament()->setFederation(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - // Soustype - /* - * 32 Bits: - * 1 bit = Libre? - * 6 bits = First round sent to FIDE - * 6 bits = First round sent to FRBE-KBSB - * 6 bits = Last round sent to FIDE - * 6 bits = Last round sent to FRBE-KBSB - * 6 bits = Number of the First board - * 1 bit = Double round robin - */ - $length = 4; - $sws->setBinaryData('SousType', self::ReadData('Hex', substr($swscontents, $offset, $length))); - $offset += $length; - - // Organising club no - $length = 4; - $sws->getTournament()->setOrganiserClubNo(self::ReadData('String', substr($swscontents, $offset, $length), 0)); - $offset += $length; - - // Organising club - $length = 8; - $sws->getTournament()->setOrganiserClub(self::ReadData('String', substr($swscontents, $offset, $length))); - $offset += $length; - - // Tournament year - $length = 4; - $sws->getTournament()->setYear(self::ReadData('Int', substr($swscontents, $offset, $length))); - $offset += $length; - - // Round dates - for ($i = 0; $i < $sws->getTournament()->getNoOfRounds(); $i++) { - $length = 4; - $round = new Round(); - $round->setRoundNo($i); - $round->setDate(self::ReadData('Date', substr($swscontents, $offset, $length))); - $sws->getTournament()->addRound($round); - $offset += $length; - } - - if ($sws->getBinaryData("CurrentRound") > 0) { - for ($i = 0; $i < $sws->getBinaryData("NewPlayer"); $i++) { - for ($x = 0; $x < $sws->getBinaryData("CreatedRounds"); $x++) { - $pairing = new Pairing(); - - $pairing->setPlayer($sws->getTournament()->getPlayerById($i)); - - $length = 4; - $opponent = self::ReadData('Int', substr($swscontents, $offset, $length)); - if ($opponent != 4294967295) { - $pairing->setOpponent($sws->getTournament()->getPlayerById($opponent)); - } - $offset += $length; - - $length = 1; - - switch (self::ReadData('Int', substr($swscontents, $offset, $length))) { - case 255: - case 253: - $color = Color::black; - break; - case 1: - case 3: - $color = Color::white; - break; - case 0: - default: - $color = Color::none; - break; - } - $pairing->setColor(new Color($color)); - $offset += $length; - - $length = 1; - switch (self::ReadData('Int', substr($swscontents, $offset, $length))) { - case 1: - $result = Result::lost; - break; - case 2: - $result = Result::absent; - break; - case 3: - $result = Result::adjourned; - break; - case 4: - $result = Result::bye; - break; - case 6: - $result = Result::draw; - break; - case 8: - $result = Result::drawadjourned; - break; - case 11: - $result = Result::won; - break; - case 12: - $result = Result::wonforfait; - break; - case 13: - $result = Result::wonadjourned; - break; - case 14: - $result = Result::wonbye; - break; - case 0: - default: - $result = Result::none; - break; - } - $pairing->setResult(new Result($result)); - $offset += $length; - - $pairing->setRound($x); - $offset += 2; - - if ($x < $sws->getBinaryData("CurrentRound")) { - $sws->getTournament()->addPairing($pairing); - } - } - } - } - - $sws->getTournament()->pairingsToRounds(); - return $sws; + trigger_error ( "This function is deprecated. Please convert your code to use the new pattern. More info to be found on Github (https://github.com/JeroenED/libpairtwo/wiki/Converting-your-code-to-the-generalized-format).", E_USER_DEPRECATED); + $reader = IOFactory::createReader("Pairtwo-6"); + return $reader->read($swsfile); } - /** - * Converts $data to $type and defaults to $default if given - * - * Possible types for $type are: - * * String (UTF-8 String representation of $data. Default: empty string '') - * * Hex (Capitalized Hex Value of $data. Default: 00) - * * Int (Unsigned Integer value of $data Default: 0) - * * Bool (Boolean representation of $data. Default: false) - * * Date (Date representation of $data. Default: 1902/01/01) - * - * @static - * @param string $type - * @param string $data - * @param mixed $default - * @return bool|DateTime|int|string - */ - private static function ReadData(string $type, string $data, $default = null) - { - switch ($type) { - case 'String': - $data = trim($data); - if ($data == '') { - return (is_null($default)) ? '' : $default; - } - return iconv('windows-1252', 'utf-8', $data); - break; - case 'Hex': - case 'Int': - case 'Bool': - case 'Date': - $hex = implode(unpack("H*", $data)); - $hex = array_reverse(str_split($hex, 2)); - - foreach ($hex as $key => $item) { - if ($item == "00") { - $hex[$key] = ""; - } else { - break; - } - } - - $hex = implode($hex); - $hex = ($hex == "") ? "00" : $hex; - if ($type == 'Hex') { - if ($hex == '00') { - return (is_null($default)) ? '00' : $default; - } - return $hex; - } elseif ($type == 'Int') { - if ($hex == '00') { - return (is_null($default)) ? 0 : $default; - } - return hexdec($hex); - } elseif ($type == 'Date') { - if ($hex == '00') { - return (is_null($default)) ? self::UIntToTimestamp(0) : $default; - } - return self::UIntToTimestamp(hexdec($hex)); - } elseif ($type == 'Bool') { - return ($hex == "01") ? true : false; - } - break; - default: - throw new \InvalidArgumentException("Datatype not known"); - break; - } - - return false; - } - - /** - * Converts integer value to a date representation - * - * @static - * @param int $date - * @return bool|DateTime - */ - private static function UIntToTimestamp(int $date) - { - $curyear = date('Y'); - $yearoffset = $curyear - self::PT_PASTOFFSET; - - // Day - $day = $date % self::PT_DAYFACTOR; - if ($day < 1) { - $day = 1; - } - - // Month - $month = ($date / self::PT_DAYFACTOR) % self::PT_MONTHFACTOR; - if ($month < 1) { - $month = 1; - } - - // Year - $year = ($date / self::PT_YEARFACTOR) + $yearoffset; - - $concat = $month . '/' . $day . '/' . intval($year); - $format = 'm/d/Y'; - - - return DateTime::createFromFormat($format, $concat); - } } diff --git a/tests/ReadSws_test.php b/tests/ReadSws_test.php index 8feffff..2b78ce8 100644 --- a/tests/ReadSws_test.php +++ b/tests/ReadSws_test.php @@ -23,11 +23,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -use JeroenED\Libpairtwo\Sws; +use JeroenED\Libpairtwo\IOFactory; require_once '../vendor/autoload.php'; -$sws = Sws::readSws('../res/testsws.sws'); +$sws = IOFactory::createReader('Pairtwo-6'); +$sws->read('../res/testsws.sws'); + echo "Release: " . $sws->getRelease() . PHP_EOL; echo "Name: " . $sws->getTournament()->getName() . PHP_EOL; echo "Organiser: " . $sws->getTournament()->getOrganiser(). PHP_EOL;