{ "info": { "author": "Tracy Poff", "author_email": "tracy.poff@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# Classic Game Resource Reader\n\ncgrr holds utility functions used by other modules for parsing game\nresource files.\n\n## Package contents\n\nAt present, cgrr.py provides three things:\n\n1. `verify`, a simple function to verify that certain files exist in a path\n2. `File`, a namedtuple to be used with `verify`\n3. `FileReader`, a class used for reading files into dictionaries\n\n### verify\n\nPass this function a list of files (instances of the `File` namedtuple) and a\npath and it will verify that those files exist in that path. It is intended to\nbe used to verify that a certain program resides in the given path, e.g. by\nchecking that the program's main executable is in the expected place.\n\n```python\nidentifying_files = [\n File(\"ARCHERY.EXE\", 31616, \"d8fae202edcc48d51a72026cbfbe7fa8\"),\n]\npath = \"path/to/archery\"\nverify(identifying_files, path)\n```\n\nThe call to `verify` above will return `True` iff a file\n`path/to/archery/ARCHERY.EXE` exists, is `31,616` bytes, and has md5 hash\n`d8fae202edcc48d51a72026cbfbe7fa8`. If `identifying_files` contains multiple\n`File` namedtuples, *all* of the files described in the list must be present.\n\n### File\n\n`File` is simply a namedtuple representing a file. The fields of the namedtuple\nare `path`, `size`, and `md5`.\n\nTo create a new `File`:\n\n```python\nexample = File(\"path/to/example.tle\", 12345, \"0123456789abcdef0123456789abcdef\")\n```\n\nThe path should be given relative to some base path (e.g. the main path to the\nprogram to be identified by that file) which will be passed to `verify`\nseparately.\n\n`size` is the file size in bytes.\n\n`md5` is the md5 hash of the file.\n\n### FileReader\n\nFileReader is a factory that produces readers for specific file formats. A\nreader provides two methods, `pack` and `unpack`, used for parsing and\nunparsing data from files. Under the hood, it uses the `struct` module.\n\nConstruct a file reader with `FileReader(format)`, where `format` is a\nstring describing the file format, such as:\n\n```python\nscore_reader = FileReader(\"\"\"\n<\nUint32 score # Score at index 0x00, before name\nstring[16] name\noptions[6] game_options # A six byte field with a custom data format\n\"\"\")\n```\n\nThe format of each line is\n\n TYPE VARIABLE_NAME\n\nor\n\n TYPE[COUNT] VARIABLE_NAME\n\nIf COUNT is not specified, it defaults to 1.\n\nOptionally, a line may contain a single character describing the\nendianness of the numbers in the file, in the style of struct. By\ndefault, little-endian ('<') integers are assumed.\n\nCharacters following a pound sign ('#') are treated as comments and\nignored.\n\nIf TYPE is one of the builtin types supported by the struct module (e.g.\nUint16), it will be processed by struct. For builtin types, COUNT is\ntreated as the repeat count for struct: Uint32[4] means four 32-bit\nunsigned integers (16 bytes), and string[4] means a 4 byte string.\n\nOtherwise, TYPE is treated as a user-defined type. Then COUNT is the\nnumber of bytes occupied by the variable, and the FileReader will look\nfor a function named parse_TYPE (e.g. parse_options) when unpacking the\ndata. If found, the function will be called with the bytestring as an\nargument and the return value assigned as the value of the variable.\nSimilarly, the FileReader will pass the variable to a function named\nunparse_TYPE (e.g. unparse_options) which should return a bytestring of\nlength COUNT when packing the data. If those functions are not defined,\nthe bytes will be returned as-is.\n\nThe `Struct` used by this module can be accessed directly as\n`score_reader.struct`, if desired.\n\nThe reader specified above will extract three variables from a 26-byte\nfile: `score`, a (little-endian) 32-bit unsigned integer; `name`, a\n16-byte string; and `game_options`, a 6-byte field in a custom format.\n\nGiven a file in the required format, the file can be parsed with:\n\n```python\ndata = scorefile.read(26)\nscores = score_reader.unpack(data)\n```\n\nwhich will produce `scores`, a dictionary with three entries\n\n```python\nscores = {\"name\" : \"SomeName\", \"score\" : 1234, \"game_options\" : b'......'}\n```\n\nGiven a dictionary with these entries, `pack` can be used to generate a\nscorefile in the original format.\n\n```python\ndata = score_reader.pack( {\"name\" : \"Cheater\",\n \"score\" : 9999,\n \"game_options\" : b'......'} )\nscorefile.write(data)\n```\n\nSince we didn't define `parse_options` and `unparse_options` functions,\nthe six bytes devoted to that variable are simply assigned directly. It\nmight be more useful to parse the options, however:\n\n```python\ndef parse_options(b):\n return { 'option' + str(i) : b[i] for i in range(6) }\n\ndef unparse_options(o):\n return bytes([o['option' + str(i)] for i in range(6)])\n```\n\n#### FileReader.from_offsets\n\nIf you know the offsets of data in a file, but not necessarily the format of the\nwhole file, the `from_offsets` constructor may be more useful.\n\nConstruct a file reader with `from_offsets(format_def)`, where `format_def` is a\nstring describing the file format, such as:\n\n```python\nFileReader.from_offsets('''\n<\n0x00 Uint32 score # Score at index 0x00, before name\n0x04 string[16] name\n0x14 options[6] options # A six byte field with a custom data format\n0x1a EOF\n''')\n```\n\nThe format of each line is\n\n OFFSET TYPE VARIABLE_NAME\n\nor\n\n OFFSET TYPE[COUNT] VARIABLE_NAME\n\nThe final line of format_def may be:\n\n FILE_LENGTH EOF\n\nOFFSET and FILE_LENGTH must be specified in hexadecimal. The number must\nbegin with '0x' and may use either capital or lowercase, i.e. 0x1a and\n0x1A are equivalent.\n\nIt is not required to specify offsets in any particular order.\n\nOptionally, a line may contain a single character describing the\nendianness of the numbers in the file, in the style of struct. By\ndefault, little-endian ('<') integers are assumed.\n\nFor an explanation of the remaining segment of each line, see the\ndocumentation for `FileReader`.\n\nThis function is useful if a file format contains unknown segments,\nbecause `from_offsets` will automatically fill in the unknown segments\nwith dummy variables. So:\n\n```python\nFileReader.from_offsets('''\n<\n0x00 Uint32 score # Score at index 0x00, before name\n0x04 string[16] name\n0x24 options[6] options # A six byte field with a custom data format\n0x50 EOF\n''')\n```\n\nis equivalent to:\n\n```python\nFileReader('''\n<\nUint32 score # 0x00-0x03: Score at index 0x00, before name\nstring[16] name # 0x04-0x13\nunknown[16] unk1 # 0x14-0x23\noptions[6] options # 0x24-0x29: A six byte field with a custom data format\nunknown[38] unk2 # 0x2a-0x4f\n''')\n```\n\nThe EOF statement is not required, but if not specified, the variable\nwith the highest offset specified will also be presumed to be the end of\nthe file.\n\n## Example usage\n\ncgrr.py is used by other modules in the CGRR project. For example:\n\n* [cgrr-gameboy](https://github.com/sopoforic/cgrr-gameboy), which reads\n and edits Game Boy ROM headers\n* [cgrr-gamecube](https://github.com/sopoforic/cgrr-gamecube), which reads\n and edits GameCube GCI files\n* [cgrr-mariospicross](https://github.com/sopoforic/cgrr-mariospicross),\n which reads and edits puzzles for the Game Boy game Mario's Picross\n* [cgrr-pokemon](https://github.com/sopoforic/cgrr-pokemon), which reads\n and edits save files for Pokemon games\n\n## License\n\nCGRR is available under the GPL v3 or later. See the file COPYING for details.\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/sopoforic/cgrr", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "cgrr", "package_url": "https://pypi.org/project/cgrr/", "platform": "", "project_url": "https://pypi.org/project/cgrr/", "project_urls": { "Homepage": "https://github.com/sopoforic/cgrr" }, "release_url": "https://pypi.org/project/cgrr/1.3.0/", "requires_dist": [ "ply" ], "requires_python": "", "summary": "Classic Game Resource Reader simplifies parsing game resource files", "version": "1.3.0" }, "last_serial": 4102618, "releases": { "1.2.1": [ { "comment_text": "", "digests": { "md5": "87ba621ecc4edbf42abeadfdafb3ce3f", "sha256": "72c11e9336648f5f8b2017199f72412a30e67d8a8b4bda76ed670fc2c9d5ac60" }, "downloads": -1, "filename": "cgrr-1.2.1-py3-none-any.whl", "has_sig": false, "md5_digest": "87ba621ecc4edbf42abeadfdafb3ce3f", "packagetype": "bdist_wheel", "python_version": "3.7", "requires_python": null, "size": 7716, "upload_time": "2018-07-25T20:01:28", "url": "https://files.pythonhosted.org/packages/d4/21/a12d911618575173b5e1860733586d31a03cbe7373bd243d23abce87dcc1/cgrr-1.2.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a3a814c626743fe25a3e94cf0792034b", "sha256": "fe216a7ecf5cf32745f33368fd154c938e72bde92c842779842136ac3ed0346c" }, "downloads": -1, "filename": "cgrr-1.2.1.tar.gz", "has_sig": false, "md5_digest": "a3a814c626743fe25a3e94cf0792034b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18845, "upload_time": "2018-07-25T20:01:25", "url": "https://files.pythonhosted.org/packages/7a/79/94a03a056bdbc150f5ec14ba82a8776f18bf4718587ebef8531ee67438b9/cgrr-1.2.1.tar.gz" } ], "1.3.0": [ { "comment_text": "", "digests": { "md5": "3c18eef9a81dce316429f284bbeca934", "sha256": "10e35bc2e4f92278097976653e4f878799136cf81a4f8f1aa708c5f273212126" }, "downloads": -1, "filename": "cgrr-1.3.0-py3-none-any.whl", "has_sig": false, "md5_digest": "3c18eef9a81dce316429f284bbeca934", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 10526, "upload_time": "2018-07-26T00:47:03", "url": "https://files.pythonhosted.org/packages/c8/e6/16fa73e91a0d5b2d11c4a735d5addbf845a0b07ea267ca7bb7868edf5fdc/cgrr-1.3.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "99e9aa5519abbe6c35e5c8e9acf6d89c", "sha256": "f50670cee22c09081029764643288786f86ae22fbd9362476cc5821f759d2689" }, "downloads": -1, "filename": "cgrr-1.3.0.tar.gz", "has_sig": false, "md5_digest": "99e9aa5519abbe6c35e5c8e9acf6d89c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21469, "upload_time": "2018-07-26T00:47:08", "url": "https://files.pythonhosted.org/packages/43/25/f1a38097816e09435e234279f3f44b4e9040db9379de1411ea4142f886d1/cgrr-1.3.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "3c18eef9a81dce316429f284bbeca934", "sha256": "10e35bc2e4f92278097976653e4f878799136cf81a4f8f1aa708c5f273212126" }, "downloads": -1, "filename": "cgrr-1.3.0-py3-none-any.whl", "has_sig": false, "md5_digest": "3c18eef9a81dce316429f284bbeca934", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 10526, "upload_time": "2018-07-26T00:47:03", "url": "https://files.pythonhosted.org/packages/c8/e6/16fa73e91a0d5b2d11c4a735d5addbf845a0b07ea267ca7bb7868edf5fdc/cgrr-1.3.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "99e9aa5519abbe6c35e5c8e9acf6d89c", "sha256": "f50670cee22c09081029764643288786f86ae22fbd9362476cc5821f759d2689" }, "downloads": -1, "filename": "cgrr-1.3.0.tar.gz", "has_sig": false, "md5_digest": "99e9aa5519abbe6c35e5c8e9acf6d89c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21469, "upload_time": "2018-07-26T00:47:08", "url": "https://files.pythonhosted.org/packages/43/25/f1a38097816e09435e234279f3f44b4e9040db9379de1411ea4142f886d1/cgrr-1.3.0.tar.gz" } ] }