diff --git a/README.md b/README.md index bc2b9b436b..b1941afa34 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,12 @@ Panda3D Panda3D is a game engine, a framework for 3D rendering and game development for Python and C++ programs. Panda3D is open-source and free for any purpose, including commercial ventures, thanks to its -[liberal license](https://www.panda3d.org/license.php). To learn more about -Panda3D's capabilities, visit the [gallery](https://www.panda3d.org/gallery.php) -and the [feature list](https://www.panda3d.org/features.php). To learn how to -use Panda3D, check the [documentation](https://www.panda3d.org/documentation.php) +[liberal license](https://www.panda3d.org/license/). To learn more about +Panda3D's capabilities, visit the [gallery](https://www.panda3d.org/gallery/) +and the [feature list](https://www.panda3d.org/features/). To learn how to +use Panda3D, check the [documentation](https://www.panda3d.org/documentation/) resources. If you get stuck, ask for help from our active -[community](https://www.panda3d.org/community.php). +[community](https://discourse.panda3d.org). Panda3D is licensed under the Modified BSD License. See the LICENSE file for more details. @@ -21,7 +21,16 @@ more details. Installing Panda3D ================== -By far, the easiest way to install the latest development build of Panda3D +The latest Panda3D SDK can be downloaded from +(this page)[https://www.panda3d.org/download/sdk-1-10-0/]. +If you are familiar with installing Python packages, you can use +the following comand: + +```bash +pip install panda3d +``` + +The easiest way to install the latest development build of Panda3D into an existing Python installation is using the following command: ```bash @@ -31,9 +40,7 @@ pip install --pre --extra-index-url https://archive.panda3d.org/ panda3d If this command fails, please make sure your version of pip is up-to-date. If you prefer to install the full SDK with all tools, the latest development -builds can be obtained from this page: - -https://www.panda3d.org/download.php?sdk&version=devel +builds can be obtained from (this page)[https://www.panda3d.org/download/]. These are automatically kept up-to-date with the latest GitHub version of Panda. @@ -96,7 +103,7 @@ python makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-g You will probably see some warnings saying that it's unable to find several dependency packages. You should determine which ones you want to include in your build and install the respective development packages. You may visit -[this manual page](https://www.panda3d.org/manual/index.php/Dependencies) +[this manual page](https://www.panda3d.org/manual/?title=Third-party_dependencies_and_license_info) for an overview of the various dependencies. If you are on Ubuntu, this command should cover the most frequently diff --git a/dtool/src/prc/streamReader.cxx b/dtool/src/prc/streamReader.cxx index 74f278e220..3286eca67a 100644 --- a/dtool/src/prc/streamReader.cxx +++ b/dtool/src/prc/streamReader.cxx @@ -26,6 +26,9 @@ get_string() { // First, get the length of the string size_t size = get_uint16(); + if (size == 0) { + return string(); + } char *buffer = (char *)alloca(size); _in->read(buffer, size); @@ -42,6 +45,9 @@ get_string32() { // First, get the length of the string size_t size = get_uint32(); + if (size == 0) { + return string(); + } char *buffer = (char *)PANDA_MALLOC_ARRAY(size); _in->read(buffer, size); @@ -60,7 +66,7 @@ get_z_string() { string result; int ch = _in->get(); - while (!_in->eof() && !_in->fail() && ch != '\0') { + while (!_in->fail() && ch != EOF && ch != '\0') { result += (char)ch; ch = _in->get(); } @@ -76,6 +82,10 @@ string StreamReader:: get_fixed_string(size_t size) { nassertr(!_in->eof() && !_in->fail(), string()); + if (size == 0) { + return string(); + } + char *buffer = (char *)alloca(size); _in->read(buffer, size); size_t read_bytes = _in->gcount(); @@ -90,8 +100,9 @@ get_fixed_string(size_t size) { */ void StreamReader:: skip_bytes(size_t size) { - nassertv(!_in->eof() && !_in->fail()); + nassertv(!_in->fail()); nassertv((int)size >= 0); + nassertv(size == 0 || !_in->eof()); while (size > 0) { _in->get(); @@ -145,9 +156,9 @@ string StreamReader:: readline() { string line; int ch = _in->get(); - while (!_in->eof() && !_in->fail()) { + while (ch != EOF && !_in->fail()) { line += (char)ch; - if (ch == '\n') { + if (ch == '\n' || _in->eof()) { // Here's the newline character. return line; } diff --git a/dtool/src/prc/streamReader_ext.cxx b/dtool/src/prc/streamReader_ext.cxx index 94c346e370..8e1b2b42ec 100644 --- a/dtool/src/prc/streamReader_ext.cxx +++ b/dtool/src/prc/streamReader_ext.cxx @@ -45,9 +45,9 @@ readline() { std::string line; int ch = in->get(); - while (!in->eof() && !in->fail()) { + while (ch != EOF && !in->fail()) { line += ch; - if (ch == '\n') { + if (ch == '\n' || in->eof()) { // Here's the newline character. break; } diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index 18f92379b8..6dd2b32ef4 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -526,11 +526,6 @@ def makewheel(version, output_dir, platform=None): # Update relevant METADATA entries METADATA['version'] = version - version_classifiers = [ - "Programming Language :: Python :: {0}".format(*sys.version_info), - "Programming Language :: Python :: {0}.{1}".format(*sys.version_info), - ] - METADATA['classifiers'].extend(version_classifiers) # Build out the metadata details = METADATA["extensions"]["python.details"] diff --git a/panda/src/downloader/decompressor.cxx b/panda/src/downloader/decompressor.cxx index 272279047d..bc4822ee91 100644 --- a/panda/src/downloader/decompressor.cxx +++ b/panda/src/downloader/decompressor.cxx @@ -183,7 +183,7 @@ decompress(const Filename &source_file) { return false; int ch = _decompress->get(); - while (!_decompress->eof() && !_decompress->fail()) { + while (ch != EOF && !_decompress->fail()) { _dest->put(ch); ch = _decompress->get(); } @@ -207,7 +207,7 @@ decompress(Ramfile &source_and_dest_file) { IDecompressStream decompress(&source, false); int ch = decompress.get(); - while (!decompress.eof() && !decompress.fail()) { + while (ch != EOF && !decompress.fail()) { dest.put(ch); ch = decompress.get(); } diff --git a/panda/src/downloader/documentSpec.cxx b/panda/src/downloader/documentSpec.cxx index 6ca2a9b260..34bfe3808d 100644 --- a/panda/src/downloader/documentSpec.cxx +++ b/panda/src/downloader/documentSpec.cxx @@ -66,7 +66,7 @@ input(std::istream &in) { // Scan the tag, up to but not including the closing paren. std::string tag; in >> ch; - while (!in.fail() && !in.eof() && ch != ')') { + while (!in.fail() && ch != EOF && ch != ')') { tag += ch; // We want to include embedded whitespace, so we use get(). ch = in.get(); @@ -81,7 +81,7 @@ input(std::istream &in) { // Scan the date, up to but not including the closing bracket. if (ch != ']') { std::string date; - while (!in.fail() && !in.eof() && ch != ']') { + while (!in.fail() && ch != EOF && ch != ']') { date += ch; ch = in.get(); } diff --git a/panda/src/downloader/httpChannel.cxx b/panda/src/downloader/httpChannel.cxx index 17c12d806a..365c819b0f 100644 --- a/panda/src/downloader/httpChannel.cxx +++ b/panda/src/downloader/httpChannel.cxx @@ -2762,7 +2762,7 @@ bool HTTPChannel:: server_getline(string &str) { nassertr(!_source.is_null(), false); int ch = (*_source)->get(); - while (!(*_source)->eof() && !(*_source)->fail()) { + while (ch != EOF && !(*_source)->fail()) { switch (ch) { case '\n': // end-of-line character, we're done. @@ -2850,7 +2850,7 @@ bool HTTPChannel:: server_get(string &str, size_t num_bytes) { nassertr(!_source.is_null(), false); int ch = (*_source)->get(); - while (!(*_source)->eof() && !(*_source)->fail()) { + while (ch != EOF && !(*_source)->fail()) { _working_get += (char)ch; if (_working_get.length() >= num_bytes) { str = _working_get; diff --git a/panda/src/downloader/httpDate.cxx b/panda/src/downloader/httpDate.cxx index a651b50f89..e49769257f 100644 --- a/panda/src/downloader/httpDate.cxx +++ b/panda/src/downloader/httpDate.cxx @@ -279,7 +279,7 @@ input(std::istream &in) { string date; ch = in.get(); - while (!in.fail() && !in.eof() && ch != '"') { + while (!in.fail() && ch != EOF && ch != '"') { date += ch; ch = in.get(); } diff --git a/panda/src/downloader/socketStream.cxx b/panda/src/downloader/socketStream.cxx index eec65d1857..cd18e7f972 100644 --- a/panda/src/downloader/socketStream.cxx +++ b/panda/src/downloader/socketStream.cxx @@ -56,7 +56,7 @@ do_receive_datagram(Datagram &dg) { // Read the first two bytes: the datagram length. while ((int)_data_so_far.size() < _tcp_header_size) { int ch = _istream->get(); - if (_istream->eof() || _istream->fail()) { + if (ch == EOF || _istream->fail()) { _istream->clear(); return false; } diff --git a/panda/src/express/hashVal.cxx b/panda/src/express/hashVal.cxx index 5d74f1dac4..479f91f752 100644 --- a/panda/src/express/hashVal.cxx +++ b/panda/src/express/hashVal.cxx @@ -50,7 +50,7 @@ input_hex(istream &in) { size_t i = 0; int ch = in.get(); - while (!in.eof() && !in.fail() && isxdigit(ch)) { + while (ch != EOF && !in.fail() && isxdigit(ch)) { if (i < 32) { buffer[i] = (char)ch; } @@ -63,7 +63,7 @@ input_hex(istream &in) { return; } - if (!in.eof()) { + if (ch != EOF) { in.putback((char)ch); } else { in.clear(); diff --git a/panda/src/express/make_ca_bundle.cxx b/panda/src/express/make_ca_bundle.cxx index 0773bb88ec..2a9712b3ce 100644 --- a/panda/src/express/make_ca_bundle.cxx +++ b/panda/src/express/make_ca_bundle.cxx @@ -108,7 +108,7 @@ main(int argc, char *argv[]) { int col = 0; unsigned int ch; ch = in.get(); - while (!in.fail() && !in.eof()) { + while (!in.fail() && ch != EOF) { if (col == 0) { out << "\n "; } else if (col == col_width) { diff --git a/panda/src/express/multifile.cxx b/panda/src/express/multifile.cxx index cdb1c56ec8..4467132b79 100644 --- a/panda/src/express/multifile.cxx +++ b/panda/src/express/multifile.cxx @@ -1785,8 +1785,7 @@ compare_subfile(int index, const Filename &filename) { in2.seekg(0); int byte1 = in1->get(); int byte2 = in2.get(); - while (!in1->fail() && !in1->eof() && - !in2.fail() && !in2.eof()) { + while (!in1->fail() && !in2.fail()) { if (byte1 != byte2) { close_read_subfile(in1); return false; @@ -2497,7 +2496,7 @@ read_index(istream &read, streampos fpos, Multifile *multifile) { StreamReader reader(read); streampos next_index = multifile->word_to_streampos(reader.get_uint32()); - if (read.eof() || read.fail()) { + if (read.fail()) { _flags |= SF_index_invalid; return 0; } @@ -2529,7 +2528,7 @@ read_index(istream &read, streampos fpos, Multifile *multifile) { } size_t name_length = reader.get_uint16(); - if (read.eof() || read.fail()) { + if (read.fail()) { _flags |= SF_index_invalid; return 0; } @@ -2543,7 +2542,7 @@ read_index(istream &read, streampos fpos, Multifile *multifile) { _name = string(name_buffer, name_length); PANDA_FREE_ARRAY(name_buffer); - if (read.eof() || read.fail()) { + if (read.fail()) { _flags |= SF_index_invalid; return 0; } diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index 1ba5b891e3..d28e2d208b 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -4158,7 +4158,7 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) cdata->_num_mipmap_levels_read = cdata->_ram_images.size(); } - if (in.fail() || in.eof()) { + if (in.fail()) { gobj_cat.error() << filename << ": truncated DDS file.\n"; return false; @@ -4924,7 +4924,7 @@ do_read_ktx(CData *cdata, istream &in, const string &filename, bool header_only) } } - if (in.fail() || in.eof()) { + if (in.fail()) { gobj_cat.error() << filename << ": truncated KTX file.\n"; return false; diff --git a/panda/src/pnmimage/pnmimage_base.cxx b/panda/src/pnmimage/pnmimage_base.cxx index 0fb9bedfc6..d5ef09e415 100644 --- a/panda/src/pnmimage/pnmimage_base.cxx +++ b/panda/src/pnmimage/pnmimage_base.cxx @@ -120,7 +120,7 @@ int pm_readbigshort(istream *in, short *sP) { StreamReader reader(in, false); *sP = reader.get_be_int16(); - return (!in->eof() && !in->fail()) ? 0 : -1; + return (!in->fail()) ? 0 : -1; } int @@ -134,7 +134,7 @@ int pm_readbiglong(istream *in, long *lP) { StreamReader reader(in, false); *lP = reader.get_be_int32(); - return (!in->eof() && !in->fail()) ? 0 : -1; + return (!in->fail()) ? 0 : -1; } int @@ -148,7 +148,7 @@ int pm_readlittleshort(istream *in, short *sP) { StreamReader reader(in, false); *sP = reader.get_int16(); - return (!in->eof() && !in->fail()) ? 0 : -1; + return (!in->fail()) ? 0 : -1; } int @@ -162,7 +162,7 @@ int pm_readlittlelong(istream *in, long *lP) { StreamReader reader(in, false); *lP = reader.get_int32(); - return (!in->eof() && !in->fail()) ? 0 : -1; + return (!in->fail()) ? 0 : -1; } int diff --git a/pandatool/src/miscprogs/binToC.cxx b/pandatool/src/miscprogs/binToC.cxx index f963c08986..6eff47c9d1 100644 --- a/pandatool/src/miscprogs/binToC.cxx +++ b/pandatool/src/miscprogs/binToC.cxx @@ -101,7 +101,7 @@ run() { int col = 0; unsigned int ch; ch = in.get(); - while (!in.fail() && !in.eof()) { + while (!in.fail() && ch != EOF) { if (col == 0) { out << "\n "; } else if (col == col_width) { diff --git a/setup.cfg b/setup.cfg index 340e432e56..decaaeff15 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,10 +13,21 @@ classifiers = Operating System :: OS Independent Programming Language :: C++ Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.4 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: Implementation :: CPython Topic :: Games/Entertainment Topic :: Multimedia Topic :: Multimedia :: Graphics Topic :: Multimedia :: Graphics :: 3D Rendering + Topic :: Software Development :: Libraries + Topic :: Software Development :: Libraries :: Application Frameworks + Topic :: Software Development :: Libraries :: Python Modules author = Panda3D Team author_email = etc-panda3d@lists.andrew.cmu.edu diff --git a/tests/express/test_multifile.py b/tests/express/test_multifile.py new file mode 100644 index 0000000000..8618688eb2 --- /dev/null +++ b/tests/express/test_multifile.py @@ -0,0 +1,12 @@ +from panda3d.core import Multifile, StringStream, IStreamWrapper + + +def test_multifile_read_empty(): + stream = StringStream(b'pmf\x00\n\r\x01\x00\x01\x00\x01\x00\x00\x00\xdb\x9d7\\\x00\x00\x00\x00') + wrapper = IStreamWrapper(stream) + + m = Multifile() + assert m.open_read(wrapper) + assert m.is_read_valid() + assert m.get_num_subfiles() == 0 + m.close() diff --git a/tests/prc/test_stream_reader.py b/tests/prc/test_stream_reader.py new file mode 100644 index 0000000000..de40e40b80 --- /dev/null +++ b/tests/prc/test_stream_reader.py @@ -0,0 +1,157 @@ +from panda3d.core import StreamReader, StringStream +import pytest + + +def test_streamreader_string(): + # Empty string + stream = StringStream(b'\x00\x00') + reader = StreamReader(stream, False) + assert reader.get_string() == '' + + # String size but no string contents + stream = StringStream(b'\x01\x00') + reader = StreamReader(stream, False) + assert reader.get_string() == '' + + # String of length 1 + stream = StringStream(b'\x01\x00A') + reader = StreamReader(stream, False) + assert reader.get_string() == 'A' + + # String with excess data + stream = StringStream(b'\x01\x00AB') + reader = StreamReader(stream, False) + assert reader.get_string() == 'A' + + # EOF before end of string + stream = StringStream(b'\x03\x00AB') + reader = StreamReader(stream, False) + assert reader.get_string() == 'AB' + + # Preserves null bytes + stream = StringStream(b'\x02\x00\x00\x00') + reader = StreamReader(stream, False) + assert reader.get_string() == '\x00\x00' + + +def test_streamreader_string32(): + # Empty string + stream = StringStream(b'\x00\x00\x00\x00') + reader = StreamReader(stream, False) + assert reader.get_string32() == '' + + # String size but no string contents + stream = StringStream(b'\x01\x00\x00\x00') + reader = StreamReader(stream, False) + assert reader.get_string32() == '' + + # String of length 1 + stream = StringStream(b'\x01\x00\x00\x00A') + reader = StreamReader(stream, False) + assert reader.get_string32() == 'A' + + # String with excess data + stream = StringStream(b'\x01\x00\x00\x00AB') + reader = StreamReader(stream, False) + assert reader.get_string32() == 'A' + + # EOF before end of string + stream = StringStream(b'\x04\x00\x00\x00AB') + reader = StreamReader(stream, False) + assert reader.get_string32() == 'AB' + + # Preserves null bytes + stream = StringStream(b'\x02\x00\x00\x00\x00\x00') + reader = StreamReader(stream, False) + assert reader.get_string32() == '\x00\x00' + + +def test_streamreader_z_string(): + # Empty stream + stream = StringStream(b'') + reader = StreamReader(stream, False) + assert reader.get_z_string() == '' + + # Empty string + stream = StringStream(b'\x00') + reader = StreamReader(stream, False) + assert reader.get_z_string() == '' + + # String of length 1 + stream = StringStream(b'A\x00') + reader = StreamReader(stream, False) + assert reader.get_z_string() == 'A' + + # String with excess data + stream = StringStream(b'ABC\x00AB') + reader = StreamReader(stream, False) + assert reader.get_z_string() == 'ABC' + + # EOF before end of string + stream = StringStream(b'ABC') + reader = StreamReader(stream, False) + assert reader.get_z_string() == 'ABC' + + +def test_streamreader_fixed_string(): + # Zero-length string + stream = StringStream(b'ABC') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(0) == '' + + # Empty stream + stream = StringStream(b'') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(1) == '' + + # Empty string + stream = StringStream(b'\x00') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(1) == '' + + # String of length 1 + stream = StringStream(b'A') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(1) == 'A' + + # String of length 1, excess data + stream = StringStream(b'ABC\x00') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(1) == 'A' + + # EOF before end of string + stream = StringStream(b'AB') + reader = StreamReader(stream, False) + assert reader.get_fixed_string(4) == 'AB' + + +def test_streamreader_readline(): + # Empty stream + stream = StringStream(b'') + reader = StreamReader(stream, False) + assert reader.readline() == b'' + assert reader.readline() == b'' + + # Single line without newline + stream = StringStream(b'A') + reader = StreamReader(stream, False) + assert reader.readline() == b'A' + assert reader.readline() == b'' + + # Single newline + stream = StringStream(b'\n') + reader = StreamReader(stream, False) + assert reader.readline() == b'\n' + assert reader.readline() == b'' + + # Line with text followed by empty line + stream = StringStream(b'A\n\n') + reader = StreamReader(stream, False) + assert reader.readline() == b'A\n' + assert reader.readline() == b'\n' + assert reader.readline() == b'' + + # Preserve null byte + stream = StringStream(b'\x00\x00') + reader = StreamReader(stream, False) + assert reader.readline() == b'\x00\x00'