diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9ca78e54d5..b6a51a2efb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,6 +17,7 @@ add_subdirectory(linkgraph) add_subdirectory(misc) add_subdirectory(music) add_subdirectory(network) +add_subdirectory(newgrf) add_subdirectory(os) add_subdirectory(pathfinder) add_subdirectory(saveload) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 24d9cee663..b5ed37dc67 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -54,6 +54,7 @@ #include "vehicle_base.h" #include "road.h" #include "newgrf_roadstop.h" +#include "newgrf/newgrf_bytereader.h" #include "table/strings.h" #include "table/build_industry.h" @@ -213,102 +214,6 @@ static inline bool IsValidNewGRFImageIndex(uint8_t image_index) return image_index == 0xFD || IsValidImageIndex(image_index); } -class OTTDByteReaderSignal { }; - -/** Class to read from a NewGRF file */ -class ByteReader { -protected: - uint8_t *data; - uint8_t *end; - -public: - ByteReader(uint8_t *data, uint8_t *end) : data(data), end(end) { } - - inline uint8_t *ReadBytes(size_t size) - { - if (data + size >= end) { - /* Put data at the end, as would happen if every byte had been individually read. */ - data = end; - throw OTTDByteReaderSignal(); - } - - uint8_t *ret = data; - data += size; - return ret; - } - - inline uint8_t ReadByte() - { - if (data < end) return *(data)++; - throw OTTDByteReaderSignal(); - } - - uint16_t ReadWord() - { - uint16_t val = ReadByte(); - return val | (ReadByte() << 8); - } - - uint16_t ReadExtendedByte() - { - uint16_t val = ReadByte(); - return val == 0xFF ? ReadWord() : val; - } - - uint32_t ReadDWord() - { - uint32_t val = ReadWord(); - return val | (ReadWord() << 16); - } - - uint32_t PeekDWord() - { - AutoRestoreBackup backup(this->data, this->data); - return this->ReadDWord(); - } - - uint32_t ReadVarSize(uint8_t size) - { - switch (size) { - case 1: return ReadByte(); - case 2: return ReadWord(); - case 4: return ReadDWord(); - default: - NOT_REACHED(); - return 0; - } - } - - std::string_view ReadString() - { - char *string = reinterpret_cast(data); - size_t string_length = ttd_strnlen(string, Remaining()); - - /* Skip past the terminating NUL byte if it is present, but not more than remaining. */ - Skip(std::min(string_length + 1, Remaining())); - - return std::string_view(string, string_length); - } - - inline size_t Remaining() const - { - return end - data; - } - - inline bool HasData(size_t count = 1) const - { - return data + count <= end; - } - - inline void Skip(size_t len) - { - data += len; - /* It is valid to move the buffer to exactly the end of the data, - * as there may not be any more data read. */ - if (data > end) throw OTTDByteReaderSignal(); - } -}; - typedef void (*SpecialSpriteHandler)(ByteReader &buf); /** The maximum amount of stations a single GRF is allowed to add */ diff --git a/src/newgrf/CMakeLists.txt b/src/newgrf/CMakeLists.txt new file mode 100644 index 0000000000..42314e960a --- /dev/null +++ b/src/newgrf/CMakeLists.txt @@ -0,0 +1,4 @@ +add_files( + newgrf_bytereader.cpp + newgrf_bytereader.h +) diff --git a/src/newgrf/newgrf_bytereader.cpp b/src/newgrf/newgrf_bytereader.cpp new file mode 100644 index 0000000000..d09c1dfadc --- /dev/null +++ b/src/newgrf/newgrf_bytereader.cpp @@ -0,0 +1,57 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file newgrf_bytereader.cpp NewGRF byte buffer reader implementation. */ + +#include "../stdafx.h" +#include "../core/backup_type.hpp" +#include "../string_func.h" +#include "newgrf_bytereader.h" + +#include "../safeguards.h" + +/** + * Read a single DWord (32 bits). + * @note The buffer is NOT advanced. + * @returns Value read from buffer. + */ +uint32_t ByteReader::PeekDWord() +{ + AutoRestoreBackup backup(this->data, this->data); + return this->ReadDWord(); +} + +/** + * Read a value of the given number of bytes. + * @returns Value read from buffer. + */ +uint32_t ByteReader::ReadVarSize(uint8_t size) +{ + switch (size) { + case 1: return this->ReadByte(); + case 2: return this->ReadWord(); + case 4: return this->ReadDWord(); + default: + NOT_REACHED(); + return 0; + } +} + +/** + * Read a string. + * @returns Sting read from the buffer. + */ +std::string_view ByteReader::ReadString() +{ + const char *string = reinterpret_cast(this->data); + size_t string_length = ttd_strnlen(string, this->Remaining()); + + /* Skip past the terminating NUL byte if it is present, but not more than remaining. */ + this->Skip(std::min(string_length + 1, this->Remaining())); + + return std::string_view(string, string_length); +} diff --git a/src/newgrf/newgrf_bytereader.h b/src/newgrf/newgrf_bytereader.h new file mode 100644 index 0000000000..b3ceec7f2b --- /dev/null +++ b/src/newgrf/newgrf_bytereader.h @@ -0,0 +1,100 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file newgrf_bytereader.h NewGRF buffer reader definition. */ + +#ifndef NEWGRF_BYTEREADER_H +#define NEWGRF_BYTEREADER_H + +class OTTDByteReaderSignal { }; + +/** Class to read from a NewGRF file */ +class ByteReader { +public: + ByteReader(const uint8_t *data, const uint8_t *end) : data(data), end(end) { } + + const uint8_t *ReadBytes(size_t size) + { + if (this->data + size >= this->end) { + /* Put data at the end, as would happen if every byte had been individually read. */ + this->data = this->end; + throw OTTDByteReaderSignal(); + } + + const uint8_t *ret = this->data; + this->data += size; + return ret; + } + + /** + * Read a single byte (8 bits). + * @return Value read from buffer. + */ + uint8_t ReadByte() + { + if (this->data < this->end) return *this->data++; + throw OTTDByteReaderSignal(); + } + + /** + * Read a single Word (16 bits). + * @return Value read from buffer. + */ + uint16_t ReadWord() + { + uint16_t val = this->ReadByte(); + return val | (this->ReadByte() << 8); + } + + /** + * Read a single Extended Byte (8 or 16 bits). + * @return Value read from buffer. + */ + uint16_t ReadExtendedByte() + { + uint16_t val = this->ReadByte(); + return val == 0xFF ? this->ReadWord() : val; + } + + /** + * Read a single DWord (32 bits). + * @return Value read from buffer. + */ + uint32_t ReadDWord() + { + uint32_t val = this->ReadWord(); + return val | (this->ReadWord() << 16); + } + + uint32_t PeekDWord(); + uint32_t ReadVarSize(uint8_t size); + std::string_view ReadString(); + + size_t Remaining() const + { + return this->end - this->data; + } + + bool HasData(size_t count = 1) const + { + return this->data + count <= this->end; + } + + void Skip(size_t len) + { + this->data += len; + /* It is valid to move the buffer to exactly the end of the data, + * as there may not be any more data read. */ + if (this->data > this->end) throw OTTDByteReaderSignal(); + } + +private: + const uint8_t *data; ///< Current position within data. + const uint8_t *end; ///< Last position of data. +}; + +#endif /* NEWGRF_BYTEREADER_H */