├── .flake8 ├── .gitattributes ├── .github ├── pull_request_template.md └── workflows │ ├── docker-push.yml │ └── pr.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── PyPoE ├── __init__.py ├── _data │ ├── custom_descriptions.txt │ └── hardcoded_descriptions.txt ├── cli │ ├── __init__.py │ ├── config.py │ ├── core.py │ ├── exporter │ │ ├── __init__.py │ │ ├── core.py │ │ ├── dat │ │ │ ├── __init__.py │ │ │ ├── handler.py │ │ │ └── parsers │ │ │ │ ├── __init__.py │ │ │ │ └── json.py │ │ ├── poe2wiki │ │ │ ├── __init__.py │ │ │ ├── admin │ │ │ │ ├── __init__.py │ │ │ │ └── unique.py │ │ │ ├── core.py │ │ │ ├── handler.py │ │ │ ├── parser.py │ │ │ └── parsers │ │ │ │ ├── __init__.py │ │ │ │ ├── area.py │ │ │ │ ├── incursion.py │ │ │ │ ├── item.py │ │ │ │ ├── itemconstants.py │ │ │ │ ├── lua.py │ │ │ │ ├── masteries.py │ │ │ │ ├── mods.py │ │ │ │ ├── monster.py │ │ │ │ ├── passives.py │ │ │ │ └── skill.py │ │ ├── util.py │ │ └── wiki │ │ │ ├── __init__.py │ │ │ ├── admin │ │ │ ├── __init__.py │ │ │ └── unique.py │ │ │ ├── core.py │ │ │ ├── handler.py │ │ │ ├── parser.py │ │ │ └── parsers │ │ │ ├── __init__.py │ │ │ ├── area.py │ │ │ ├── incursion.py │ │ │ ├── item.py │ │ │ ├── itemconstants.py │ │ │ ├── lua.py │ │ │ ├── masteries.py │ │ │ ├── mods.py │ │ │ ├── monster.py │ │ │ ├── passives.py │ │ │ └── skill.py │ └── handler.py ├── poe │ ├── __init__.py │ ├── constants.py │ ├── file │ │ ├── __init__.py │ │ ├── bundle.py │ │ ├── dat.py │ │ ├── dgr.py │ │ ├── file_set.py │ │ ├── file_system.py │ │ ├── ggpk.py │ │ ├── idl.py │ │ ├── idt.py │ │ ├── it.py │ │ ├── ot.py │ │ ├── psg.py │ │ ├── shared │ │ │ ├── __init__.py │ │ │ ├── cache.py │ │ │ └── keyvalues.py │ │ ├── specification │ │ │ ├── __init__.py │ │ │ ├── data │ │ │ │ ├── __init__.py │ │ │ │ ├── alpha.py │ │ │ │ ├── beta.py │ │ │ │ ├── generated.py │ │ │ │ ├── poe2.py │ │ │ │ └── stable.py │ │ │ ├── errors.py │ │ │ ├── fields.py │ │ │ └── generation │ │ │ │ ├── __init__.py │ │ │ │ ├── column_naming.py │ │ │ │ ├── custom_attributes.py │ │ │ │ ├── import_dat_schema.py │ │ │ │ ├── template.py │ │ │ │ └── virtual_fields.py │ │ ├── stat_filters.py │ │ ├── translations.py │ │ └── tsi.py │ ├── patchserver.py │ ├── path.py │ ├── sim │ │ ├── __init__.py │ │ ├── formula.py │ │ ├── item.py │ │ ├── mods.py │ │ └── monster.py │ └── text.py ├── shared │ ├── __init__.py │ ├── config │ │ ├── __init__.py │ │ └── validator.py │ ├── containers.py │ ├── decorators.py │ ├── mixins.py │ └── murmur2.py └── ui │ ├── __init__.py │ ├── ggpk_viewer │ ├── __init__.py │ ├── core.py │ ├── menu.py │ └── toolbar.py │ ├── launchpad │ └── __init__.py │ └── shared │ ├── __init__.py │ ├── dialog.py │ ├── file │ ├── __init__.py │ ├── ggpk.py │ ├── handler.py │ ├── manager.py │ └── model.py │ ├── proxy_filter_model.py │ ├── regex_widgets.py │ ├── settings.py │ └── table_context_menus.py ├── README.md ├── docs ├── Makefile ├── generate_rst_templates.py ├── make.bat └── source │ ├── API │ ├── overview.rst │ └── structure.rst │ ├── CLI │ ├── config.rst │ ├── dat.rst │ ├── overview.rst │ ├── setup.rst │ └── wiki.rst │ ├── GUI │ ├── ggpk_viewer.rst │ └── overview.rst │ ├── _templates │ ├── autosummary │ │ └── module.rst │ ├── layout.html │ └── module.html │ ├── conf.py │ ├── contribution.rst │ ├── index.rst │ ├── installation.rst │ └── report_issue.rst ├── export.bash ├── poetry.lock ├── pyproject.toml ├── scripts ├── convert_conf_to_py.py └── make_empty_spec.py └── tests ├── PyPoE ├── cli │ └── exporter │ │ └── wiki │ │ ├── parser │ │ └── test_wiki_item_parser.py │ │ └── test_parser.py ├── poe │ ├── file │ │ ├── _data │ │ │ ├── Metadata │ │ │ │ └── StatDescriptions │ │ │ │ │ ├── descriptions_base.txt │ │ │ │ │ └── descriptions_extended.txt │ │ │ ├── keyvalues.kv │ │ │ ├── keyvalues_base.kv │ │ │ ├── keyvalues_write.kv │ │ │ ├── specifications │ │ │ │ ├── dat_testspec.py │ │ │ │ ├── invalid_argument_combination.py │ │ │ │ ├── invalid_enum_name.py │ │ │ │ ├── invalid_foreign_key_file.py │ │ │ │ ├── invalid_foreign_key_id.py │ │ │ │ ├── rr_test.py │ │ │ │ ├── runtime_missing_foreign_key1.py │ │ │ │ ├── runtime_missing_foreign_key2.py │ │ │ │ ├── runtime_rowsize_mismatch.py │ │ │ │ ├── virtual_key_duplicate.py │ │ │ │ ├── virtual_key_empty.py │ │ │ │ ├── virtual_key_invalid_data_type.py │ │ │ │ └── virtual_key_invalid_key.py │ │ │ ├── test.idl │ │ │ ├── test.idt │ │ │ └── test_write.idl │ │ ├── shared │ │ │ └── test_keyvalues.py │ │ ├── test_dat.py │ │ ├── test_ggpk.py │ │ ├── test_idl.py │ │ ├── test_idt.py │ │ ├── test_ot.py │ │ ├── test_psg.py │ │ ├── test_stat_filters.py │ │ └── test_translations.py │ ├── sim │ │ ├── test_formula.py │ │ ├── test_item.py │ │ └── test_mods.py │ ├── test_patchserver.py │ ├── test_path.py │ └── test_text.py └── shared │ ├── config │ └── test_validator.py │ ├── test_decorators.py │ ├── test_mixins.py │ └── test_murmur2.py ├── conftest.py └── test_data.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | extend-ignore = E203 3 | exclude = 4 | .venv/ 5 | max-line-length=120 6 | max-cognitive-complexity=10 7 | docstring-convention = google 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.py ident 2 | *.ini ident 3 | *.txt ident -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ``` 2 | DELETE THIS TEXT BOX BEFORE SUBMITTING THE PR. 3 | 4 | When filling in the template, please delete the "filler" paragraph below the `# Header` 5 | portion and replace it with what the paragraph is asking from you. 6 | Keep these PR's nice and tidy and spend the time to write it out 7 | properly. We will reject PR's that do not follow or fill in this template properly. 8 | ``` 9 | 10 | # Abstract 11 | 12 | A quick one/two sentence "shot" of what was done in this PR so people can understand at a glance. 13 | 14 | # Action Taken 15 | 16 | Please describe what did you do in this pull request in detail. Try to provide as much detail as possible, so those who review/read this PR 17 | can understand what was changed, added or removed from the repo. Feel free to provide pictures, attachments, code examples, etc. 18 | 19 | # Caveats 20 | 21 | List any issues or gotcha's for this PR or relating to your change in any way, so that it provides transparency toward what could be the 22 | pitfalls of your methods, or perhaps what linked issues/bugs you've uncovered whilst making your changes. Anything that affects functionality 23 | in any way that you feel relevant to mention (especially for other people) should go here. 24 | 25 | # FAO 26 | 27 | Please use this block to `@` people you think would benefit from viewing this PR, even if you later decide to add them as reviewers. 28 | This can also be used to "ping" people you may not want a review from, but want to make aware of your changes in a general sense. 29 | -------------------------------------------------------------------------------- /.github/workflows/docker-push.yml: -------------------------------------------------------------------------------- 1 | name: docker push 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | REGISTRY: ghcr.io 8 | IMAGE_NAME: ${{ github.repository }} 9 | 10 | jobs: 11 | build-and-push-image: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: read 15 | packages: write 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: docker/login-action@v3 19 | with: 20 | registry: ${{ env.REGISTRY }} 21 | username: ${{ github.actor }} 22 | password: ${{ secrets.GITHUB_TOKEN }} 23 | - id: meta 24 | uses: docker/metadata-action@v5 25 | with: 26 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 27 | - uses: docker/build-push-action@v5 28 | with: 29 | context: . 30 | push: true 31 | tags: ${{ steps.meta.outputs.tags }} 32 | labels: ${{ steps.meta.outputs.labels }} 33 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: PR 2 | on: 3 | push: 4 | branches-ignore: 5 | - 'dev' 6 | pull_request: 7 | branches: 8 | - '**' 9 | 10 | jobs: 11 | ci: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Install Python 16 | uses: actions/setup-python@v4 17 | with: 18 | python-version: "3.11" 19 | - name: Install poetry 20 | uses: abatilo/actions-poetry@v2 21 | - name: Setup a local virtual environment (if no poetry.toml file) 22 | run: | 23 | poetry config virtualenvs.create true --local 24 | poetry config virtualenvs.in-project true --local 25 | - uses: actions/cache@v3 26 | name: Define a cache for the virtual environment based on the dependencies lock file 27 | with: 28 | path: ./.venv 29 | key: venv-${{ hashFiles('poetry.lock') }} 30 | - name: Install the project dependencies 31 | run: poetry install 32 | - uses: actions/cache@v3 33 | with: 34 | path: ~/.cache/pre-commit 35 | key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }} 36 | - name: Run commit checks 37 | run: poetry run pre-commit run --all-files 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .git 2 | .idea 3 | .svn 4 | .pyc 5 | .cache 6 | __pycache__ 7 | /PyPoE.egg-info 8 | /build 9 | /docs/build 10 | /docs/source/_autosummary 11 | /docs/source/autosummary.rst 12 | .vscode 13 | .venv -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_language_version: 2 | python: python3 3 | default_stages: 4 | - commit 5 | - push 6 | repos: 7 | # Pre-commit and plugins 8 | - repo: https://github.com/pre-commit/pre-commit-hooks 9 | rev: v4.4.0 10 | hooks: 11 | - id: no-commit-to-branch 12 | name: Don't commit to master 13 | # Using this mirror lets us use mypyc-compiled black, which is about 2x faster 14 | - repo: https://github.com/psf/black-pre-commit-mirror 15 | rev: 24.10.0 16 | hooks: 17 | - id: black 18 | - repo: https://github.com/pycqa/isort 19 | rev: 5.13.2 20 | hooks: 21 | - id: isort 22 | args: ["--profile", "black", "--filter-files"] 23 | name: Sort imports with isort 24 | # Linter 25 | - repo: https://github.com/pycqa/flake8 26 | rev: 7.1.1 27 | hooks: 28 | - id: flake8 29 | exclude: ^PyPoE/ui/ 30 | entry: flake8 31 | name: "Linting using Flake8" 32 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## What's this? 2 | 3 | This is a working guide on how to contribute to this repository. It is here so that you could confidently help us with developing PyPoE and make patches/pull requests. 4 | If you have any questions - please message on the [#tools-dev](https://discord.gg/juZ2dUZY) channel in our discord server. 5 | 6 | ## How does the branching model work? 7 | 8 | !!!IMPORTANT!!! -- DO NOT forget to perform a `git pull` before branching to ensure you have the latest code! 9 | 10 | Simply put, you branch off from `dev` by using branch names like: 11 | 12 | - `feature/my-branch-name` 13 | - `bugfix/my-bugfix-name` 14 | - `patch/my-patch-name` 15 | 16 | And then raise a PR ensuring that your target branch for the merge is our fork's `dev` branch. 17 | 18 | ## Merges, Reviews, Etc.. 19 | 20 | Once your PR is up, assign a reviewer (ask us on discord and post your PR link). Once it is approved, we will merge the work into the `dev` branch. Once work has completed for a given PoE game version, it will be tagged as a release. 21 | 22 | ## Releases 23 | 24 | When we are working on certain patches, while we are not ready to say "this release is fully finished" we can still tag key milestones for them using git tags. In this case, you should tag your pre-release following this convention: 25 | 26 | `major.minor.patch leaguename patch` 27 | 28 | for instance 29 | 30 | `3.15.4 Expedition Patch` 31 | 32 | Whereas when you are ready to release, you should instead use this convention for tagging: 33 | 34 | `major.minor leaguename release` 35 | 36 | such as: 37 | 38 | `3.15 Expedition Release` 39 | 40 | In order to tell whether you should release something, there needs to be a discussing with fellow developers in the [#tools-dev](https://discord.gg/juZ2dUZY) channel of our discord. 41 | 42 | ## Contribution and PRs 43 | 44 | ### Commits 45 | When making commits to your personal fork that you intend to push to our branches, we ask that you keep your commit messages clean and informative. Here are some examples of bad commit messages for a piece of code: 46 | 47 | `code:` 48 | ```py 49 | def my_excellent_method(arg:int): 50 | return arg + 1 51 | 52 | # >>>>>> 53 | 54 | def addone(arg:int) -> int: 55 | """Adds one to the passed in integer argument.""" 56 | return arg + 1 57 | ``` 58 | 59 | `bad commit examples:` 60 | - `...` 61 | - `commit` 62 | - `Changed method` 63 | - `Improved function` 64 | 65 | `good commit examples:` 66 | - `Renamed my_excellent_method and added docstring explaining what it does` 67 | - `Refactored my_excellent_method, added docstring, and return type` 68 | 69 | Keep them sweet, but informative and meaningful. 70 | 71 | ### Pull requests 72 | 73 | When creating PRs, you should be given a PR template when creating a PR on git, please follow it and fill it in appropriately, giving as much information as possible based on the size of your commits and quantity of code changed. 74 | 75 | ### Caution 76 | 77 | We want to bring this repo to a good state and as such the quality of work both within the codebase and within git are paramount and as such we will be rejecting any commits/PRs if they do not meet our quality standards. We may also ask you to change them before rejecting, so you have a chance to have them accepted once changes are made to comply with our quality policy. 78 | 79 | ## How to pull/push? 80 | 81 | If you want to checkout a particular branch you should first fetch from the remote, by using `git fetch` after you've cloned the repo. Then you can simply checkout using `git checkout dev` or `git checkout patches` depending on which one of those you want as a base for your work. 82 | 83 | Let's say you want to submit some fixes for the general code, then you should do: 84 | 85 | ```sh 86 | git checkout dev # check out the remote branch 87 | git pull origin dev # pull latest changes from remote 88 | git checkout -b "branchname" # make a new branch from this one with a name you chose. 89 | ``` 90 | 91 | Once you are ready to push your changes, do the following: 92 | 93 | ```sh 94 | git add file1 file2 file3 # Add the files you changed 95 | git commit -m "my nice commit msg" # make a commit 96 | git push origin dev # push your changes to our remote as a pull req 97 | ``` 98 | 99 | After that you should be given a URL inside of your CLI which you can follow to finalise the pull request on github. 100 | 101 | It is imperative that you set your target branch correctly in git, so make sure that your PR says: 102 | 103 | Merge changes into `wiki/pypoe/dev` from `you/somebranch` for instance. 104 | If something isn't clear, please message us on discord and we will be happy to assist! -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:slim AS base 2 | 3 | ENV LANG C.UTF-8 4 | ENV LC_ALL C.UTF-8 5 | ENV PYTHONDONTWRITEBYTECODE 1 6 | ENV PYTHONFAULTHANDLER 1 7 | ENV PYTHONUNBUFFERED 1 8 | 9 | FROM base as builder 10 | 11 | WORKDIR /builder 12 | 13 | COPY pyproject.toml poetry.lock ./ 14 | 15 | RUN pip install --no-cache-dir poetry &&\ 16 | python -m venv /venv &&\ 17 | poetry export -f requirements.txt |\ 18 | /venv/bin/pip install --no-cache-dir -r /dev/stdin 19 | 20 | COPY . . 21 | RUN poetry build && /venv/bin/pip install --no-cache-dir dist/*.whl 22 | 23 | FROM base as final 24 | 25 | ENV PATH "/venv/bin:${PATH}" 26 | ENV VIRTUAL_ENV "/venv" 27 | 28 | COPY --from=builder /venv /venv 29 | 30 | WORKDIR /pypoe 31 | 32 | COPY export.bash ./ 33 | RUN mkdir ./out ./temp &&\ 34 | pypoe_exporter config set out_dir ./out &&\ 35 | pypoe_exporter config set temp_dir ./temp &&\ 36 | pypoe_exporter setup perform 37 | -------------------------------------------------------------------------------- /PyPoE/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Library init 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 2d0ffae867b4d85370ac8867c1ba336444c289a8 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Library Init 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | import os 33 | 34 | # Python 35 | import platform 36 | import warnings 37 | 38 | # ============================================================================= 39 | # Globals 40 | # ============================================================================= 41 | 42 | __all__ = [ 43 | "APP_DIR", 44 | "DIR", 45 | "DATA_DIR", 46 | ] 47 | __version__ = "1.0.0a0" 48 | 49 | # ============================================================================= 50 | # Functions 51 | # ============================================================================= 52 | 53 | 54 | def _get_app_dir(): 55 | osys = platform.system() 56 | if osys == "Windows": 57 | vars = ["APPDATA"] 58 | subdir = "PyPoE" 59 | elif osys in ["Linux", "Darwin"]: 60 | vars = ["HOME", "PWD"] 61 | subdir = ".PyPoE" 62 | else: 63 | raise RuntimeError("Unsupported Operating System") 64 | 65 | dir = None 66 | for var in vars: 67 | if var not in os.environ: 68 | continue 69 | dir = os.environ[var] 70 | if not os.path.exists(dir): 71 | continue 72 | break 73 | 74 | if dir is None: 75 | raise RuntimeError("Home/user directory not found") 76 | 77 | dir = os.path.join(dir, subdir) 78 | if not os.path.exists(dir): 79 | os.mkdir(dir) 80 | 81 | return dir 82 | 83 | 84 | # ============================================================================= 85 | # Init 86 | # ============================================================================= 87 | 88 | warnings.simplefilter("default", DeprecationWarning) 89 | APP_DIR = _get_app_dir() 90 | DIR = os.path.join(os.path.dirname(__file__)) 91 | DATA_DIR = os.path.join(DIR, "_data") 92 | -------------------------------------------------------------------------------- /PyPoE/_data/custom_descriptions.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/PyPoE/_data/custom_descriptions.txt -------------------------------------------------------------------------------- /PyPoE/_data/hardcoded_descriptions.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/PyPoE/_data/hardcoded_descriptions.txt -------------------------------------------------------------------------------- /PyPoE/cli/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | CLI Package Init 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: c35ec0457b6736d7b2b1fa6d3d4d1f359b868646 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | CLI Package init 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | GGPK User Interface Classes 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 8f01ef5b85bde738cda4457bcd70c0d9666558cd $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Creates a qt User Interface to browse GGPK files. 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | import os 34 | import warnings 35 | 36 | # 3rd-Party 37 | from colorama import init 38 | 39 | # self 40 | from PyPoE import APP_DIR 41 | from PyPoE.cli.config import ConfigHelper 42 | from PyPoE.cli.core import OutputHook 43 | 44 | # ============================================================================= 45 | # Globals 46 | # ============================================================================= 47 | 48 | __all__ = ["CONFIG_PATH", "config"] 49 | 50 | CONFIG_PATH = os.path.join(APP_DIR, "exporter.conf") 51 | 52 | config = ConfigHelper(infile=CONFIG_PATH) 53 | 54 | # ============================================================================= 55 | # Init 56 | # ============================================================================= 57 | 58 | init() 59 | OutputHook(warnings.showwarning) 60 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/core.py: -------------------------------------------------------------------------------- 1 | """ 2 | Exporter Core 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/core.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: ee54b9135a9fb333d266d8a663b6e5c49fd4b7b0 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Exporter main function(s) and entry point. 21 | 22 | The following calls are equivalent: 23 | 24 | .. code-block:: none 25 | 26 | pypoe_exporter 27 | python PyPoE/cli/exporter/core.py 28 | 29 | 30 | Agreement 31 | =============================================================================== 32 | 33 | See PyPoE/LICENSE 34 | 35 | Documentation 36 | =============================================================================== 37 | 38 | .. autofunction:: main 39 | """ 40 | 41 | # ============================================================================= 42 | # Imports 43 | # ============================================================================= 44 | 45 | # Python 46 | import argparse 47 | 48 | from PyPoE.cli.core import run 49 | from PyPoE.cli.exporter import config 50 | from PyPoE.cli.exporter.dat import DatHandler 51 | from PyPoE.cli.exporter.poe2wiki.core import WikiHandler as WikiHandler2 52 | from PyPoE.cli.exporter.wiki.core import WikiHandler 53 | from PyPoE.cli.handler import ConfigHandler, SetupHandler 54 | from PyPoE.poe.constants import DISTRIBUTOR, VERSION 55 | 56 | # self 57 | from PyPoE.shared.config.validator import IntEnumValidator 58 | 59 | # 3rd party 60 | 61 | 62 | # ============================================================================= 63 | # Classes 64 | # ============================================================================= 65 | 66 | 67 | # ============================================================================= 68 | # Functions 69 | # ============================================================================= 70 | 71 | 72 | def setup_config(): 73 | config.validator.functions.update( 74 | { 75 | "is_version": IntEnumValidator( 76 | enum=VERSION, 77 | ), 78 | "is_distributor": IntEnumValidator( 79 | enum=DISTRIBUTOR, 80 | ), 81 | } 82 | ) 83 | 84 | config.add_option("version", "is_version(default=%s)" % VERSION.DEFAULT.value) 85 | config.add_option("distributor", "is_distributor(default=%s)" % DISTRIBUTOR.DEFAULT.value) 86 | config.add_option( 87 | "ggpk_path", 'is_directory(default="", exists=True, allow_empty=True, allow_http=True)' 88 | ) 89 | config.add_option( 90 | "language", 91 | 'option("English", "French", "German", "Portuguese",' 92 | '"Russian", "Spanish", "Thai", "Simplified Chinese",' 93 | '"Traditional Chinese", "Korean", default="English")', 94 | ) 95 | 96 | 97 | def main(): 98 | """ 99 | Entry point for the CLI PyPoE exporter 100 | """ 101 | # Setup 102 | main_parser = argparse.ArgumentParser() 103 | main_sub = main_parser.add_subparsers() 104 | 105 | setup_config() 106 | 107 | DatHandler(main_sub) 108 | WikiHandler(main_sub) 109 | WikiHandler2(main_sub) 110 | # In that order.. 111 | SetupHandler(main_sub, config) 112 | ConfigHandler(main_sub, config) 113 | 114 | main_parser.add_argument("--quiet", action="store_true") 115 | main_parser.add_argument("--mem", type=int, help="Memory limit in bytes (linux only)") 116 | 117 | # Execute 118 | run(main_parser, config) 119 | 120 | 121 | if __name__ == "__main__": 122 | main() 123 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/dat/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | .dat Exporter 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/dat/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: d6ea344c058d24641ee64997a9db30216f8d8ffc $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | .dat Exporter 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # 3rd-party 35 | 36 | # self 37 | from PyPoE.cli.exporter.dat.parsers.json import JSONExportHandler 38 | 39 | # ============================================================================= 40 | # Globals 41 | # ============================================================================= 42 | 43 | __all__ = ["DatHandler"] 44 | 45 | # ============================================================================= 46 | # Classes 47 | # ============================================================================= 48 | 49 | 50 | class DatHandler: 51 | """ 52 | 53 | :type sql: argparse.ArgumentParser 54 | """ 55 | 56 | def __init__(self, sub_parser): 57 | """ 58 | 59 | :type sub_parser: argparse._SubParsersAction 60 | """ 61 | parser = sub_parser.add_parser( 62 | "dat", 63 | help=".dat export", 64 | ) 65 | parser.set_defaults(func=lambda args: parser.print_help()) 66 | 67 | sub = parser.add_subparsers(help="Export type") 68 | JSONExportHandler(sub) 69 | 70 | 71 | # ============================================================================= 72 | # Functions 73 | # ============================================================================= 74 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/dat/parsers/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/dat/parsers/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 3e83b8dfea8344df6b504b7ea162a74088c6e95e $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # 3rd-party 35 | 36 | # self 37 | 38 | # ============================================================================= 39 | # Globals 40 | # ============================================================================= 41 | 42 | __all__ = [] 43 | 44 | # ============================================================================= 45 | # Classes 46 | # ============================================================================= 47 | 48 | # ============================================================================= 49 | # Functions 50 | # ============================================================================= 51 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/poe2wiki/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/wiki/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 857f033447afcfa62ffc7c1b0a6e354b6d5e3cc1 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # 3rd-party 35 | 36 | # self 37 | 38 | # ============================================================================= 39 | # Globals 40 | # ============================================================================= 41 | 42 | __all__ = [] 43 | 44 | # ============================================================================= 45 | # Classes 46 | # ============================================================================= 47 | 48 | # ============================================================================= 49 | # Functions 50 | # ============================================================================= 51 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/poe2wiki/admin/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Administrative parser init 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/wiki/admin/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 8c5264614c66236d9c193a8f1051556b8d476e72 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | from PyPoE.cli.exporter.poe2wiki.admin.unique import UniqueCommandHandler 31 | 32 | # ============================================================================= 33 | # Globals 34 | # ============================================================================= 35 | 36 | 37 | ADMIN_HANDLERS = [UniqueCommandHandler] 38 | 39 | __all__ = ["ADMIN_HANDLERS"] 40 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/poe2wiki/core.py: -------------------------------------------------------------------------------- 1 | """ 2 | Core Wiki Exporter 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/wiki/core.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 789b027c7cb824363bda2fe3f4d6e362b2b56924 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Core Wiki Exporter 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # self 35 | from PyPoE.cli.exporter.poe2wiki.admin import ADMIN_HANDLERS 36 | from PyPoE.cli.exporter.poe2wiki.parsers import WIKI_HANDLERS 37 | from PyPoE.cli.handler import BaseHandler 38 | 39 | # ============================================================================= 40 | # Globals 41 | # ============================================================================= 42 | 43 | __all__ = ["WikiHandler"] 44 | 45 | # ============================================================================= 46 | # Classes 47 | # ============================================================================= 48 | 49 | 50 | class WikiHandler(BaseHandler): 51 | def __init__(self, sub_parser): 52 | # TODO Config Options 53 | 54 | # Parser 55 | self.parser = sub_parser.add_parser("poe2wiki", help="PoE2 Wiki Exporter") 56 | self.parser.set_defaults(func=lambda args: self.parser.print_help()) 57 | wiki_sub = self.parser.add_subparsers() 58 | 59 | for handler in WIKI_HANDLERS: 60 | handler(wiki_sub) 61 | 62 | for handler in ADMIN_HANDLERS: 63 | handler(wiki_sub) 64 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/poe2wiki/parsers/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Parser package init 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/wiki/parsers/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 83d943e95c4b13dffb4d60e85b03adc440cfc1fc $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Automatically finds all ExporterHandlers in the package files and import them 21 | into WIKI_HANDLERS. 22 | 23 | Agreement 24 | =============================================================================== 25 | 26 | See PyPoE/LICENSE 27 | """ 28 | 29 | # ============================================================================= 30 | # Imports 31 | # ============================================================================= 32 | 33 | 34 | # Python 35 | import os 36 | from importlib import import_module 37 | 38 | # self 39 | from PyPoE.cli.exporter.poe2wiki.handler import ExporterHandler 40 | 41 | # ============================================================================= 42 | # Globals 43 | # ============================================================================= 44 | 45 | 46 | WIKI_HANDLERS = [] 47 | 48 | __all__ = ["WIKI_HANDLERS"] 49 | 50 | 51 | # ============================================================================= 52 | # Funcs 53 | # ============================================================================= 54 | 55 | 56 | def _load(): 57 | cur_dir = os.path.split(os.path.realpath(__file__))[0] 58 | for file_name in os.listdir(cur_dir): 59 | if file_name.startswith("_"): 60 | continue 61 | file_name = file_name.replace(".py", "") 62 | imp = import_module("." + file_name, __package__) 63 | for obj_name in dir(imp): 64 | if obj_name.startswith("_"): 65 | continue 66 | 67 | if not obj_name.endswith("Handler"): 68 | continue 69 | 70 | obj = getattr(imp, obj_name) 71 | 72 | # Not a class 73 | if not isinstance(obj, type): 74 | continue 75 | 76 | # Only export handlers 77 | if not issubclass(obj, ExporterHandler): 78 | continue 79 | 80 | # Only subclasses of which 81 | if obj is ExporterHandler: 82 | continue 83 | 84 | WIKI_HANDLERS.append(obj) 85 | 86 | 87 | # ============================================================================= 88 | # Init 89 | # ============================================================================= 90 | 91 | 92 | _load() 93 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/poe2wiki/parsers/itemconstants.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | PyPoE/cli/exporter/poe2wiki/parsers/itemconstants.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: f635e35ecb2a581de94384da412dc019b005c38f $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | angelic_knight | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | Global constants the item exporter such as lists of items to exclude from exporting. 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | 25 | Documentation 26 | =============================================================================== 27 | """ 28 | 29 | # ============================================================================= 30 | # Imports 31 | # ============================================================================= 32 | 33 | # Python 34 | 35 | # 3rd-party 36 | 37 | # self 38 | 39 | # ============================================================================= 40 | # Globals 41 | # ============================================================================= 42 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/util.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions for exporters 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/util.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 8c293ea4fca69fbb5ec1d55df3439d0ed12acc9b $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Utility functions for exporters. 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | import json 29 | 30 | # Python 31 | import re 32 | from urllib import request 33 | 34 | from PyPoE.cli.exporter import config 35 | 36 | # self 37 | from PyPoE.poe.path import PoEPath 38 | 39 | # ============================================================================= 40 | # Imports 41 | # ============================================================================= 42 | 43 | 44 | # ============================================================================= 45 | # Globals 46 | # ============================================================================= 47 | 48 | __all__ = [ 49 | "get_content_path", 50 | "fix_path", 51 | ] 52 | 53 | 54 | # ============================================================================= 55 | # Functions 56 | # ============================================================================= 57 | 58 | 59 | def get_content_path(sequel=1): 60 | """ 61 | Returns the path to the current content.ggpk based on the specified 62 | config variables for the version & distributor. 63 | 64 | :return: Path of the content ggpk 65 | :rtype: str 66 | 67 | :raises SetupError: if no valid path was found. 68 | """ 69 | path = config.get_option("ggpk_path") 70 | if path == "": 71 | args = config.get_option("version"), config.get_option("distributor") 72 | paths = PoEPath(*args).get_installation_paths() 73 | 74 | if not paths: 75 | with request.urlopen( 76 | f"https://lvlvllvlvllvlvl.github.io/poecdn-bundle-index/poe{sequel}/urls.json" 77 | ) as cdn_url: 78 | paths = json.loads(cdn_url.read().decode("utf-8"))["urls"] 79 | 80 | return paths[0] 81 | else: 82 | return path 83 | 84 | 85 | def fix_path(path: str) -> str: 86 | path = path.replace('"', "'", 2).replace("\t", "") 87 | if re.search("[a-zA-Z]:.*", path) is not None: 88 | return path[:2] + re.sub(r":", "_", path[2:]) 89 | else: 90 | return path 91 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/wiki/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/wiki/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 857f033447afcfa62ffc7c1b0a6e354b6d5e3cc1 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # 3rd-party 35 | 36 | # self 37 | 38 | # ============================================================================= 39 | # Globals 40 | # ============================================================================= 41 | 42 | __all__ = [] 43 | 44 | # ============================================================================= 45 | # Classes 46 | # ============================================================================= 47 | 48 | # ============================================================================= 49 | # Functions 50 | # ============================================================================= 51 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/wiki/admin/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Administrative parser init 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/wiki/admin/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 44044d9a4c6c22212fed9173bed8e81bc2e62454 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | from PyPoE.cli.exporter.wiki.admin.unique import UniqueCommandHandler 31 | 32 | # ============================================================================= 33 | # Globals 34 | # ============================================================================= 35 | 36 | 37 | ADMIN_HANDLERS = [UniqueCommandHandler] 38 | 39 | __all__ = ["ADMIN_HANDLERS"] 40 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/wiki/core.py: -------------------------------------------------------------------------------- 1 | """ 2 | Core Wiki Exporter 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/wiki/core.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 89a6c1877610a6ca22973cf9f962fd36271c6f6d $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Core Wiki Exporter 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # self 35 | from PyPoE.cli.core import Msg, console 36 | from PyPoE.cli.exporter import config 37 | from PyPoE.cli.exporter.wiki.admin import ADMIN_HANDLERS 38 | from PyPoE.cli.exporter.wiki.parsers import WIKI_HANDLERS 39 | from PyPoE.cli.handler import BaseHandler 40 | 41 | # ============================================================================= 42 | # Globals 43 | # ============================================================================= 44 | 45 | __all__ = ["WikiHandler"] 46 | 47 | # ============================================================================= 48 | # Classes 49 | # ============================================================================= 50 | 51 | 52 | class WikiHandler(BaseHandler): 53 | def __init__(self, sub_parser): 54 | # Config Options 55 | config.add_option("temp_dir", "is_directory(exists=True, make_absolute=True)") 56 | config.add_option("out_dir", "is_directory(exists=True, make_absolute=True)") 57 | config.register_setup("temp_dir", self._setup) 58 | config.add_setup_variable("temp_dir", "hash", 'string(default="")') 59 | config.add_setup_listener("version", self._ver_dist_changed) 60 | config.add_setup_listener("ggpk_path", self._ver_dist_changed) 61 | 62 | # Parser 63 | self.parser = sub_parser.add_parser("wiki", help="Wiki Exporter") 64 | self.parser.set_defaults(func=lambda args: self.parser.print_help()) 65 | wiki_sub = self.parser.add_subparsers() 66 | 67 | for handler in WIKI_HANDLERS: 68 | handler(wiki_sub) 69 | 70 | for handler in ADMIN_HANDLERS: 71 | handler(wiki_sub) 72 | 73 | def _ver_dist_changed(self, key, value, old_value): 74 | if value == old_value: 75 | return 76 | config.set_setup_variable("temp_dir", "performed", False) 77 | console('Setup needs to be performed due to changes to "%s"' % key, msg=Msg.warning) 78 | 79 | def _setup(self, args): 80 | """ 81 | :param args: argparse args passed on 82 | :return: 83 | """ 84 | console("Done.") 85 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/wiki/parsers/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Parser package init 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/cli/exporter/wiki/parsers/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 3103b1f028c69bfab47ef4a2ea3aade78466d15e $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Automatically finds all ExporterHandlers in the package files and import them 21 | into WIKI_HANDLERS. 22 | 23 | Agreement 24 | =============================================================================== 25 | 26 | See PyPoE/LICENSE 27 | """ 28 | 29 | # ============================================================================= 30 | # Imports 31 | # ============================================================================= 32 | 33 | 34 | # Python 35 | import os 36 | from importlib import import_module 37 | 38 | # self 39 | from PyPoE.cli.exporter.wiki.handler import ExporterHandler 40 | 41 | # ============================================================================= 42 | # Globals 43 | # ============================================================================= 44 | 45 | 46 | WIKI_HANDLERS = [] 47 | 48 | __all__ = ["WIKI_HANDLERS"] 49 | 50 | 51 | # ============================================================================= 52 | # Funcs 53 | # ============================================================================= 54 | 55 | 56 | def _load(): 57 | cur_dir = os.path.split(os.path.realpath(__file__))[0] 58 | for file_name in os.listdir(cur_dir): 59 | if file_name.startswith("_"): 60 | continue 61 | file_name = file_name.replace(".py", "") 62 | imp = import_module("." + file_name, __package__) 63 | for obj_name in dir(imp): 64 | if obj_name.startswith("_"): 65 | continue 66 | 67 | if not obj_name.endswith("Handler"): 68 | continue 69 | 70 | obj = getattr(imp, obj_name) 71 | 72 | # Not a class 73 | if not isinstance(obj, type): 74 | continue 75 | 76 | # Only export handlers 77 | if not issubclass(obj, ExporterHandler): 78 | continue 79 | 80 | # Only subclasses of which 81 | if obj is ExporterHandler: 82 | continue 83 | 84 | WIKI_HANDLERS.append(obj) 85 | 86 | 87 | # ============================================================================= 88 | # Init 89 | # ============================================================================= 90 | 91 | 92 | _load() 93 | -------------------------------------------------------------------------------- /PyPoE/cli/exporter/wiki/parsers/itemconstants.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | PyPoE/cli/exporter/wiki/parsers/itemconstants.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: 2ce7b46437be6b7f068451b2c8995504645bda04 $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | angelic_knight | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | Global constants the item exporter such as lists of items to exclude from exporting. 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | 25 | Documentation 26 | =============================================================================== 27 | """ 28 | 29 | # ============================================================================= 30 | # Imports 31 | # ============================================================================= 32 | 33 | # Python 34 | 35 | # 3rd-party 36 | 37 | # self 38 | 39 | # ============================================================================= 40 | # Globals 41 | # ============================================================================= 42 | 43 | 44 | MAPS_IN_SERIES_BUT_NOT_ON_ATLAS = { 45 | "Metadata/Items/Maps/MapWorldsHarbingerUber", 46 | "Metadata/Items/Maps/MapWorldsHarbingerHigh", 47 | "Metadata/Items/Maps/MapWorldsHarbingerMid", 48 | "Metadata/Items/Maps/MapWorldsHarbingerLow", 49 | "Metadata/Items/Maps/MapWorldsPhoenix", 50 | "Metadata/Items/Maps/MapWorldsChimera", 51 | "Metadata/Items/Maps/MapWorldsHydra", 52 | "Metadata/Items/Maps/MapWorldsMinotaur", 53 | "Metadata/Items/Maps/MapWorldsVaalTemple", 54 | } 55 | 56 | MAPS_TO_SKIP_COLORING = { 57 | "Metadata/Items/Maps/MapWorldsHarbingerUber", 58 | "Metadata/Items/Maps/MapWorldsHarbingerHigh", 59 | "Metadata/Items/Maps/MapWorldsHarbingerMid", 60 | "Metadata/Items/Maps/MapWorldsHarbingerLow", 61 | "Metadata/Items/Maps/MapWorldsPhoenix", 62 | "Metadata/Items/Maps/MapWorldsChimera", 63 | "Metadata/Items/Maps/MapWorldsHydra", 64 | "Metadata/Items/Maps/MapWorldsMinotaur", 65 | "Metadata/Items/Maps/MapWorldsVaalTemple", 66 | } 67 | 68 | MAPS_TO_SKIP_COMPOSITING = { 69 | "Metadata/Items/Maps/MapWorldsHarbingerUber", 70 | "Metadata/Items/Maps/MapWorldsHarbingerHigh", 71 | "Metadata/Items/Maps/MapWorldsHarbingerMid", 72 | "Metadata/Items/Maps/MapWorldsHarbingerLow", 73 | } 74 | -------------------------------------------------------------------------------- /PyPoE/poe/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | poe/__init__.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: 8e8aa1e56e696c0836081f8df9079ac77b0d7b08 $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | All modules directly related to Path of Exile can be found in this package. 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | 32 | # 3rd-party 33 | 34 | # self 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | # ============================================================================= 41 | # Classes 42 | # ============================================================================= 43 | 44 | # ============================================================================= 45 | # Functions 46 | # ============================================================================= 47 | -------------------------------------------------------------------------------- /PyPoE/poe/file/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | PyPoE/poe/file/__init__.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: 5d7a2c7cb41e48740f48af599c5dbc010807ffe0 $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | 32 | # 3rd-party 33 | 34 | # self 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | # __all__ = [] 41 | 42 | # ============================================================================= 43 | # Classes 44 | # ============================================================================= 45 | 46 | # ============================================================================= 47 | # Functions 48 | # ============================================================================= 49 | -------------------------------------------------------------------------------- /PyPoE/poe/file/dgr.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from PyPoE.poe.file.shared import AbstractFile, ParserError 4 | 5 | 6 | class DGRFile(AbstractFile): 7 | _re_version = re.compile(r"^version ([0-9]+)$") 8 | _re_data = re.compile(r'^(?P\S+):\s+(?P.*)|"(?P.*)"|(?P\d+)$') 9 | _re_token = re.compile( 10 | r'^(?:(?P\d+)|"(?P(?:[^"]|\\")*)"|(?P\S+))\s*(?P.*)$' 11 | ) 12 | 13 | def tokenise(self, line): 14 | result = [] 15 | rest = line 16 | while rest: 17 | match = self._re_token.match(rest) 18 | if not match: 19 | raise ParserError(f"Unexpected value in {line}\n at {rest}") 20 | if match.group("int") is not None: 21 | result.append(int(match.group("int"))) 22 | elif match.group("word") is not None: 23 | result.append(match.group("word")) 24 | else: 25 | quoted = match.group("quoted") 26 | result.append(quoted) 27 | if '"' in quoted or "\\\\" in quoted: 28 | # Assume that backslash-escapes work as one would expect? Nah let's throw it up 29 | raise ParserError(f'Possible backslash-escape found in "{quoted}"') 30 | rest = match.group("rest") 31 | 32 | return result 33 | 34 | def _read(self, buffer, *args, **kwargs): 35 | lines = buffer.read().decode("utf-16").splitlines() 36 | 37 | version = self._re_version.match(lines[0]) 38 | if not version: 39 | raise ParserError("Failed to find version. File may not be a .dgr file or malformed.") 40 | self.version = int(version.group(1)) 41 | 42 | self.data = {} 43 | self.strings = [] 44 | 45 | i = 1 46 | while i < len(lines): 47 | data = self._re_data.match(lines[i]) 48 | if not data: 49 | break 50 | i = i + 1 51 | 52 | ground = data.group("string") 53 | if ground is not None: 54 | self.strings.append(ground) 55 | elif data.group("number"): 56 | if not hasattr(self, "numbers"): 57 | self.numbers = [] 58 | self.numbers.append(int(data.group("number"))) 59 | else: 60 | room = data.group("key") 61 | value = data.group("value") 62 | match room: 63 | case "Size": 64 | self.size = [int(n) for n in value.split()] 65 | case "Nodes": 66 | self.node_count = int(value) 67 | case "Edges": 68 | self.edge_count = int(value) 69 | case _: 70 | try: 71 | parsed = self.tokenise(value) 72 | if len(parsed) == 1: 73 | self.data[room] = parsed[0] 74 | else: 75 | self.data[room] = parsed or None 76 | except Exception: 77 | print("Error parsing value", value, "for key", room) 78 | raise 79 | 80 | if i + self.node_count + self.edge_count != len(lines): 81 | raise ParserError( 82 | "Validation error: expected", 83 | i + self.node_count + self.edge_count, 84 | "lines, but found", 85 | len(lines), 86 | ) 87 | 88 | self.nodes = [] 89 | self.edges = [] 90 | edge_i = i + self.node_count 91 | 92 | for line in lines[i:edge_i]: 93 | x, y, vals, *rest = self.tokenise(line) 94 | vals, [room, transform, strings, *rest] = rest[:vals], rest[vals:] 95 | strings, rest = rest[:strings], rest[strings:] 96 | self.nodes.append( 97 | { 98 | "x": x, 99 | "y": y, 100 | "room": room, 101 | "transform": transform, 102 | "numbers": vals, 103 | "strings": strings, 104 | "unknown": rest, 105 | } 106 | ) 107 | 108 | for line in lines[edge_i:]: 109 | fr, to, path, *rest = self.tokenise(line) 110 | path, rest = rest[: path * 2], rest[path * 2 :] 111 | self.edges.append( 112 | { 113 | "to": to, 114 | "from": fr, 115 | "path": [path[i:][:2] for i in range(0, len(path), 2)], 116 | "unknown": rest, 117 | } 118 | ) 119 | -------------------------------------------------------------------------------- /PyPoE/poe/file/file_set.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from PyPoE.poe.file.shared import AbstractFile, ParserError 4 | 5 | 6 | class FileSet(AbstractFile): 7 | """ 8 | Representation of a .rs (roomset) or .tst (tileset) file 9 | """ 10 | 11 | _re_version = re.compile(r"^version ([0-9]+)$") 12 | _re_data = re.compile(r'^(?P[^"]*)"(?P[^"]*)"(?P[^"]*)$') 13 | 14 | def _read(self, buffer, *args, **kwargs): 15 | lines = buffer.read().decode("utf-16").splitlines() 16 | 17 | version = self._re_version.match(lines[0]) 18 | if version: 19 | self.version = int(version.group(1)) 20 | lines = lines[1:] 21 | 22 | self.files = [] 23 | 24 | for line in lines: 25 | m = self._re_data.match(line) 26 | if m: 27 | v = {"file": m.group("file")} 28 | if m.group("prefix"): 29 | v["prefix"] = m.group("prefix").split() 30 | if m.group("suffix"): 31 | v["suffix"] = m.group("suffix").split() 32 | self.files.append(v) 33 | elif not line or line.isspace() or line.startswith("//"): 34 | pass 35 | else: 36 | raise ParserError(f"Unexpected line '{line}'") 37 | -------------------------------------------------------------------------------- /PyPoE/poe/file/it.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | PyPoE/poe/file/it.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: 7a5298970325b6a84c2aa71ac7110ed058bf51f0 $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | Support for .it file format. 19 | 20 | Starting with version 3.20.0 of the game items use .it files instead of .ot 21 | 22 | See also: 23 | 24 | * :mod:`PyPoE.poe.file.ot` 25 | * :mod:`PyPoE.poe.file.otc` 26 | * :mod:`PyPoE.poe.file.dat` 27 | 28 | Agreement 29 | =============================================================================== 30 | 31 | See PyPoE/LICENSE 32 | 33 | 34 | Documentation 35 | =============================================================================== 36 | 37 | .. autoclass:: ITFile 38 | :exclude-members: clear, copy, default_factory, fromkeys, get, items, keys, pop, popitem, setdefault, update, values 39 | 40 | .. autoclass:: ITFileCache 41 | 42 | """ # noqa 43 | 44 | # ============================================================================= 45 | # Imports 46 | # ============================================================================= 47 | 48 | # Python 49 | 50 | # 3rd-party 51 | 52 | from PyPoE.poe.file.shared.keyvalues import ( 53 | AbstractKeyValueFile, 54 | AbstractKeyValueFileCache, 55 | AbstractKeyValueSection, 56 | ) 57 | 58 | # self 59 | from PyPoE.shared.decorators import doc 60 | 61 | # ============================================================================= 62 | # Globals 63 | # ============================================================================= 64 | 65 | __all__ = ["ITFile", "ITFileCache"] 66 | 67 | # ============================================================================= 68 | # Classes 69 | # ============================================================================= 70 | 71 | 72 | class BaseKeyValueSection(AbstractKeyValueSection): 73 | NAME = "Base" 74 | ORDERED_HASH_KEYS = {"tag"} 75 | 76 | 77 | class ModsKeyValueSection(AbstractKeyValueSection): 78 | NAME = "Mods" 79 | ORDERED_HASH_KEYS = {"enable_rarity"} 80 | 81 | 82 | class SocketsKeyValueSection(AbstractKeyValueSection): 83 | NAME = "Sockets" 84 | 85 | 86 | class StatsKeyValueSection(AbstractKeyValueSection): 87 | NAME = "Stats" 88 | 89 | 90 | class ImprintKeyValueSection(AbstractKeyValueSection): 91 | NAME = "Imprint" 92 | 93 | 94 | @doc(append=AbstractKeyValueFile) 95 | class ITFile(AbstractKeyValueFile): 96 | """ 97 | Representation of a .it file. 98 | """ 99 | 100 | SECTIONS = dict( 101 | (s.NAME, s) 102 | for s in [ 103 | BaseKeyValueSection, 104 | ModsKeyValueSection, 105 | SocketsKeyValueSection, 106 | StatsKeyValueSection, 107 | ImprintKeyValueSection, 108 | ] 109 | ) 110 | 111 | EXTENSION = ".it" 112 | 113 | def __init__(self, *args, **kwargs): 114 | super().__init__(*args, **kwargs) 115 | 116 | 117 | @doc(append=AbstractKeyValueFileCache) 118 | class ITFileCache(AbstractKeyValueFileCache[ITFile]): 119 | """ 120 | Cache for ITFile instances. 121 | """ 122 | 123 | FILE_TYPE = ITFile 124 | -------------------------------------------------------------------------------- /PyPoE/poe/file/ot.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | PyPoE/poe/file/ot.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: 19a6cf23eb887e85cd3179df621a32c01d034648 $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | Support for .ot file format. 19 | 20 | .ot file seem to be generally used for server-side settings related to abstract 21 | objects. 22 | 23 | Generally make sure to consider the context of the file when interpreting the 24 | contents; there is a chance they're extended or embedded though .dat files and 25 | the key/value pairs found are in relevance to the context. 26 | 27 | Usually they're accompanied by .otc files which handle client-side settings. 28 | 29 | See also: 30 | 31 | * :mod:`PyPoE.poe.file.otc` 32 | * :mod:`PyPoE.poe.file.dat` 33 | 34 | Agreement 35 | =============================================================================== 36 | 37 | See PyPoE/LICENSE 38 | 39 | 40 | Documentation 41 | =============================================================================== 42 | 43 | .. autoclass:: OTFile 44 | :exclude-members: clear, copy, default_factory, fromkeys, get, items, keys, pop, popitem, setdefault, update, values 45 | 46 | .. autoclass:: OTFileCache 47 | 48 | """ # noqa 49 | 50 | # ============================================================================= 51 | # Imports 52 | # ============================================================================= 53 | 54 | # Python 55 | 56 | # 3rd-party 57 | 58 | from PyPoE.poe.file.shared.keyvalues import ( 59 | AbstractKeyValueFile, 60 | AbstractKeyValueFileCache, 61 | AbstractKeyValueSection, 62 | ) 63 | 64 | # self 65 | from PyPoE.shared.decorators import doc 66 | 67 | # ============================================================================= 68 | # Globals 69 | # ============================================================================= 70 | 71 | __all__ = ["OTFile", "OTFileCache"] 72 | 73 | # ============================================================================= 74 | # Classes 75 | # ============================================================================= 76 | 77 | 78 | class ActionKeyValueSection(AbstractKeyValueSection): 79 | NAME = "Actor" 80 | 81 | 82 | class AnimatedKeyValueSection(AbstractKeyValueSection): 83 | NAME = "Animated" 84 | 85 | 86 | class BaseKeyValueSection(AbstractKeyValueSection): 87 | NAME = "Base" 88 | ORDERED_HASH_KEYS = {"tag"} 89 | 90 | 91 | class ModsKeyValueSection(AbstractKeyValueSection): 92 | NAME = "Mods" 93 | ORDERED_HASH_KEYS = {"enable_rarity"} 94 | 95 | 96 | class PathfindingKeyValueSection(AbstractKeyValueSection): 97 | NAME = "Pathfinding" 98 | 99 | 100 | class PositionedKeyValueSection(AbstractKeyValueSection): 101 | NAME = "Positioned" 102 | 103 | 104 | class SocketsKeyValueSection(AbstractKeyValueSection): 105 | NAME = "Sockets" 106 | 107 | 108 | class StatsKeyValueSection(AbstractKeyValueSection): 109 | NAME = "Stats" 110 | 111 | 112 | @doc(append=AbstractKeyValueFile) 113 | class OTFile(AbstractKeyValueFile): 114 | """ 115 | Representation of a .ot file. 116 | """ 117 | 118 | SECTIONS = dict( 119 | (s.NAME, s) 120 | for s in [ 121 | ActionKeyValueSection, 122 | AnimatedKeyValueSection, 123 | BaseKeyValueSection, 124 | ModsKeyValueSection, 125 | PathfindingKeyValueSection, 126 | PositionedKeyValueSection, 127 | SocketsKeyValueSection, 128 | StatsKeyValueSection, 129 | ] 130 | ) 131 | 132 | EXTENSION = ".ot" 133 | 134 | def __init__(self, *args, **kwargs): 135 | super().__init__(*args, **kwargs) 136 | 137 | 138 | @doc(append=AbstractKeyValueFileCache) 139 | class OTFileCache(AbstractKeyValueFileCache[OTFile]): 140 | """ 141 | Cache for OTFile instances. 142 | """ 143 | 144 | FILE_TYPE = OTFile 145 | -------------------------------------------------------------------------------- /PyPoE/poe/file/specification/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | PyPoE/poe/file/specification/__init__.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: 3c1471e0fb2f6d00b02076e8fb398489195d6924 $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | .. autofunction:: load 16 | """ 17 | 18 | # ============================================================================= 19 | # Imports 20 | # ============================================================================= 21 | 22 | # Python 23 | import importlib 24 | from importlib.machinery import SourceFileLoader 25 | 26 | # self 27 | from PyPoE.poe.constants import VERSION 28 | 29 | # 3rd-party 30 | 31 | 32 | # ============================================================================= 33 | # Globals 34 | # ============================================================================= 35 | 36 | __all__ = ["load"] 37 | 38 | # ============================================================================= 39 | # Globals 40 | # ============================================================================= 41 | 42 | 43 | def load(path=None, version=VERSION.DEFAULT, reload=False, validate=None): 44 | """ 45 | Loads a specification from a python module that can be used for the dat 46 | files. 47 | The file must implement the classes from 48 | :py:mod:`PyPoE.poe.file.specification.fields` and expose the specification 49 | with a variable "specification" for this to work properly. 50 | 51 | Since this function is using python imports specifications are 52 | automatically cached once loaded. If using a cached version is not desired 53 | set the reload parameter to True. 54 | 55 | .. warning:: 56 | Please note that many usages of the reload function will cause a memory 57 | leak since python does not remove old modules from it's cache. 58 | 59 | Parameters 60 | ---------- 61 | path : str 62 | If specified, read the specified python module as specification 63 | version : constants.VERSION 64 | Version of the game to load the specification for; only works if 65 | path is not specified. 66 | reload : bool 67 | Whether to reload the specified specification. 68 | validate : bool or None 69 | Whether additional validation will be run on the Specification. 70 | By default (None), this will only occur when custom specifications are 71 | loaded and not when default specifications are loaded. 72 | 73 | Returns 74 | ------- 75 | :class:`ConfigObj` 76 | returns the ConfigObj of the read file. 77 | 78 | 79 | Raises 80 | ------ 81 | ValueError 82 | if version passed is not valid 83 | SpecificationError 84 | if validation is enabled and any issues occur 85 | """ 86 | if path is None: 87 | if validate is None: 88 | validate = False 89 | 90 | if version in ( 91 | VERSION.STABLE, 92 | VERSION.BETA, 93 | VERSION.ALPHA, 94 | VERSION.GENERATED, 95 | VERSION.POE2, 96 | ): 97 | module = importlib.import_module( 98 | "PyPoE.poe.file.specification.data.%s" % version.name.lower() 99 | ) 100 | else: 101 | raise ValueError("Unknown version or version currently not supported: %s" % version) 102 | else: 103 | if validate is None: 104 | validate = True 105 | 106 | module = SourceFileLoader("", path).load_module() 107 | 108 | if reload: 109 | importlib.reload(module) 110 | 111 | if validate: 112 | module.specification.validate() 113 | 114 | return module.specification 115 | -------------------------------------------------------------------------------- /PyPoE/poe/file/specification/data/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | PyPoE/poe/file/specification/data/__init__.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: d891d7c24650b544fe0b352fe540d1e26de8660d $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | .dat specifications for the GGG Path of Exile client 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | 32 | # 3rd-party 33 | 34 | # self 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | __all__ = [] 41 | -------------------------------------------------------------------------------- /PyPoE/poe/file/specification/errors.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | PyPoE/poe/file/specification/errors.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: 3761b7a447de609e5c7d4a53f4659ddb25da403b $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | 25 | Documentation 26 | =============================================================================== 27 | 28 | .. autoclass:: SpecificationError 29 | 30 | .. autoclass:: SpecificationError.ERRORS 31 | 32 | .. autoclass:: SpecificationWarning 33 | """ 34 | 35 | # ============================================================================= 36 | # Imports 37 | # ============================================================================= 38 | 39 | # Python 40 | from enum import IntEnum 41 | 42 | # 3rd-party 43 | 44 | # self 45 | 46 | # ============================================================================= 47 | # Globals 48 | # ============================================================================= 49 | 50 | __all__ = ["SpecificationError", "SpecificationWarning"] 51 | 52 | # ============================================================================= 53 | # Exceptions & Warnings 54 | # ============================================================================= 55 | 56 | 57 | class SpecificationError(ValueError): 58 | """ 59 | SpecificationErrors are raised to indicate there is a problem with the 60 | specification compared to the data. 61 | 62 | Unlike most errors, they are raised with an error code and the error 63 | message. The error code can be used to capture specific errors more 64 | accurately. 65 | """ 66 | 67 | class ERRORS(IntEnum): 68 | """ 69 | Numeric meaning: 70 | 71 | * 1xxx - indicates issues with format of fields 72 | * 2xxx - indicates issues with format of virtual fields 73 | * 3xxx - indicates issues at runtime 74 | 75 | Attributes 76 | ---------- 77 | INVALID_FOREIGN_KEY_FILE 78 | Foreign key file does not exist 79 | INVALID_FOREIGN_KEY_ID 80 | Foreign key with the specified id does not exist 81 | INVALID_ARGUMENT_COMBINATION 82 | Invalid combination of multiple arguments; i.e. when they can't be 83 | used together 84 | INVALID_ENUM_NAME 85 | Enum does not exist in :py:mod:`PyPoE.poe.constants` 86 | VIRTUAL_KEY_EMPTY 87 | Virtual key does not have fields defined 88 | VIRTUAL_KEY_DUPLICATE 89 | Virtual key is a duplicate of a regular key 90 | VIRTUAL_KEY_INVALID_KEY 91 | Invalid fields specified for the virtual key 92 | VIRTUAL_KEY_INVALID_DATA_TYPE 93 | Invalid data type(s) in the target fields 94 | RUNTIME_MISSING_SPECIFICATION 95 | No specification found in the specification format used for the 96 | function call 97 | RUNTIME_MISSING_FOREIGN_KEY 98 | A single foreign key reference could not be resolved 99 | RUNTIME_ROWSIZE_MISMATCH 100 | The row size in the specification doesn't match the real data row 101 | size 102 | """ 103 | 104 | INVALID_FOREIGN_KEY_FILE = 1000 105 | INVALID_FOREIGN_KEY_ID = 1001 106 | INVALID_ARGUMENT_COMBINATION = 1002 107 | INVALID_ENUM_NAME = 1003 108 | VIRTUAL_KEY_EMPTY = 2000 109 | VIRTUAL_KEY_DUPLICATE = 2001 110 | VIRTUAL_KEY_INVALID_KEY = 2002 111 | VIRTUAL_KEY_INVALID_DATA_TYPE = 2003 112 | RUNTIME_MISSING_SPECIFICATION = 3000 113 | RUNTIME_MISSING_FOREIGN_KEY = 3001 114 | RUNTIME_ROWSIZE_MISMATCH = 3002 115 | 116 | def __init__(self, code, msg): 117 | super().__init__() 118 | self.code = self.ERRORS(code) 119 | self.msg = msg 120 | 121 | def __str__(self): 122 | return "%s: %s" % (repr(self.code), self.msg) 123 | 124 | 125 | class SpecificationWarning(UserWarning): 126 | pass 127 | -------------------------------------------------------------------------------- /PyPoE/poe/file/specification/generation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/PyPoE/poe/file/specification/generation/__init__.py -------------------------------------------------------------------------------- /PyPoE/poe/file/specification/generation/column_naming.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from PyPoE.poe.constants import VERSION 4 | 5 | 6 | class UnknownColumnNameGenerator: 7 | _flag_count = 0 8 | _key_count = 0 9 | _keys_count = 0 10 | _data_count = 0 11 | _unknown_count = 0 12 | 13 | def next_name(self, column) -> str: 14 | if column.type == "bool": 15 | name = f"Flag{self._flag_count}" 16 | self._flag_count += 1 17 | elif column.array: 18 | if column.type == "foreignrow": 19 | name = f"Keys{self._keys_count}" 20 | self._keys_count += 1 21 | else: 22 | name = f"Data{self._data_count}" 23 | self._data_count += 1 24 | elif column.type == "foreignrow": 25 | name = f"Key{self._key_count}" 26 | self._key_count += 1 27 | else: 28 | name = f"Unknown{self._unknown_count}" 29 | self._unknown_count += 1 30 | return name 31 | 32 | 33 | def StableToGeneratedNameMapping(name: str): 34 | for suffix in ["Key", "Keys", "sKey", "sKeys", "esKey", "esKeys", "s"]: 35 | if name.endswith(suffix): 36 | yield name.removesuffix(suffix) 37 | m = re.match(r"^(.*\D)(\d+)$", name) 38 | if m and m.group(1).endswith(suffix): 39 | yield m.group(1).removesuffix(suffix) + m.group(2) 40 | 41 | 42 | name_mappings = {VERSION.STABLE: StableToGeneratedNameMapping} 43 | -------------------------------------------------------------------------------- /PyPoE/poe/file/specification/generation/custom_attributes.py: -------------------------------------------------------------------------------- 1 | class CustomizedField: 2 | def __init__(self, enum: str = None): 3 | self.enum = enum 4 | 5 | 6 | custom_attributes = { 7 | "BaseItemTypes.dat": { 8 | "ModDomainsKey": CustomizedField( 9 | enum="MOD_DOMAIN", 10 | ), 11 | }, 12 | "BestiaryRecipes.dat": { 13 | "GameMode": CustomizedField( 14 | enum="GAME_MODES", 15 | ), 16 | }, 17 | "BestiaryRecipeComponent.dat": { 18 | "RarityKey": CustomizedField( 19 | enum="RARITY", 20 | ), 21 | }, 22 | "BetrayalUpgrades.dat": { 23 | "BetrayalUpgradeSlotsKey": CustomizedField( 24 | enum="BETRAYAL_UPGRADE_SLOTS", 25 | ), 26 | }, 27 | "DelveUpgrades.dat": { 28 | "DelveUpgradeTypeKey": CustomizedField( 29 | enum="DELVE_UPGRADE_TYPE", 30 | ), 31 | }, 32 | "GrantedEffectsPerLevel.dat": { 33 | "StatInterpolationTypesKeys": CustomizedField( 34 | enum="STAT_INTERPOLATION_TYPES", 35 | ), 36 | }, 37 | "HarvestObjects.dat": { 38 | "ObjectType": CustomizedField( 39 | enum="HARVEST_OBJECT_TYPES", 40 | ), 41 | }, 42 | "Mods.dat": { 43 | "Domain": CustomizedField( 44 | enum="MOD_DOMAIN", 45 | ), 46 | "GenerationType": CustomizedField( 47 | enum="MOD_GENERATION_TYPE", 48 | ), 49 | "GameMode": CustomizedField( 50 | enum="GAME_MODES", 51 | ), 52 | }, 53 | "Scarabs.dat": { 54 | "ScarabType": CustomizedField( 55 | enum="SCARAB_TYPES", 56 | ), 57 | }, 58 | "ShopPaymentPackage.dat": { 59 | "ShopPackagePlatformKeys": CustomizedField( 60 | enum="SHOP_PACKAGE_PLATFORM", 61 | ), 62 | }, 63 | "SupporterPackSets.dat": { 64 | "ShopPackagePlatformKey": CustomizedField( 65 | enum="SHOP_PACKAGE_PLATFORM", 66 | ), 67 | }, 68 | "Words.dat": { 69 | "WordlistsKey": CustomizedField( 70 | enum="WORDLISTS", 71 | ), 72 | }, 73 | } 74 | -------------------------------------------------------------------------------- /PyPoE/poe/file/specification/generation/template.py: -------------------------------------------------------------------------------- 1 | # template for data/stable.py 2 | """ 3 | Description 4 | =============================================================================== 5 | 6 | Contains the specification for the stable version of the game. 7 | This file is generated automatically based on 8 | https://github.com/poe-tool-dev/dat-schema. Do not modify it manually. 9 | 10 | Please see the following for more details: 11 | :py:mod:`PyPoE.poe.file.specification.fields` 12 | Information about the Field classes 13 | :py:mod:`PyPoE.poe.file.specification` 14 | Specification loader 15 | :py:mod:`PyPoE.poe.file.specification.generation` 16 | Automatic generation 17 | 18 | Agreement 19 | =============================================================================== 20 | 21 | See PyPoE/LICENSE 22 | """ 23 | 24 | # flake8: noqa 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # 3rd-party 31 | from PyPoE.poe.file.specification.fields import Field, File, Specification, VirtualField 32 | 33 | # self 34 | 35 | # ============================================================================= 36 | # Globals 37 | # ============================================================================= 38 | 39 | __all__ = [ 40 | "specification", 41 | ] 42 | 43 | specification = Specification( 44 | 0, # 45 | { 46 | # 47 | }, 48 | ) 49 | -------------------------------------------------------------------------------- /PyPoE/poe/file/stat_filters.py: -------------------------------------------------------------------------------- 1 | """ 2 | Parser for skillpopup_stat_filters.txt 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/poe/file/stat_filters.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: f82d8b7c4b21db97d1d288e55a6d666982120e6f $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Parser for Metadata/StatDescriptions/skillpopup_stat_filters.txt 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | 27 | Documentation 28 | =============================================================================== 29 | 30 | Public API 31 | ------------------------------------------------------------------------------- 32 | 33 | .. autoclass:: StatFilterFile 34 | 35 | .. autoclass:: SkillEntry 36 | """ 37 | 38 | # ============================================================================= 39 | # Imports 40 | # ============================================================================= 41 | 42 | # Python 43 | import re 44 | 45 | from PyPoE.poe.file.shared import AbstractFileReadOnly 46 | 47 | # self 48 | from PyPoE.shared.mixins import ReprMixin 49 | 50 | # 3rd-party 51 | 52 | 53 | # ============================================================================= 54 | # Globals 55 | # ============================================================================= 56 | 57 | __all__ = [] 58 | 59 | # ============================================================================= 60 | # Classes 61 | # ============================================================================= 62 | 63 | 64 | class SkillEntry(ReprMixin): 65 | """ 66 | 67 | Attributes 68 | ---------- 69 | skill_id : str 70 | Id from ActiveSkills.dat 71 | translation_file_path : str 72 | Path to the translation file that should be used for this skill id. 73 | Path is relative to content.ggpk 74 | stats : list[str] 75 | Order in which to display stats 76 | """ 77 | 78 | __slots__ = ["skill_id", "translation_file_path", "stats"] 79 | 80 | def __init__(self, skill_id, translation_file_path, stats): 81 | self.skill_id = skill_id 82 | self.translation_file_path = translation_file_path 83 | self.stats = stats 84 | 85 | 86 | class StatFilterFile(AbstractFileReadOnly): 87 | """ 88 | Parser for Metadata/skillpopup_stat_filters.txt 89 | 90 | Attributes 91 | ---------- 92 | groups : dict[str, list[str]] 93 | Dictionary containing stat groups with the id as key, and the list of 94 | stats as value 95 | skills : dict[str, SkillEntry] 96 | Dictionary mapping the active skill id (as key) to a the respective 97 | :class:`SkillEntry` instance as value. 98 | """ 99 | 100 | _re_find_sections = re.compile( 101 | # header 102 | r"^(?:" r"group (?P[\w]+)|" r'(?P[\w]+) "(?P[\w/\.]+)"' r")[\r\n]+" 103 | # contents 104 | r"^{" r"(?P[^}]*)" r"^}", 105 | re.UNICODE | re.MULTILINE, 106 | ) 107 | 108 | _re_find_contents = re.compile( 109 | r"[\w$]+", 110 | re.UNICODE | re.MULTILINE, 111 | ) 112 | 113 | groups = None 114 | skills = None 115 | 116 | def _read(self, buffer, *args, **kwargs): 117 | data = buffer.read().decode("utf-16") 118 | 119 | self.groups = {} 120 | self.skills = {} 121 | 122 | for match in self._re_find_sections.finditer(data): 123 | contents = self._re_find_contents.findall(match.group("contents")) 124 | if match.group("group"): 125 | self.groups[match.group("group")] = contents 126 | elif match.group("skill_id"): 127 | stats = [] 128 | for stat in contents: 129 | if stat.startswith("$"): 130 | stats.extend(self.groups[stat[1:]]) 131 | else: 132 | stats.append(stat) 133 | self.skills[match.group("skill_id")] = SkillEntry( 134 | skill_id=match.group("skill_id"), 135 | translation_file_path=match.group("file"), 136 | stats=stats, 137 | ) 138 | -------------------------------------------------------------------------------- /PyPoE/poe/file/tsi.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from PyPoE.poe.file.shared import AbstractFile, ParserError 4 | 5 | 6 | class TSIFile(AbstractFile): 7 | _re_data = re.compile( 8 | r'^(?P\S+)\s+(?:"(?P.*)"|(?P\d+)|(?P\d+\s*)+)$' 9 | ) 10 | 11 | def _read(self, buffer, *args, **kwargs): 12 | 13 | self.data = {} 14 | 15 | for line in buffer.read().decode("utf-16").splitlines(): 16 | m = self._re_data.match(line) 17 | if m: 18 | if m.group("number"): 19 | self.data[m.group("key")] = int(m.group("number")) 20 | if m.group("numbers"): 21 | self.data[m.group("key")] = [int(n) for n in m.group("numbers").split()] 22 | else: 23 | self.data[m.group("key")] = m.group("string") 24 | elif not line or line.isspace() or line.startswith("//"): 25 | pass 26 | else: 27 | raise ParserError(f"Unexpected line '{line}'") 28 | -------------------------------------------------------------------------------- /PyPoE/poe/sim/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | PyPoE/sim/__init__.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: 738bbf451684ede5ce4e82d7e2467c805e81444e $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | 32 | # 3rd-party 33 | 34 | # self 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | # ============================================================================= 41 | # Classes 42 | # ============================================================================= 43 | 44 | # ============================================================================= 45 | # Functions 46 | # ============================================================================= 47 | -------------------------------------------------------------------------------- /PyPoE/shared/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shared Python code 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/shared/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 77c0877d7e78533963b6945d4a8763117fb737c2 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # 3rd-party 35 | 36 | # self 37 | 38 | # ============================================================================= 39 | # Globals 40 | # ============================================================================= 41 | 42 | __all__ = [] 43 | 44 | # ============================================================================= 45 | # Classes 46 | # ============================================================================= 47 | 48 | 49 | class InheritedDocStringsMeta(type): 50 | def __new__(cls, name, args, attrs): 51 | if not ("__doc__" in attrs and attrs["__doc__"]): 52 | for mro in cls.mro(cls): 53 | docstring = mro.__doc__ 54 | if docstring is not None: 55 | cls.__doc__ = docstring 56 | # attrs['__doc__'] = docstring 57 | break 58 | for attr, attribute in attrs.items(): 59 | if attribute.__doc__: 60 | continue 61 | 62 | for mro in cls.mro(cls): 63 | if not hasattr(mro, attr): 64 | break 65 | docstring = getattr(mro, attr).__doc__ 66 | if docstring is not None: 67 | attribute.__doc__ = docstring 68 | break 69 | 70 | return type.__new__(cls, name, args, attrs) 71 | 72 | 73 | # ============================================================================= 74 | # Functions 75 | # ============================================================================= 76 | -------------------------------------------------------------------------------- /PyPoE/shared/config/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utilities for config handling 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/shared/config/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 4a187a5caccb97ea31c6ddc607a3c7583d694d9f $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # 3rd-party 35 | 36 | # self 37 | 38 | # ============================================================================= 39 | # Globals 40 | # ============================================================================= 41 | 42 | __all__ = [] 43 | 44 | # ============================================================================= 45 | # Classes 46 | # ============================================================================= 47 | 48 | # ============================================================================= 49 | # Functions 50 | # ============================================================================= 51 | -------------------------------------------------------------------------------- /PyPoE/shared/murmur2.py: -------------------------------------------------------------------------------- 1 | """ 2 | MurmurHash2 Python Implementation 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/shared/murmur2.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 04572e9d3cddcefe98973c4862cc7643630fbb7a $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | A pure python implementation of the MurmurHash2 algorithm by Austin Appleby. 21 | See also: https://code.google.com/p/smhasher/wiki/MurmurHash 22 | 23 | Agreement 24 | =============================================================================== 25 | 26 | See PyPoE/LICENSE 27 | """ 28 | 29 | # ============================================================================= 30 | # Imports 31 | # ============================================================================= 32 | 33 | import struct 34 | 35 | # ============================================================================= 36 | # Globals & Constants 37 | # ============================================================================= 38 | 39 | DEFAULT_SEED = 0 40 | # 'm' and 'r' are mixing constants generated offline. 41 | # They're not really 'magic', they just happen to work well. 42 | M = 0x5BD1E995 43 | R = 24 44 | 45 | int32 = 0xFFFFFFFF 46 | 47 | # ============================================================================= 48 | # Functions 49 | # ============================================================================= 50 | 51 | 52 | def murmur2_32(byte_data, seed=DEFAULT_SEED): 53 | """ 54 | Creates a murmur2 32 bit integer hash from the given byte_data and seed. 55 | 56 | :param bytes byte_data: the bytes to hash 57 | :param int seed: seed to initialize this with 58 | :return int: 32 bit hash 59 | """ 60 | 61 | length = len(byte_data) 62 | # Initialize the hash to a 'random' value 63 | h = (seed ^ length) & int32 64 | 65 | # Mix 4 bytes at a time into the hash 66 | index = 0 67 | 68 | while length >= 4: 69 | k = struct.unpack("> R & int32) 73 | k = k * M & int32 74 | 75 | h = h * M & int32 76 | h = (h ^ k) & int32 77 | 78 | index += 4 79 | length -= 4 80 | 81 | # Handle the last few bytes of the input array 82 | if length >= 3: 83 | h = (h ^ byte_data[index + 2] << 16) & int32 84 | if length >= 2: 85 | h = (h ^ byte_data[index + 1] << 8) & int32 86 | if length >= 1: 87 | h = (h ^ byte_data[index]) & int32 88 | h = h * M & int32 89 | 90 | # Do a few final mixes of the hash to ensure the last few bytes are 91 | # well-incorporated. 92 | h = h ^ (h >> 13 & int32) 93 | h = h * M & int32 94 | h = h ^ (h >> 15 & int32) 95 | 96 | return h 97 | 98 | 99 | def bytes_to_long(bytes): 100 | assert len(bytes) == 8 101 | return sum((b << (k * 8) for k, b in enumerate(bytes))) 102 | 103 | 104 | # https://gist.github.com/wey-gu/5543c33987c0a5e8f7474b9b80cd36aa 105 | def murmur2_64a(data, seed=0x1337B33F): 106 | import ctypes 107 | 108 | m = ctypes.c_uint64(0xC6A4A7935BD1E995).value 109 | 110 | r = ctypes.c_uint32(47).value 111 | 112 | MASK = ctypes.c_uint64(2**64 - 1).value 113 | 114 | data_as_bytes = bytearray(data) 115 | 116 | seed = ctypes.c_uint64(seed).value 117 | 118 | h = seed ^ ((m * len(data_as_bytes)) & MASK) 119 | 120 | off = int(len(data_as_bytes) / 8) * 8 121 | for ll in range(0, off, 8): 122 | k = bytes_to_long(data_as_bytes[ll : ll + 8]) 123 | k = (k * m) & MASK 124 | k = k ^ ((k >> r) & MASK) 125 | k = (k * m) & MASK 126 | h = h ^ k 127 | h = (h * m) & MASK 128 | 129 | length = len(data_as_bytes) & 7 130 | 131 | if length >= 7: 132 | h = h ^ (data_as_bytes[off + 6] << 48) 133 | 134 | if length >= 6: 135 | h = h ^ (data_as_bytes[off + 5] << 40) 136 | 137 | if length >= 5: 138 | h = h ^ (data_as_bytes[off + 4] << 32) 139 | 140 | if length >= 4: 141 | h = h ^ (data_as_bytes[off + 3] << 24) 142 | 143 | if length >= 3: 144 | h = h ^ (data_as_bytes[off + 2] << 16) 145 | 146 | if length >= 2: 147 | h = h ^ (data_as_bytes[off + 1] << 8) 148 | 149 | if length >= 1: 150 | h = h ^ data_as_bytes[off] 151 | h = (h * m) & MASK 152 | 153 | h = h ^ ((h >> r) & MASK) 154 | h = (h * m) & MASK 155 | h = h ^ ((h >> r) & MASK) 156 | 157 | return ctypes.c_uint64(h).value 158 | -------------------------------------------------------------------------------- /PyPoE/ui/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | UI Code 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/ui/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 057ab58c40321342f93737d1566c8c9ad6774916 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # 3rd-party 35 | 36 | # self 37 | from PyPoE.ui.ggpk_viewer import GGPKViewerMainWindow 38 | from PyPoE.ui.launchpad import launchpad_main 39 | 40 | # ============================================================================= 41 | # Globals 42 | # ============================================================================= 43 | 44 | __all__ = [] 45 | 46 | _apps = [GGPKViewerMainWindow] 47 | 48 | # ============================================================================= 49 | # Entry point 50 | # ============================================================================= 51 | 52 | 53 | def main(*args, **kwargs): 54 | launchpad_main(_apps, *args, **kwargs) 55 | 56 | 57 | if __name__ == "__main__": 58 | main() 59 | -------------------------------------------------------------------------------- /PyPoE/ui/ggpk_viewer/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | GGPK Viewer code 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/ui/shared/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 66c41da5ed593459e72afadb56a787e1c8103fb5 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Specific code for the GGPK Viewer UI application. 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # 3rd-party 35 | 36 | # self 37 | from PyPoE.ui.ggpk_viewer.core import GGPKViewerMainWindow 38 | from PyPoE.ui.shared import main 39 | 40 | # ============================================================================= 41 | # Globals 42 | # ============================================================================= 43 | 44 | __all__ = ["GGPKViewerMainWindow"] 45 | 46 | # ============================================================================= 47 | # Classes 48 | # ============================================================================= 49 | 50 | # ============================================================================= 51 | # Functions 52 | # ============================================================================= 53 | 54 | if __name__ == "__main__": 55 | main(GGPKViewerMainWindow) 56 | -------------------------------------------------------------------------------- /PyPoE/ui/launchpad/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Launchpad 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/ui/launchpad/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 8a8380389ac9a7435b7e1a959d8a0400f3f9cd46 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | import sys 34 | 35 | # 3rd-party 36 | from PySide2.QtCore import * 37 | from PySide2.QtWidgets import * 38 | 39 | # self 40 | from PyPoE.ui.shared import main 41 | 42 | # ============================================================================= 43 | # Globals 44 | # ============================================================================= 45 | 46 | __all__ = ["LaunchpadMainWindow", "launchpad_main"] 47 | 48 | # ============================================================================= 49 | # Classes 50 | # ============================================================================= 51 | 52 | 53 | class LaunchpadMainWindow(QMainWindow): 54 | child_closed = Signal(QWidget) 55 | 56 | def __init__(self, apps, *args, **kwargs): 57 | QMainWindow.__init__(self, *args, **kwargs) 58 | self.apps = apps 59 | 60 | self.child_closed.connect(self._handle_closed_child) 61 | 62 | self.setWindowTitle(self.tr("PyPoE UI Launchpad")) 63 | 64 | frame = QFrame(parent=self) 65 | layout = QVBoxLayout() 66 | frame.setLayout(layout) 67 | 68 | layout.addWidget(QLabel(self.tr("Choose an application to start"))) 69 | 70 | self.buttons = [] 71 | self.instances = [] 72 | for i, qmainwindow_cls in enumerate(apps): 73 | button = QPushButton(qmainwindow_cls.NAME) 74 | button.clicked.connect(self._wrap_clicked(i)) 75 | layout.addWidget(button) 76 | self.buttons.append(button) 77 | self.instances.append(None) 78 | 79 | self.setCentralWidget(frame) 80 | 81 | def _wrap_clicked(self, i): 82 | def wrapped(): 83 | return self.run_application(i) 84 | 85 | return wrapped 86 | 87 | def _handle_closed_child(self, qwidget): 88 | for i, item in enumerate(self.instances): 89 | if item == qwidget: 90 | break 91 | 92 | self.buttons[i].setEnabled(True) 93 | self.buttons[i].setText(qwidget.NAME) 94 | 95 | def run_application(self, i): 96 | self.buttons[i].setEnabled(False) 97 | self.buttons[i].setText(self.buttons[i].text() + self.tr(" (Running)")) 98 | qmainwindow = self.apps[i](parent=self) 99 | qmainwindow.show() 100 | qmainwindow.activateWindow() 101 | 102 | self.instances[i] = qmainwindow 103 | 104 | 105 | # ============================================================================= 106 | # Functions 107 | # ============================================================================= 108 | 109 | 110 | def launchpad_main(*args, **kwargs): 111 | main(LaunchpadMainWindow, *args, **kwargs) 112 | -------------------------------------------------------------------------------- /PyPoE/ui/shared/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shared UI code 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/ui/shared/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 876ff675bbaf6ec69190c6752ce19d4d00e704f3 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | import os 33 | 34 | # Python 35 | import sys 36 | import time 37 | 38 | # 3rd-party 39 | from PySide2.QtCore import * 40 | from PySide2.QtWidgets import * 41 | 42 | # self 43 | from PyPoE.ui.shared.settings import SettingsWindow 44 | 45 | # ============================================================================= 46 | # Globals 47 | # ============================================================================= 48 | 49 | __all__ = ["SharedMainWindow", "main"] 50 | 51 | # ============================================================================= 52 | # Classes 53 | # ============================================================================= 54 | 55 | 56 | class SharedMainWindow(QMainWindow): 57 | """ 58 | Shared Window class for use in sub applications to be launched from the 59 | Launchpad. 60 | """ 61 | 62 | sig_log_message = Signal(str) 63 | 64 | def __init__(self, *args, app_name=NotImplemented, **kwargs): 65 | super().__init__(*args, **kwargs) 66 | 67 | self.NAME = app_name 68 | 69 | QCoreApplication.setApplicationName(app_name) 70 | QSettings.setDefaultFormat(QSettings.IniFormat) 71 | self.settings = QSettings() 72 | self.APP_ROOT_DIR = os.path.split(self.settings.fileName())[0] 73 | self.APP_DIR = os.path.join(self.APP_ROOT_DIR, app_name) 74 | if not os.path.exists(self.APP_DIR): 75 | os.makedirs(self.APP_DIR) 76 | 77 | # Setup logging 78 | self.sig_log_message.connect(self._write_log) 79 | 80 | # Still needs to be setup 81 | self.notification = QTextEdit(readOnly=True) 82 | self.notification.setFixedHeight(100) 83 | 84 | self.settings_window = SettingsWindow(parent=self) 85 | 86 | def _write_log(self, msg, notification=None): 87 | timef = time.strftime("%H:%M:%S - ") 88 | if notification is None: 89 | notification = msg 90 | self.statusBar().showMessage(timef + notification) 91 | self.notification.append(timef + msg) 92 | QApplication.instance().processEvents() 93 | 94 | def closeEvent(self, *args, **kwargs): 95 | p = self.parent() 96 | if p is None: 97 | return 98 | 99 | p.child_closed.emit(self) 100 | 101 | 102 | # ============================================================================= 103 | # Functions 104 | # ============================================================================= 105 | 106 | 107 | def main(maincls, *args, **kwargs): 108 | """ 109 | Load translations/app and start the qt application. 110 | 111 | Parameters 112 | ---------- 113 | maincls 114 | Class to instantiate with the given arguments and keywords 115 | """ 116 | translator = QTranslator() 117 | translator.load("i18n/en_US") 118 | 119 | app = QApplication(sys.argv) 120 | app.setOrganizationName("PyPoE") 121 | app.installTranslator(translator) 122 | 123 | maininst = maincls(*args, **kwargs) 124 | maininst.show() 125 | 126 | sys.exit(app.exec_()) 127 | -------------------------------------------------------------------------------- /PyPoE/ui/shared/dialog.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shared Dialog Prompts 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/ui/shared/dialog.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 2a3090c26b4302a40546d916535d749ef9bdc43a $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Contains various self-contained dialog prompts for various tasks. 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | import re 34 | 35 | # 3rd Party 36 | from PySide2.QtCore import * 37 | from PySide2.QtWidgets import * 38 | 39 | # self 40 | from PyPoE.ui.shared.regex_widgets import RegexFlagsBox 41 | 42 | # ============================================================================= 43 | # Imports 44 | # ============================================================================= 45 | 46 | __all__ = ["RegExSearchDialog"] 47 | 48 | # ============================================================================= 49 | # Imports 50 | # ============================================================================= 51 | 52 | 53 | class RegExSearchDialog(QDialog): 54 | def __init__(self, *args, **kwargs): 55 | QDialog.__init__(self, *args, **kwargs) 56 | 57 | self.setWindowTitle(self.tr("RegEx Search Dialog")) 58 | 59 | self.master_layout = QVBoxLayout() 60 | self.setLayout(self.master_layout) 61 | 62 | self.regex_box = RegexFlagsBox(default_flags=re.IGNORECASE) 63 | self.master_layout.addWidget(self.regex_box) 64 | 65 | self.option_group_box = QGroupBox(self.tr("Search Options", parent=self)) 66 | self.master_layout.addWidget(self.option_group_box) 67 | 68 | self.option_group_box_layout = QVBoxLayout() 69 | self.option_group_box.setLayout(self.option_group_box_layout) 70 | 71 | self.option_search_directories = QCheckBox( 72 | self.tr("Search directory names", parent=self.option_group_box) 73 | ) 74 | self.option_group_box_layout.addWidget(self.option_search_directories) 75 | 76 | self.option_full_path = QCheckBox(self.tr("Show full path", parent=self.option_group_box)) 77 | self.option_group_box_layout.addWidget(self.option_full_path) 78 | 79 | self.master_layout.addWidget(QLabel(self.tr("Enter Regular Expression:"), parent=self)) 80 | 81 | self.regex_input = QLineEdit() 82 | self.master_layout.addWidget(self.regex_input) 83 | 84 | self.button_box = QDialogButtonBox( 85 | QDialogButtonBox.Ok | QDialogButtonBox.Cancel | QDialogButtonBox.Reset 86 | ) 87 | self.button_box.accepted.connect(self.accept) 88 | self.button_box.rejected.connect(self.reject) 89 | 90 | b = self.button_box.button(QDialogButtonBox.Reset) 91 | b.clicked.connect(self.reset) 92 | 93 | self.master_layout.addWidget(self.button_box) 94 | 95 | # Set default states 96 | self.reset() 97 | 98 | def accept(self, *args, **kwargs): 99 | # Validate and store the regex 100 | try: 101 | self.regex_compiled = re.compile(self.regex_input.text(), self.regex_box.get_flags()) 102 | except re.error as e: 103 | QMessageBox.critical( 104 | self, self.tr("RegEx Error"), self.tr("regular Expression error:\n %s") % e.args[0] 105 | ) 106 | # TODO: This may be unncessary. Actually should accept even return rejected? No idea. 107 | return QDialog.Rejected 108 | 109 | return QDialog.accept(self, *args, **kwargs) 110 | 111 | def reset(self): 112 | """ 113 | Set defaults and empty the input text 114 | """ 115 | self.regex_compiled = re.compile("") 116 | self.regex_input.setText("") 117 | self.regex_box.set_defaults() 118 | self.option_search_directories.setCheckState(Qt.CheckState.Unchecked) 119 | self.option_full_path.setCheckState(Qt.CheckState.Checked) 120 | -------------------------------------------------------------------------------- /PyPoE/ui/shared/file/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shared UI code for file handling 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/ui/shared/__init__.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 6da613543dc70a3ec41224482edc53cafcfc4285 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Shared UI code for handling different file types and displaying them as 21 | QWidgets. 22 | 23 | Agreement 24 | =============================================================================== 25 | 26 | See PyPoE/LICENSE 27 | """ 28 | 29 | # ============================================================================= 30 | # Imports 31 | # ============================================================================= 32 | 33 | # Python 34 | 35 | # 3rd-party 36 | 37 | # self 38 | 39 | # ============================================================================= 40 | # Globals 41 | # ============================================================================= 42 | 43 | __all__ = [] 44 | 45 | # ============================================================================= 46 | # Classes 47 | # ============================================================================= 48 | 49 | # ============================================================================= 50 | # Functions 51 | # ============================================================================= 52 | -------------------------------------------------------------------------------- /PyPoE/ui/shared/table_context_menus.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | PyPoE/ui/shared/table_context_menus.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 0b78ceae529624da81b13cda6afc70a2b3f5a745 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # 3rd-party 35 | from PySide2.QtCore import * 36 | from PySide2.QtWidgets import * 37 | 38 | # self 39 | 40 | # ============================================================================= 41 | # Globals 42 | # ============================================================================= 43 | 44 | __all__ = ["TableContextReadOnlyMenu"] 45 | 46 | # ============================================================================= 47 | # Classes 48 | # ============================================================================= 49 | 50 | 51 | class TableContextReadOnlyMenu(QMenu): 52 | def __init__(self, *args, **kwargs): 53 | QMenu.__init__(self, *args, **kwargs) 54 | 55 | qtableview = self.parent() 56 | qtableview.setContextMenuPolicy(Qt.CustomContextMenu) 57 | qtableview.customContextMenuRequested.connect(self.popup) 58 | 59 | self.action_copy = self.addAction("Copy", self.copy) 60 | 61 | self.point = None 62 | 63 | def _handle_data(self, data): 64 | return str(data) 65 | 66 | def copy(self, *args, **kwargs): 67 | qmodelindexlist = self.parent().selectionModel().selectedIndexes() 68 | 69 | rmin, rmax, cmin, cmax = 2**32, 0, 2**32, 0 70 | 71 | for qmodelindex in qmodelindexlist: 72 | rid = qmodelindex.row() 73 | cid = qmodelindex.column() 74 | 75 | rmin = min(rmin, rid) 76 | rmax = max(rmax, rid) 77 | cmin = min(cmin, cid) 78 | cmax = max(cmax, cid) 79 | 80 | # +1 to offset for the minimum size 81 | matrix = [[None for j in range(0, cmax - cmin + 1)] for i in range(0, rmax - rmin + 1)] 82 | 83 | for qmodelindex in qmodelindexlist: 84 | matrix[qmodelindex.row() - rmin][qmodelindex.column() - cmin] = self._handle_data( 85 | qmodelindex.data() 86 | ) 87 | 88 | # Transform the matrix into a string 89 | out = [] 90 | for row in matrix: 91 | out.append("\t".join(["" if cell is None else cell for cell in row])) 92 | 93 | QApplication.clipboard().setText("\n".join(out)) 94 | 95 | def popup(self, point, *args, **kwargs): 96 | self.point = point 97 | QMenu.popup(self, self.parent().mapToGlobal(point)) 98 | 99 | 100 | # ============================================================================= 101 | # Functions 102 | # ============================================================================= 103 | -------------------------------------------------------------------------------- /docs/generate_rst_templates.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | genmodules.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: 939cf29892b2fd542c99c84b9cde6cf7a6b8239f $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | import os 32 | import re 33 | 34 | # self 35 | import PyPoE 36 | 37 | # 3rd-party 38 | 39 | 40 | # ============================================================================= 41 | # Globals 42 | # ============================================================================= 43 | 44 | __all__ = [] 45 | 46 | # ============================================================================= 47 | # Classes 48 | # ============================================================================= 49 | 50 | # ============================================================================= 51 | # Functions 52 | # ============================================================================= 53 | 54 | if __name__ == "__main__": 55 | curdir = os.path.split(__file__)[0] 56 | 57 | outpaths = [] 58 | 59 | for dirpath, dirnames, filenames in os.walk(PyPoE.DIR): 60 | for item in list(dirnames): 61 | if item.startswith(".") or item.startswith("__pycache"): 62 | dirnames.remove(item) 63 | 64 | for item in list(filenames): 65 | if item.startswith(".") or not item.endswith(".py"): 66 | filenames.remove(item) 67 | 68 | pypoe_path = dirpath.replace(PyPoE.DIR, "PyPoE").strip("\\/") 69 | for filename in filenames: 70 | path = re.split(r"\\|/", os.path.join(pypoe_path, filename)) 71 | if path[-1] == "__init__.py": 72 | del path[-1] 73 | 74 | path[-1] = path[-1].replace(".py", "") 75 | 76 | outpaths.append(".".join(path)) 77 | 78 | outpaths.sort() 79 | with open(os.path.join(curdir, "source", "autosummary.rst"), "w") as f: 80 | f.write(".. autosummary::\n") 81 | f.write(" :toctree: _autosummary\n \n") 82 | for path in outpaths: 83 | f.write(" %s\n" % path) 84 | -------------------------------------------------------------------------------- /docs/source/API/overview.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | ============================================================================== 3 | 4 | The API of PyPoE provides many classes and functions to access the proprietary 5 | file formats in an attempt to recreate and open source a lot of Path of Exile 6 | functionality for generic purpose tool programming. 7 | 8 | It is a good idea to take a look at the :ref:`modindex` to get started - the 9 | relevant modules are located under the :mod:`PyPoE.poe` tree. -------------------------------------------------------------------------------- /docs/source/API/structure.rst: -------------------------------------------------------------------------------- 1 | Project Structure 2 | ============================================================================== 3 | 4 | The project is organized in various folders 5 | 6 | +-------------+---------------------------------------------------------------+ 7 | |Directory |Description | 8 | +-------------+---------------------------------------------------------------+ 9 | |/ |The root project folder. Only the very core files such as the | 10 | | |setup file or the LICENSE file should reside here. | 11 | +-------------+---------------------------------------------------------------+ 12 | |/docs/ |The root folder for the documentation. | 13 | | |It contains scripts and build files as well as the source | 14 | | |subdirectory | 15 | +-------------+---------------------------------------------------------------+ 16 | |/docs/source |Source documentation files and description that are not | 17 | | |contained in the python source files themselves. | 18 | | |Also contains templates and some generated files. | 19 | +-------------+---------------------------------------------------------------+ 20 | |/scripts |Collection of scripts intended to be invoked from the command | 21 | | |line. | 22 | | |Unlike the other items, the scripts do not ahere to the same | 23 | | |quality standards | 24 | +-------------+---------------------------------------------------------------+ 25 | |/tests/ |Tests for py.test | 26 | +-------------+---------------------------------------------------------------+ 27 | |/PyPoE/ |Root python folder | 28 | +-------------+---------------------------------------------------------------+ 29 | |/PyPoE/_data/|Shared data used by the other files. | 30 | +-------------+---------------------------------------------------------------+ -------------------------------------------------------------------------------- /docs/source/CLI/config.rst: -------------------------------------------------------------------------------- 1 | Config 2 | ============================================================================== 3 | 4 | Config sub commands are accessible with: 5 | 6 | :command:`config ` 7 | 8 | The config will be located in the user folder. 9 | 10 | Linux: ~/.PyPoE 11 | 12 | Windows: %APPDATA%/PyPoE/ 13 | 14 | Commands 15 | ------------------------------------------------------------------------------ 16 | 17 | .. _cli-config-get: 18 | 19 | config get 20 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 21 | 22 | :command:`config get ` is used to get a config variable. 23 | 24 | :command:`config get -h` provides a list of available variables. 25 | Alternatively it is also possible to use the :ref:`cli-config-print_all` command. 26 | 27 | .. _cli-config-set: 28 | 29 | config set 30 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 31 | 32 | :command:`config set ` is used to get a config variable. 33 | 34 | :command:`config set -h` provides a list of available variables. 35 | Alternatively it is also possible to use the :ref:`cli-config-print_all` 36 | command. 37 | 38 | .. _cli-config-print_all: 39 | 40 | config print_all 41 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 42 | 43 | :command:`config print_all` is used to print a list of the currently 44 | registered config variables and their values. 45 | It will also show missing values. 46 | 47 | For example: 48 | 49 | .. code-block:: none 50 | 51 | 09:11:46 Current stored config variables: 52 | 09:11:46 distributor: DISTRIBUTOR.ALL 53 | 09:11:46 version: VERSION.STABLE 54 | 55 | 09:11:46 Missing config variables (require config set): 56 | 09:11:46 out_dir 57 | 09:11:46 temp_dir 58 | 59 | If you see an output similar to above, it is required to set the variables shown 60 | in the output with :ref:`cli-config-set`. 61 | 62 | For example: 63 | 64 | :command:`config set out_dir C:/Out` 65 | 66 | :command:`config set temp_dir C:/Temp` 67 | 68 | PyPoE Exporter config variables 69 | ------------------------------------------------------------------------------ 70 | 71 | distributor 72 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 73 | 74 | The distributor to use when scanning for a GGPK file. 75 | 76 | A list of valid distributors is located at 77 | :py:class:`PyPoE.poe.constants.DISTRIBUTOR` 78 | 79 | The variable accepts both numeric and literal values, however it is recommended 80 | to use the literal value. 81 | 82 | version 83 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 84 | 85 | The version to use when scanning for a GGPK file. 86 | 87 | A list of valid versions is located at 88 | :py:class:`PyPoE.poe.constants.VERSION` 89 | 90 | The variable accepts both numeric and literal values, however it is recommended 91 | to use the literal value. 92 | 93 | temp_dir 94 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 95 | 96 | The directory to extract temporary files to. This exists in order to speed 97 | up the commandline for certain actions. 98 | 99 | A change to this variable will require :ref:`setup-perform`. 100 | 101 | .. warning:: 102 | The selected directory should have enough space to hold the files. Above 103 | 500MB of free space is recommended. 104 | 105 | out_dir 106 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 107 | 108 | The default directory output files are written to. -------------------------------------------------------------------------------- /docs/source/CLI/dat.rst: -------------------------------------------------------------------------------- 1 | Dat Exporter 2 | ============================================================================== 3 | 4 | Setup sub commands are accessible with: 5 | 6 | :command:`dat ` 7 | 8 | Commands 9 | ------------------------------------------------------------------------------ 10 | 11 | dat json 12 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 13 | 14 | :command:`dat json [--files FILE [FILE ...]]` 15 | 16 | The dat json export is used to export .dat files to json format. 17 | 18 | The resulting json will roughly look like this: 19 | 20 | .. code-block:: none 21 | 22 | [ 23 | { 24 | // One of the files you've specified in the --files parameter 25 | "filename": "FILE", 26 | // headers in order from the dat specification 27 | "headers": [ 28 | { 29 | // name of the header 30 | 'name': 'Name', 31 | [ ... ] // other fields 32 | }, 33 | ... // other headers 34 | ] 35 | "data": [ 36 | // Each row 37 | [ ... ], 38 | ], 39 | }, 40 | ... // other files 41 | ] 42 | 43 | For details on what the extra fields mean in the headers, please see 44 | the dat specification file: 45 | https://github.com/OmegaK2/PyPoE/blob/dev/PyPoE/_data/dat.specification.ini -------------------------------------------------------------------------------- /docs/source/CLI/overview.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | ============================================================================== 3 | 4 | The command line interface of PyPoE features various utilities split up into 5 | command line scripts. 6 | 7 | Currently there are the following scripts: 8 | 9 | :command:`pypoe_exporter` 10 | Exporting functionality for the Path of Exile wiki and .dat files 11 | 12 | 13 | Invoking a script without any additional parameters will implicitly tell you 14 | about the available sub commands and command line options. 15 | However, it is possible to explicitly open the help by specifying :command:`-h` 16 | or :command:`--help`. 17 | 18 | So for example: 19 | 20 | :command:`pypoe_exporter -h` 21 | 22 | 23 | Each of the scripts comes with their own :ref:`CLI/config`. In order to use the 24 | scripts the config variables need to be set first, then the setup needs to be 25 | performed. -------------------------------------------------------------------------------- /docs/source/CLI/setup.rst: -------------------------------------------------------------------------------- 1 | Setup 2 | ============================================================================== 3 | 4 | Setup sub commands are accessible with: 5 | 6 | :command:`setup ` 7 | 8 | Commands 9 | ------------------------------------------------------------------------------ 10 | 11 | .. _setup-perform: 12 | 13 | setup perform 14 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 15 | :command:`setup peform` is used to perform the setup 16 | 17 | Depending on the type of setup and the speed of your computer this action may 18 | take a while. -------------------------------------------------------------------------------- /docs/source/CLI/wiki.rst: -------------------------------------------------------------------------------- 1 | Wiki Exporter 2 | ============================================================================== 3 | 4 | Setup sub commands are accessible with: 5 | 6 | :command:`wiki ` 7 | 8 | Commands 9 | ------------------------------------------------------------------------------ 10 | 11 | wiki 12 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -------------------------------------------------------------------------------- /docs/source/GUI/ggpk_viewer.rst: -------------------------------------------------------------------------------- 1 | GGPK Viewer 2 | =============================================================================== 3 | 4 | The GGPK Viewer is as the name suggests an utility to browse the contents of 5 | Path of Exile's content.ggpk. 6 | 7 | Features in a nutshell: 8 | 9 | * browsing the ggpk content file tree 10 | * searching for files using regular expressions (see :py:mod:`re`) 11 | * extracting of files or directories 12 | * live opening of various file formats, but in particular the proprietary .dat 13 | file format 14 | 15 | .. note:: 16 | Opening the GGPK may take several seconds depending on your disk and cpu 17 | speed. 18 | 19 | Viewing .dat files 20 | ------------------------------------------------------------------------------- 21 | 22 | The GGPK Viewer includes the ability to view the binary .dat files. 23 | 24 | They're parsed in a way that provides extra information about the entries 25 | for viewing purposes and in particular updating PyPoE's .dat specification. 26 | 27 | In particular information about pointers is kept intact and parsed into a table 28 | format adjusted , as such the dat viewer can be much slower then the API 29 | counter-part (i.e :class:`DatFile`). 30 | 31 | .. warning:: 32 | Opening large .dat files such as GrantedEffectsPerLevel.dat will 'hang' 33 | the UI until the processing is completed. 34 | 35 | As soon a .dat file is opening, filters can be applied to the individual 36 | rows by right-clicking the header columns. 37 | 38 | Columns can also be sorted by left-clicking on them. -------------------------------------------------------------------------------- /docs/source/GUI/overview.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | ============================================================================== 3 | 4 | The graphical user interface of PyPoE features various utilities split up into 5 | several applications. 6 | 7 | Those applications can be accessed though the launchpad, which can be run 8 | with: 9 | 10 | :command:`pypoe_ui` 11 | 12 | 13 | The individual applications (except the launchpad) can also be directly invoked 14 | through the command line by executing the top-level __init__.py file: 15 | 16 | :command:`python /PyPoE/ui//__init__.py` 17 | 18 | So for example: 19 | 20 | :command:`python /home/myuser/PyPoE/ui/ggpk_viewer/__init__.py` -------------------------------------------------------------------------------- /docs/source/_templates/autosummary/module.rst: -------------------------------------------------------------------------------- 1 | .. automodule:: {{fullname}} 2 | :no-members: 3 | :no-undoc-members: 4 | :no-special-members: 5 | :no-inherited-members: -------------------------------------------------------------------------------- /docs/source/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | 3 | {% block header %} 4 | {% if fullname is defined %} 5 | {% set parent_name = fullname.rsplit('.', 1)[0] %} 6 | {% if parent_name %} 7 | {{parent_name}} 8 | {% endif %} 9 | {% endif %} 10 | 11 | {% endblock %} -------------------------------------------------------------------------------- /docs/source/_templates/module.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/docs/source/_templates/module.html -------------------------------------------------------------------------------- /docs/source/contribution.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | =============================================================================== 3 | 4 | Everyone is generally welcome to contribute to the project. 5 | 6 | There are basically 3 ways to do so: 7 | 8 | * Feature requests & :doc:`reporting issues ` 9 | * Sending pull requests 10 | * Becoming a dev 11 | 12 | Pull requests 13 | ------------------------------------------------------------------------------- 14 | 15 | You're welcome to send a pull request on github for features or changes you 16 | think that should be in PyPoE. 17 | 18 | Please keep a few things in mind: 19 | 20 | * the code in the request should be well behaved and a proper fix or addition 21 | * the change should be under the MIT license 22 | * if it's an entirely new feature, it may debatable whether it is has a place 23 | in PyPoE 24 | * the submitted code should: 25 | * be well behaved and a proper fix or addition 26 | * be PEP8 compatible (minus the line-length) 27 | * include changes in the respective tests or new tests 28 | * validate against existing tests (if tests were changed, validate against 29 | those) 30 | * backwards incompatible changes are more suited for the dev branch 31 | 32 | So for example: 33 | 34 | * Likely to be accepted 35 | 36 | * general improvements to existing code of UI, CLI or API 37 | * new, well-behaved features that extend the existing functional 38 | * support for missing file formats 39 | * updated dat.specification.ini 40 | * additional tests 41 | 42 | * Likely to be rejected 43 | 44 | * changes unrelated to the goal of the project 45 | * refusal of making the change itself available under MIT license 46 | * changes with poor coding style 47 | 48 | Become a dev 49 | ------------------------------------------------------------------------------- 50 | 51 | If you want to become an active developer and meet the requirements please 52 | contact me. I'll manually unlock people for access to the repo. 53 | 54 | .. note:: 55 | 56 | If you just want to contribute a few changes, there is no need to become a 57 | dev and you can send pull requests instead. 58 | 59 | If you just want your own repo, just fork the project on github. 60 | 61 | **Requirements** 62 | 63 | * You should 64 | 65 | * have a good amount of experience with developing in Python 3 66 | * be willing to actively contribute, i.e. making changes on your own, working 67 | on the TODOs 68 | * be fluent in English (written); no you don't have to be a perfect speaker, 69 | but you're English should be good enough to have no issues with 70 | communication 71 | * be available in the IRC channel 72 | * I'll want to see some pieces of code you've written; a good history of 73 | pull requests will suffice, otherwise: 74 | 75 | * another open source project you've been involved in, 76 | * some private things you've written but are willing to let me have a look at 77 | 78 | It would be extra helpful, if you: 79 | 80 | * have experience with C/C++ and writing embedded python libraries with C/API 81 | * have a lot of experience with Path of Exile [for the api] 82 | * are experienced with using Mediawiki [for exporter] 83 | * speak other languages fluently or natively [for translating the project] 84 | * are adept with reverse engineering [to help with api] 85 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to PyPoE's documentation! 2 | ================================= 3 | 4 | PyPoE is collection of Python 3 based tools to work with Path of Exile. 5 | The target audience are both end users and developers. 6 | 7 | Goals of the Project 8 | ------------------------------------------------------------------------------ 9 | 10 | * provide a mostly complete and stable python3 api to work with Path of Exile 11 | and it's files 12 | * have a stable and useful UI binding to browse the files 13 | * provide CLI tools for extracting files for convince 14 | * provide utilities to publish the data on the Path of Exile wiki 15 | 16 | Core Features 17 | ------------------------------------------------------------------------------ 18 | 19 | * Development Library 20 | * Graphical User Interface (UI or GUI) 21 | * Command Line Interface (CLI) 22 | 23 | 24 | Getting Started 25 | ============================================================================== 26 | 27 | .. toctree:: 28 | 29 | installation 30 | report_issue 31 | contribution 32 | 33 | GUI 34 | ============================================================================== 35 | 36 | .. toctree:: 37 | 38 | GUI/overview 39 | GUI/ggpk_viewer 40 | 41 | CLI 42 | ============================================================================== 43 | 44 | .. toctree:: 45 | 46 | CLI/overview 47 | CLI/config 48 | CLI/setup 49 | CLI/dat 50 | CLI/wiki 51 | 52 | Developer Overview 53 | ============================================================================== 54 | 55 | .. toctree:: 56 | 57 | API/overview 58 | API/structure 59 | 60 | Indices and tables 61 | ================== 62 | 63 | * :ref:`genindex` 64 | * :ref:`modindex` 65 | * :ref:`search` 66 | 67 | -------------------------------------------------------------------------------- /docs/source/report_issue.rst: -------------------------------------------------------------------------------- 1 | Reporting an issue 2 | =============================================================================== 3 | 4 | First please make sure whether is unknown/not reported yet: 5 | 6 | * Validate it is an issue on PyPoE's end and not just a coding mistake on your 7 | end 8 | * Make sure you are running the latest version of PyPoE 9 | * Check open github issues: https://github.com/OmegaK2/PyPoE/issues 10 | * Check the "TODO"s in the affected file or function 11 | 12 | After taking those steps please report the issue to github and provide as 13 | much information as you can: 14 | 15 | * Title: Short, but definitive description of the bug 16 | * Description: Detailed description of the bug, in particular steps on how to 17 | reproduce it 18 | * if available, provide the relevant python traceback 19 | * if available, provide the relevant code that caused the bug 20 | 21 | You can also submit pull requests that to help fix bugs, I'll review (and 22 | possibly) edit them. You agree they'll placed under the MIT license (for more 23 | info see :doc:`contribution`) -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 100 3 | target-version = ["py311"] 4 | 5 | [tool.isort] 6 | profile = "black" 7 | 8 | [tool.poetry] 9 | name = "PyPoE" 10 | version = "1.1.0" 11 | description = "A suite of tools to export game data for Path of Exile" 12 | authors = ["Your Name "] 13 | license = "MIT" 14 | readme = "README.md" 15 | packages = [ 16 | { include = "PyPoE", from = "." }, 17 | ] 18 | 19 | [tool.poetry.scripts] 20 | pypoe_exporter = 'PyPoE.cli.exporter.core:main' 21 | pypoe_schema_import = 'PyPoE.poe.file.specification.generation.import_dat_schema:main' 22 | 23 | [tool.poetry.dependencies] 24 | python = "^3.11" 25 | configobj = "^5.0.6" 26 | colorama = "^0.4.6" 27 | tqdm = "^4.64.1" 28 | mwclient = "^0.11.0" 29 | mwparserfromhell = "^0.6.4" 30 | rapidfuzz = "^3.11.0" 31 | pydds = "~0.0.5" 32 | pyooz = "~0.0.6" 33 | fnvhash = "^0.1.0" 34 | brotli = "^1.0.9" 35 | matplotlib = "^3.8.2" 36 | numpy = "^2.2.1" 37 | 38 | 39 | [tool.poetry.group.dev.dependencies] 40 | isort = "^5.11.4" 41 | black = "^22.12.0" 42 | flake8 = "^6.0.0" 43 | pytest = "^7.2.0" 44 | pre-commit = "^2.21.0" 45 | 46 | [build-system] 47 | requires = ["poetry-core"] 48 | build-backend = "poetry.core.masonry.api" 49 | -------------------------------------------------------------------------------- /scripts/make_empty_spec.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | scripts/make_empty_spec.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: 81ebbc576e30e67bfe13fda844caaa3ed6d59045 $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | 25 | Documentation 26 | =============================================================================== 27 | 28 | Public API 29 | ------------------------------------------------------------------------------- 30 | 31 | Internal API 32 | ------------------------------------------------------------------------------- 33 | """ 34 | 35 | # ============================================================================= 36 | # Imports 37 | # ============================================================================= 38 | 39 | # Python 40 | import os 41 | import struct 42 | 43 | # self 44 | from PyPoE.poe.constants import DISTRIBUTOR, VERSION 45 | from PyPoE.poe.file import dat, specification 46 | from PyPoE.poe.file.bundle import Index 47 | from PyPoE.poe.file.ggpk import GGPKFile 48 | from PyPoE.poe.path import PoEPath 49 | 50 | # 3rd-party 51 | 52 | 53 | # ============================================================================= 54 | # Globals 55 | # ============================================================================= 56 | 57 | __all__ = [] 58 | 59 | # ============================================================================= 60 | # Classes 61 | # ============================================================================= 62 | 63 | # ============================================================================= 64 | # Functions 65 | # ============================================================================= 66 | 67 | 68 | def spec_unknown(size, i=0): 69 | if size == 0: 70 | return "" 71 | spec = "Field(" 72 | out = [] 73 | while size >= 4: 74 | out.append(spec) 75 | out.append(" name='Unknown%s'," % i) 76 | out.append(" type='int',") 77 | out.append("),") 78 | size -= 4 79 | i += 1 80 | 81 | mod = size % 4 82 | for j in range(0, mod): 83 | out.append(spec) 84 | out.append(" name='Unknown%s'," % i) 85 | out.append(" type='byte',") 86 | out.append("),") 87 | i += 1 88 | 89 | return " " * 12 + ("\n" + " " * 12).join(out) 90 | 91 | 92 | def run(): 93 | out = [] 94 | 95 | path = PoEPath(version=VERSION.STABLE, distributor=DISTRIBUTOR.GGG).get_installation_paths()[0] 96 | existing_set = set(specification.load(VERSION.STABLE).keys()) 97 | 98 | ggpk = GGPKFile() 99 | ggpk.read(os.path.join(path, "content.ggpk")) 100 | ggpk.directory_build() 101 | 102 | index = Index() 103 | index.read(ggpk[Index.PATH].record.extract()) 104 | 105 | file_set = set() 106 | 107 | for name in index.get_dir_record("Data").files: 108 | if not name.endswith(".dat"): 109 | continue 110 | 111 | # Not a regular dat file, ignore 112 | if name in ["Languages.dat"]: 113 | continue 114 | 115 | file_set.add(name) 116 | 117 | new = sorted(file_set.difference(set(existing_set))) 118 | 119 | for fn in new: 120 | fr = index.get_file_record("Data/" + fn) 121 | fr.bundle.read(ggpk[fr.bundle.ggpk_path].record.extract()) 122 | binary = fr.get_file() 123 | data_offset = binary.find(dat.DAT_FILE_MAGIC_NUMBER) 124 | n_rows = struct.unpack(" 0: 127 | record_length = length // n_rows 128 | 129 | out.append( 130 | """ '%s': File( 131 | fields=( 132 | %s 133 | ), 134 | ),""" 135 | % (fn, spec_unknown(record_length)) 136 | ) 137 | 138 | print("\n".join(out)) 139 | 140 | 141 | if __name__ == "__main__": 142 | run() 143 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/Metadata/StatDescriptions/descriptions_base.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/tests/PyPoE/poe/file/_data/Metadata/StatDescriptions/descriptions_base.txt -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/Metadata/StatDescriptions/descriptions_extended.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/tests/PyPoE/poe/file/_data/Metadata/StatDescriptions/descriptions_extended.txt -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/keyvalues.kv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/tests/PyPoE/poe/file/_data/keyvalues.kv -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/keyvalues_base.kv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/tests/PyPoE/poe/file/_data/keyvalues_base.kv -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/keyvalues_write.kv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/tests/PyPoE/poe/file/_data/keyvalues_write.kv -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/invalid_argument_combination.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: 22beaf4c266eb8413977141078c61ec51015c01d $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | # 3rd-party 32 | 33 | # self 34 | from PyPoE.poe.file.specification.fields import Field, File, Specification 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | specification = Specification( 41 | { 42 | "Main.dat": File( 43 | fields=( 44 | Field( 45 | name="Invalid", 46 | type="int", 47 | key="Other.dat", 48 | enum="MOD_DOMAIN", 49 | ), 50 | ), 51 | ), 52 | "Other.dat": File( 53 | fields=(), 54 | ), 55 | } 56 | ) 57 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/invalid_enum_name.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: 039f4606217ab90a89ab7f771603c62254881a7c $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | # 3rd-party 32 | 33 | # self 34 | from PyPoE.poe.file.specification.fields import Field, File, Specification 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | specification = Specification( 41 | { 42 | "Main.dat": File( 43 | fields=( 44 | Field( 45 | name="Invalid", 46 | type="int", 47 | enum="THIS_DOES_NOT_EXIST", 48 | ), 49 | ), 50 | ), 51 | } 52 | ) 53 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/invalid_foreign_key_file.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: da11488121fb312999dd69c8592f2a8f2ef70ffe $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | # 3rd-party 32 | 33 | # self 34 | from PyPoE.poe.file.specification.fields import Field, File, Specification 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | specification = Specification( 41 | { 42 | "Main.dat": File( 43 | fields=( 44 | Field( 45 | name="Missing", 46 | type="int", 47 | key="Missing.dat", 48 | ), 49 | ), 50 | ), 51 | } 52 | ) 53 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/invalid_foreign_key_id.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: 7945e389c354c4e4753b4a6a23860a2393a82e9d $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | # 3rd-party 32 | 33 | # self 34 | from PyPoE.poe.file.specification.fields import Field, File, Specification 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | specification = Specification( 41 | { 42 | "Main.dat": File( 43 | fields=( 44 | Field( 45 | name="ForeignKey", 46 | type="int", 47 | key="Other.dat", 48 | key_id="Missing", 49 | ), 50 | ), 51 | ), 52 | "Other.dat": File( 53 | fields=(), 54 | ), 55 | } 56 | ) 57 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/rr_test.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: bb0e465fc3687e4359cdb287b6f20f5050e9bcb4 $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | 32 | # 3rd-party 33 | 34 | # self 35 | from PyPoE.poe.file.specification.fields import Field, File, Specification 36 | 37 | # ============================================================================= 38 | # Globals 39 | # ============================================================================= 40 | 41 | specification = Specification( 42 | { 43 | "Main.dat": File( 44 | fields=( 45 | Field( 46 | name="ForeignKey", 47 | type="int", 48 | key="Other.dat", 49 | ), 50 | Field( 51 | name="ForeignKeyOffset", 52 | type="int", 53 | key="Other.dat", 54 | key_offset=1, 55 | ), 56 | Field( 57 | name="ForeignKeyMismatch", 58 | type="int", 59 | ), 60 | Field( 61 | name="ForeignKeyNone", 62 | type="int", 63 | key="Other.dat", 64 | ), 65 | Field( 66 | name="ForeignKeyCellValue", 67 | type="int", 68 | key="Other.dat", 69 | key_id="Value", 70 | ), 71 | Field( 72 | name="ConstTest", 73 | type="int", 74 | enum="MOD_DOMAIN", 75 | ), 76 | ), 77 | ), 78 | "Other.dat": File( 79 | fields=( 80 | Field( 81 | name="Value", 82 | type="int", 83 | unique=True, 84 | ), 85 | ), 86 | ), 87 | } 88 | ) 89 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/runtime_missing_foreign_key1.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: 2f9449468a6bacdc8cc14fd39ade97b2ff89dc36 $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | # 3rd-party 32 | 33 | # self 34 | from PyPoE.poe.file.specification.fields import Field, File, Specification 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | specification = Specification( 41 | { 42 | "Main.dat": File( 43 | fields=( 44 | Field( 45 | name="ForeignKey", 46 | type="int", 47 | ), 48 | Field( 49 | name="ForeignKeyOffset", 50 | type="int", 51 | ), 52 | Field( 53 | name="ForeignKeyMismatch", 54 | type="int", 55 | key="Other.dat", 56 | ), 57 | Field( 58 | name="ForeignKeyNone", 59 | type="int", 60 | ), 61 | Field( 62 | name="ForeignKeyCellValue", 63 | type="int", 64 | ), 65 | Field( 66 | name="ConstTest", 67 | type="int", 68 | ), 69 | ), 70 | ), 71 | "Other.dat": File( 72 | fields=( 73 | Field( 74 | name="Value", 75 | type="int", 76 | unique=True, 77 | ), 78 | ), 79 | ), 80 | } 81 | ) 82 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/runtime_missing_foreign_key2.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: 808c8a1a563e7e33beea1e352175220edc3dff9a $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | # 3rd-party 32 | 33 | # self 34 | from PyPoE.poe.file.specification.fields import Field, File, Specification 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | specification = Specification( 41 | { 42 | "Main.dat": File( 43 | fields=( 44 | Field( 45 | name="ForeignKey", 46 | type="int", 47 | key="Other.dat", 48 | key_id="Value", 49 | ), 50 | Field( 51 | name="ForeignKeyOffset", 52 | type="int", 53 | ), 54 | Field( 55 | name="ForeignKeyMismatch", 56 | type="int", 57 | ), 58 | Field( 59 | name="ForeignKeyNone", 60 | type="int", 61 | ), 62 | Field( 63 | name="ForeignKeyCellValue", 64 | type="int", 65 | ), 66 | Field( 67 | name="ConstTest", 68 | type="int", 69 | ), 70 | ), 71 | ), 72 | "Other.dat": File( 73 | fields=( 74 | Field( 75 | name="Value", 76 | type="int", 77 | unique=True, 78 | ), 79 | ), 80 | ), 81 | } 82 | ) 83 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/runtime_rowsize_mismatch.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: 7cacac28799f4762d005785ff7269f0b7b9ce067 $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | # 3rd-party 32 | 33 | # self 34 | from PyPoE.poe.file.specification.fields import Field, File, Specification 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | specification = Specification( 41 | { 42 | "Main.dat": File( 43 | fields=( 44 | Field( 45 | name="One", 46 | type="int", 47 | ), 48 | ), 49 | ), 50 | } 51 | ) 52 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/virtual_key_duplicate.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: 7fcbe068648c7a0295f52f142fbc027fe9a43012 $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | # 3rd-party 32 | 33 | # self 34 | from PyPoE.poe.file.specification.fields import Field, File, Specification, VirtualField 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | specification = Specification( 41 | { 42 | "Main.dat": File( 43 | fields=( 44 | Field( 45 | name="One", 46 | type="int", 47 | ), 48 | Field( 49 | name="Two", 50 | type="int", 51 | ), 52 | Field( 53 | name="Three", 54 | type="int", 55 | ), 56 | Field( 57 | name="Four", 58 | type="int", 59 | ), 60 | ), 61 | virtual_fields=( 62 | VirtualField( 63 | name="One", 64 | fields=("Two", "Three"), 65 | ), 66 | ), 67 | ), 68 | } 69 | ) 70 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/virtual_key_empty.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: 66930417de93e017b7f1903f38f32cffb2819b8c $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | # 3rd-party 32 | 33 | # self 34 | from PyPoE.poe.file.specification.fields import Field, File, Specification, VirtualField 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | specification = Specification( 41 | { 42 | "Main.dat": File( 43 | fields=( 44 | Field( 45 | name="One", 46 | type="int", 47 | ), 48 | Field( 49 | name="Two", 50 | type="int", 51 | ), 52 | Field( 53 | name="Three", 54 | type="int", 55 | ), 56 | Field( 57 | name="Four", 58 | type="int", 59 | ), 60 | ), 61 | virtual_fields=( 62 | VirtualField( 63 | name="Empty", 64 | fields="", 65 | ), 66 | ), 67 | ), 68 | } 69 | ) 70 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/virtual_key_invalid_data_type.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: 9580a95cdb31b37ed07b68b320f9395e630ba0b1 $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | # 3rd-party 32 | 33 | # self 34 | from PyPoE.poe.file.specification.fields import Field, File, Specification, VirtualField 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | specification = Specification( 41 | { 42 | "Main.dat": File( 43 | fields=( 44 | Field( 45 | name="One", 46 | type="ref|list|int", 47 | ), 48 | Field( 49 | name="Two", 50 | type="int", 51 | ), 52 | Field( 53 | name="Three", 54 | type="int", 55 | ), 56 | Field( 57 | name="Four", 58 | type="int", 59 | ), 60 | ), 61 | virtual_fields=( 62 | VirtualField( 63 | name="Virtual", 64 | fields=("One", "Two"), 65 | zip=True, 66 | ), 67 | ), 68 | ), 69 | } 70 | ) 71 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/specifications/virtual_key_invalid_key.py: -------------------------------------------------------------------------------- 1 | """Overview 2 | =============================================================================== 3 | 4 | +----------+------------------------------------------------------------------+ 5 | | Path | tests/PyPoE/poe/file/_data/specifications/ | 6 | +----------+------------------------------------------------------------------+ 7 | | Version | 1.0.0a0 | 8 | +----------+------------------------------------------------------------------+ 9 | | Revision | $Id: 447e8ddd009422d3881c7fd825994501545afd3b $ | 10 | +----------+------------------------------------------------------------------+ 11 | | Author | Omega_K2 | 12 | +----------+------------------------------------------------------------------+ 13 | 14 | Description 15 | =============================================================================== 16 | 17 | Specification used for tests 18 | 19 | Agreement 20 | =============================================================================== 21 | 22 | See PyPoE/LICENSE 23 | 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | # 3rd-party 32 | 33 | # self 34 | from PyPoE.poe.file.specification.fields import Field, File, Specification, VirtualField 35 | 36 | # ============================================================================= 37 | # Globals 38 | # ============================================================================= 39 | 40 | specification = Specification( 41 | { 42 | "Main.dat": File( 43 | fields=( 44 | Field( 45 | name="One", 46 | type="int", 47 | ), 48 | Field( 49 | name="Two", 50 | type="int", 51 | ), 52 | Field( 53 | name="Three", 54 | type="int", 55 | ), 56 | Field( 57 | name="Four", 58 | type="int", 59 | ), 60 | ), 61 | virtual_fields=( 62 | VirtualField( 63 | name="Virtual", 64 | fields=("Five", "Six"), 65 | ), 66 | ), 67 | ), 68 | } 69 | ) 70 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/test.idl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/tests/PyPoE/poe/file/_data/test.idl -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/test.idt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/tests/PyPoE/poe/file/_data/test.idt -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/_data/test_write.idl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Path-of-Exile-Wiki/PyPoE/6395c6c7635aba81baa0c59b7df34b6d7af443b0/tests/PyPoE/poe/file/_data/test_write.idl -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/test_ggpk.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for PyPoE.poe.file.ggpk 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | tests/PyPoE/poe/file/test_ggpk.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: fceb657c1d31a45730dfdabd0fd091088c82d3d9 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Tests for ggpk.py 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | 27 | .. todo:: 28 | 29 | * The entire test I suppose 30 | """ 31 | 32 | # ============================================================================= 33 | # Imports 34 | # ============================================================================= 35 | 36 | # Python 37 | 38 | # 3rd Party 39 | 40 | # self 41 | 42 | # ============================================================================= 43 | # Setup 44 | # ============================================================================= 45 | 46 | DDS_UNCOMPRESSED = "Art/Textures/Characters/Adventurer/adventurerPalid_colour.dds" 47 | DDS_COMPRESSED = "Art/2DArt/BuffIcons/AssassinsMark.dds" 48 | 49 | # ============================================================================= 50 | # Tests 51 | # ============================================================================= 52 | 53 | 54 | # These tests will raise errors if something is wrong, like decompression 55 | # errors 56 | class TestDDSExtract: 57 | def test_uncompressed(self, file_system): 58 | file_system.extract_dds(file_system.get_file(DDS_UNCOMPRESSED)) 59 | 60 | def test_compressed(self, file_system): 61 | file_system.extract_dds(file_system.get_file(DDS_COMPRESSED)) 62 | 63 | def test_reference(self, file_system): 64 | data = b"*" + DDS_COMPRESSED.encode("ascii") 65 | file_system.extract_dds(data) 66 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/test_idt.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for PyPoE.poe.file.idt 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | tests/PyPoE/poe/file/test_idt.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: b1346d85ac74a52b8084f5637d927de60d9df5b3 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Tests for PyPoE/tests/poe/file/test_idt.py 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | import os 34 | from tempfile import TemporaryDirectory 35 | 36 | # 3rd-party 37 | import pytest 38 | 39 | # self 40 | from PyPoE.poe.file import idt 41 | 42 | # ============================================================================= 43 | # Setup 44 | # ============================================================================= 45 | 46 | cur_dir = os.path.split(os.path.realpath(__file__))[0] 47 | idl_path = os.path.join(cur_dir, "_data", "test.idt") 48 | 49 | # ============================================================================= 50 | # Fixtures 51 | # ============================================================================= 52 | 53 | data = { 54 | "version": 1, 55 | "image": "Art/2DItems/Test.dds", 56 | "records": [ 57 | { 58 | "name": "Blade", 59 | "records": [ 60 | {"x": 1, "y": 1}, 61 | {"x": 2, "y": 2}, 62 | ], 63 | }, 64 | { 65 | "name": "Handle", 66 | "records": [ 67 | {"x": 1, "y": 1}, 68 | {"x": 2, "y": 2}, 69 | {"x": 3, "y": 3}, 70 | {"x": 4, "y": 4}, 71 | ], 72 | }, 73 | ], 74 | } 75 | 76 | 77 | @pytest.fixture 78 | def idt_file(): 79 | return idt.IDTFile(data) 80 | 81 | 82 | # ============================================================================= 83 | # Tests 84 | # ============================================================================= 85 | 86 | 87 | class TestIDLFile: 88 | def eq(self, idt_file): 89 | assert idt_file.version == data["version"] 90 | assert idt_file.image == data["image"] 91 | for i, tex in enumerate(data["records"]): 92 | assert idt_file.records[i].name == tex["name"] 93 | for j, coord in enumerate(tex["records"]): 94 | assert idt_file.records[i].records[j].x == coord["x"] 95 | assert idt_file.records[i].records[j].y == coord["y"] 96 | 97 | def test_init(self, idt_file): 98 | self.eq(idt_file) 99 | 100 | def test_read(self, idt_file): 101 | idt_file2 = idt.IDTFile() 102 | idt_file2.read(idl_path) 103 | 104 | self.eq(idt_file) 105 | 106 | def test_write(self, idt_file): 107 | idt_file2 = idt.IDTFile() 108 | 109 | with TemporaryDirectory() as d: 110 | tmp_path = os.path.join(d, "test_write.idt") 111 | idt_file.write(tmp_path) 112 | idt_file2.read(tmp_path) 113 | 114 | self.eq(idt_file2) 115 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/test_ot.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for PyPoE.poe.file.ot 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | tests/PyPoE/poe/file/test_ot.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 9979bf2185be836ac83916aa17424e6350ad1926 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Tests for ot.py 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | 27 | TODO 28 | =============================================================================== 29 | 30 | - The entire test I suppose 31 | """ 32 | 33 | # ============================================================================= 34 | # Imports 35 | # ============================================================================= 36 | 37 | # self 38 | 39 | # ============================================================================= 40 | # Setup 41 | # ============================================================================= 42 | 43 | # ============================================================================= 44 | # Tests 45 | # ============================================================================= 46 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/test_psg.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for PyPoE.poe.file.psg 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | tests/PyPoE/poe/file/test_psg.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: ee67ef128eb113480f6a274025a7fec169fbc572 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | from PyPoE.poe.file import psg 34 | 35 | # self 36 | 37 | # ============================================================================= 38 | # Setup 39 | # ============================================================================= 40 | 41 | # ============================================================================= 42 | # Fixtures 43 | # ============================================================================= 44 | 45 | # ============================================================================= 46 | # Tests 47 | # ============================================================================= 48 | 49 | 50 | def test_psg(file_system, rr): 51 | f = psg.PSGFile(passive_skills_dat_file=rr) 52 | f.read(file_system.get_file("Metadata/PassiveSkillGraph.psg")) 53 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/file/test_stat_filters.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for PyPoE.poe.file.stat_filters 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | tests/PyPoE/poe/file/test_stat_filters.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 4066c49503618c89976e7e2ffbc78493a58bd012 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | from PyPoE.poe.file import stat_filters 34 | 35 | # self 36 | 37 | # ============================================================================= 38 | # Setup 39 | # ============================================================================= 40 | 41 | # ============================================================================= 42 | # Fixtures 43 | # ============================================================================= 44 | 45 | # ============================================================================= 46 | # Tests 47 | # ============================================================================= 48 | 49 | 50 | def test_stat_filter_file(file_system): 51 | f = stat_filters.StatFilterFile() 52 | f.read(file_system.get_file("Metadata/StatDescriptions/skillpopup_stat_filters.txt")) 53 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/test_path.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for PyPoE.poe.path 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | tests/PyPoE/poe/test_path.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: f2e6bca2b0a393296604e48d61230b1c483d7bc2 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Tests for path.py 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | 27 | TODO 28 | =============================================================================== 29 | 30 | - The entire test I suppose 31 | """ 32 | 33 | # ============================================================================= 34 | # Imports 35 | # ============================================================================= 36 | 37 | # ============================================================================= 38 | # Setup 39 | # ============================================================================= 40 | 41 | # ============================================================================= 42 | # Tests 43 | # ============================================================================= 44 | -------------------------------------------------------------------------------- /tests/PyPoE/poe/test_text.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | tests/PyPoE/poe/test_text.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: e4c40ad0200157a28bf4fccaecb523fbd03cc75e $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | Tests for PyPoE.poe.text 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | from functools import partial 32 | 33 | # 3rd-party 34 | import pytest 35 | 36 | # self 37 | from PyPoE.poe import text 38 | 39 | # ============================================================================= 40 | # Setup 41 | # ============================================================================= 42 | 43 | 44 | def sample_parser(**kwargs): 45 | if kwargs["parameter"]: 46 | return '<%(id)s attr="%(parameter)s">%(hstr)s' % kwargs 47 | else: 48 | return "<%(id)s>%(hstr)s" % kwargs 49 | 50 | 51 | # ============================================================================= 52 | # Fixtures 53 | # ============================================================================= 54 | 55 | # ============================================================================= 56 | # Tests 57 | # ============================================================================= 58 | 59 | 60 | class TestTags: 61 | sample_strings = [ 62 | ( 63 | "Basic", 64 | "Basic", 65 | [], 66 | ), 67 | # Basic functionality 68 | ( 69 | "Test {test} part 2 {more test}", 70 | 'Test test part 2 more test', 71 | ["item"], 72 | ), 73 | # nested values 74 | ( 75 | "{{<size:45>{test}}}", 76 | '<bold><title><size attr="45">test</size>', 77 | ["bold", "title", "size"], 78 | ), 79 | ( 80 | "Test <>", 81 | "Test <>", 82 | [], 83 | ), 84 | ( 85 | "Format string {0} test", 86 | "Format string {0} test", 87 | [], 88 | ), 89 | ] 90 | 91 | @pytest.mark.parametrize("input,output,handler_ids", sample_strings) 92 | def test_parsing_results(self, input, output, handler_ids): 93 | handlers = {hid: partial(sample_parser, id=hid) for hid in handler_ids} 94 | 95 | tag = text.parse_description_tags(input) 96 | assert tag.handle_tags(handlers=handlers) == output 97 | -------------------------------------------------------------------------------- /tests/PyPoE/shared/config/test_validator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Overview 3 | =============================================================================== 4 | 5 | +----------+------------------------------------------------------------------+ 6 | | Path | test_validator.py | 7 | +----------+------------------------------------------------------------------+ 8 | | Version | 1.0.0a0 | 9 | +----------+------------------------------------------------------------------+ 10 | | Revision | $Id: fc57ed2e9fb1c06fc8a84259c1f7a59af1d9c8e3 $ | 11 | +----------+------------------------------------------------------------------+ 12 | | Author | Omega_K2 | 13 | +----------+------------------------------------------------------------------+ 14 | 15 | Description 16 | =============================================================================== 17 | 18 | 19 | 20 | Agreement 21 | =============================================================================== 22 | 23 | See PyPoE/LICENSE 24 | """ 25 | 26 | # ============================================================================= 27 | # Imports 28 | # ============================================================================= 29 | 30 | # Python 31 | import os 32 | from enum import IntEnum 33 | 34 | # 3rd-party 35 | import pytest 36 | from validate import ValidateError 37 | 38 | # self 39 | from PyPoE.shared.config import validator 40 | 41 | # ============================================================================= 42 | # Setup 43 | # ============================================================================= 44 | 45 | 46 | class MyIntEnum(IntEnum): 47 | a = 1 48 | b = 2 49 | c = 3 50 | 51 | 52 | # ============================================================================= 53 | # Fixtures 54 | # ============================================================================= 55 | 56 | 57 | @pytest.fixture(scope="module") 58 | def int_enum_tester(): 59 | return validator.IntEnumValidator(enum=MyIntEnum, default=MyIntEnum.a) 60 | 61 | 62 | @pytest.fixture(scope="module") 63 | def file_path(): 64 | return __file__ 65 | 66 | 67 | @pytest.fixture(scope="module") 68 | def dir_path(): 69 | return os.path.split(__file__)[0] 70 | 71 | 72 | # ============================================================================= 73 | # Tests 74 | # ============================================================================= 75 | 76 | 77 | class TestIntEnumValidator: 78 | values = ( 79 | (1, MyIntEnum.a), 80 | ("a", MyIntEnum.a), 81 | ("1", MyIntEnum.a), 82 | # Will be written as this value into config 83 | ("MyIntEnum.a", MyIntEnum.a), 84 | ) 85 | 86 | fvalues = ( 87 | ("4", "4 (str) is out of range and should fail"), 88 | ("2.0", "2.0 (str) is not a valid integer"), 89 | (4, "4 (int) is out of range and should fail"), 90 | (2.0, "2.0 (float) is not a valid integer"), 91 | ) 92 | 93 | def test_init(self, int_enum_tester): 94 | with pytest.raises(TypeError): 95 | validator.IntEnumValidator(enum=int) 96 | validator.IntEnumValidator(enum=MyIntEnum, default=5) 97 | validator.IntEnumValidator(enum=MyIntEnum, default="c") 98 | validator.IntEnumValidator(enum=MyIntEnum, default=int) 99 | 100 | @pytest.mark.parametrize("value,expected", values) 101 | def test_success(self, int_enum_tester, value, expected): 102 | assert int_enum_tester(value) == expected 103 | 104 | @pytest.mark.parametrize("value,errmsg", fvalues) 105 | def test_fail(self, int_enum_tester, value, errmsg): 106 | with pytest.raises(ValidateError): 107 | int_enum_tester(value) 108 | 109 | 110 | class TestIsFile: 111 | def test_success(self, file_path): 112 | assert validator.is_file(file_path) == file_path 113 | 114 | @pytest.mark.xfail(ValidateError) 115 | def test_fail(self, dir_path): 116 | validator.is_file(dir_path) 117 | 118 | 119 | class TestIsDirectory: 120 | def test_success(self, dir_path): 121 | assert validator.is_directory(dir_path) == dir_path 122 | 123 | @pytest.mark.xfail(ValidateError) 124 | def test_fail(self, file_path): 125 | validator.is_directory(file_path) 126 | -------------------------------------------------------------------------------- /tests/PyPoE/shared/test_mixins.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | tests/PyPoE/shared/test_mixins.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 18d91dba332014fbee43c8dbe3afa33b3f58c089 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | from collections import OrderedDict 34 | 35 | # 3rd-party 36 | import pytest 37 | 38 | # self 39 | from PyPoE.shared import mixins 40 | 41 | # ============================================================================= 42 | # Setup 43 | # ============================================================================= 44 | 45 | 46 | class Repr(mixins.ReprMixin): 47 | def __init__(self, a, b=42): 48 | self.a = a 49 | self._b = b 50 | self.extra = 1337 51 | 52 | 53 | # ============================================================================= 54 | # Tests 55 | # ============================================================================= 56 | 57 | 58 | class TestReprMixin: 59 | @pytest.fixture 60 | def r(self): 61 | return Repr(5) 62 | 63 | def test_defaults(self, r): 64 | assert repr(r) == "Repr<%s>(a=5)" % hex(id(r)) 65 | 66 | def test_private(self, r): 67 | r._REPR_PRIVATE_ATTRIBUTES = True 68 | assert repr(r) == "Repr<%s>(a=5, b=42)" % hex(id(r)) 69 | 70 | def test_override(self, r): 71 | r._REPR_ARGUMENTS_TO_ATTRIBUTES = {"b": "extra"} 72 | assert repr(r) == "Repr<%s>(a=5, b=1337)" % hex(id(r)) 73 | 74 | def test_override_missing(self, r): 75 | r._REPR_ARGUMENTS_TO_ATTRIBUTES = {"b": "extra"} 76 | r._REPR_ARGUMENTS_IGNORE_MISSING = True 77 | assert repr(r) == "Repr<%s>(b=1337)" % hex(id(r)) 78 | 79 | def test_ingore(self, r): 80 | r._REPR_ARGUMENTS_IGNORE = {"a"} 81 | assert repr(r) == "Repr<%s>()" % hex(id(r)) 82 | 83 | def test_extra_attributes(self, r): 84 | r._REPR_EXTRA_ATTRIBUTES = OrderedDict( 85 | ( 86 | ( 87 | "b", 88 | "_b", 89 | ), 90 | ("extra", None), 91 | ) 92 | ) 93 | assert repr(r) == "Repr<%s>(a=5, b=42, extra=1337)" % hex(id(r)) 94 | -------------------------------------------------------------------------------- /tests/PyPoE/shared/test_murmur2.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | tests/PyPoE/shared/test_murmur2.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 607183a1cc49ed311e017b0704f32b1f74b2acff $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | 21 | 22 | Agreement 23 | =============================================================================== 24 | 25 | See PyPoE/LICENSE 26 | """ 27 | 28 | # ============================================================================= 29 | # Imports 30 | # ============================================================================= 31 | 32 | # Python 33 | 34 | # 3rd-party 35 | import pytest 36 | 37 | # self 38 | from PyPoE.shared import murmur2 39 | 40 | # ============================================================================= 41 | # Setup 42 | # ============================================================================= 43 | 44 | data = [ 45 | ("This is a test".encode("ascii"), 895688205, 0), 46 | ("This is a test".encode("ascii"), 1204582478, 42), 47 | ] 48 | 49 | # ============================================================================= 50 | # Fixtures 51 | # ============================================================================= 52 | 53 | # ============================================================================= 54 | # Tests 55 | # ============================================================================= 56 | 57 | 58 | @pytest.mark.parametrize("data,result,seed", data) 59 | def test_murmur2_32(data, result, seed): 60 | assert murmur2.murmur2_32(data, seed) == result 61 | -------------------------------------------------------------------------------- /tests/test_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for _data/dat.specification.ini 3 | 4 | Overview 5 | =============================================================================== 6 | 7 | +----------+------------------------------------------------------------------+ 8 | | Path | tests/test_data.py | 9 | +----------+------------------------------------------------------------------+ 10 | | Version | 1.0.0a0 | 11 | +----------+------------------------------------------------------------------+ 12 | | Revision | $Id: 535d444fdaa3114696f10f40c8a1ac012b97f072 $ | 13 | +----------+------------------------------------------------------------------+ 14 | | Author | Omega_K2 | 15 | +----------+------------------------------------------------------------------+ 16 | 17 | Description 18 | =============================================================================== 19 | 20 | Tests for for the specifications found in dat.specification.ini. 21 | Running this test is relatively time-consuming, so it may be a good idea to 22 | avoid it unless a PoE update has been released to locate broken or unsupported 23 | .dat files. 24 | 25 | Agreement 26 | =============================================================================== 27 | 28 | See PyPoE/LICENSE 29 | """ 30 | 31 | # ============================================================================= 32 | # Imports 33 | # ============================================================================= 34 | 35 | # Python 36 | 37 | # 3rd Party 38 | import pytest 39 | 40 | # self 41 | from PyPoE.poe.file import dat 42 | from PyPoE.poe.file.specification import load 43 | 44 | # ============================================================================= 45 | # Functions 46 | # ============================================================================= 47 | 48 | 49 | # ============================================================================= 50 | # Setup 51 | # ============================================================================= 52 | 53 | 54 | @pytest.fixture(scope="module") 55 | def files(poe_version): 56 | return [section for section in load(version=poe_version)] 57 | 58 | 59 | # ============================================================================= 60 | # Tests 61 | # ============================================================================= 62 | 63 | 64 | # Kind of testing the reading of the files twice, but whatever. 65 | # dat_file_name is parametrized in conftest.py 66 | @pytest.mark.parametrize("x64", (False,)) 67 | def test_definitions(dat_file_name, file_system, x64): 68 | opt = {"use_dat_value": False, "x64": x64} 69 | if x64: 70 | dat_file_name += "64" 71 | # Will raise errors accordingly if it fails 72 | df = dat.DatFile(dat_file_name) 73 | try: 74 | df.read(file_system.get_file("Data/" + dat_file_name), **opt) 75 | # If a file is in the spec, but not in the dat file this is allright 76 | except FileNotFoundError: 77 | return 78 | 79 | 80 | def test_missing(files, file_system): 81 | file_set = set() 82 | 83 | for fn in file_system.index.get_dir_record("Data/").files: 84 | if not fn.endswith(".dat"): 85 | continue 86 | 87 | # Not a regular dat file, ignore 88 | if fn in ["Languages.dat"]: 89 | continue 90 | 91 | file_set.add(fn) 92 | 93 | # Sorting by name makes this easier to correct when error shows up 94 | assert sorted(file_set.difference(set(files))) == [], "ggpk contains unhandled .dat files" 95 | assert ( 96 | sorted(set(files).difference(file_set)) == [] 97 | ), "dat specification contains unused dat files" 98 | 99 | 100 | # unique_dat_file_name & unique_field_name are parametrized in conftest.py 101 | def test_uniqueness(unique_dat_file_name, unique_dat_field_name, rr): 102 | df = rr[unique_dat_file_name] 103 | index = df.table_columns[unique_dat_field_name]["index"] 104 | 105 | data = [] 106 | for row in df: 107 | value = row[index] if not isinstance(row[index], dat.DatRecord) else row[index].rowid 108 | # Duplicate "None" values are acceptable. 109 | if value is None: 110 | continue 111 | data.append(value) 112 | 113 | assert len(data) == len(set(data)) 114 | --------------------------------------------------------------------------------