Splatlogger 1.5.3__tar.gz → 1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {splatlogger-1.5.3/src/Splatlogger.egg-info → splatlogger-1.6}/PKG-INFO +27 -9
- splatlogger-1.5.3/PKG-INFO → splatlogger-1.6/README.md +22 -20
- {splatlogger-1.5.3 → splatlogger-1.6}/pyproject.toml +6 -4
- splatlogger-1.5.3/README.md → splatlogger-1.6/src/Splatlogger.egg-info/PKG-INFO +38 -6
- {splatlogger-1.5.3 → splatlogger-1.6}/src/Splatlogger.egg-info/SOURCES.txt +1 -0
- splatlogger-1.6/src/Splatlogger.egg-info/requires.txt +4 -0
- {splatlogger-1.5.3 → splatlogger-1.6}/src/splatlogger/__main__.py +125 -79
- splatlogger-1.6/src/splatlogger/cemu_gecko.py +72 -0
- {splatlogger-1.5.3 → splatlogger-1.6}/src/splatlogger/data.py +175 -184
- {splatlogger-1.5.3 → splatlogger-1.6}/src/splatlogger/match_logger.py +66 -46
- splatlogger-1.6/src/splatlogger/tcpgecko.py +162 -0
- {splatlogger-1.5.3 → splatlogger-1.6}/src/splatlogger/tcpgecko_aroma.py +36 -46
- splatlogger-1.5.3/src/Splatlogger.egg-info/requires.txt +0 -2
- splatlogger-1.5.3/src/splatlogger/tcpgecko.py +0 -189
- {splatlogger-1.5.3 → splatlogger-1.6}/setup.cfg +0 -0
- {splatlogger-1.5.3 → splatlogger-1.6}/src/Splatlogger.egg-info/dependency_links.txt +0 -0
- {splatlogger-1.5.3 → splatlogger-1.6}/src/Splatlogger.egg-info/entry_points.txt +0 -0
- {splatlogger-1.5.3 → splatlogger-1.6}/src/Splatlogger.egg-info/top_level.txt +0 -0
- {splatlogger-1.5.3 → splatlogger-1.6}/src/splatlogger/__init__.py +0 -0
|
@@ -1,27 +1,35 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Splatlogger
|
|
3
|
-
Version: 1.
|
|
4
|
-
Summary: A PID/PNID grabber and match logger for Splatoon
|
|
3
|
+
Version: 1.6
|
|
4
|
+
Summary: A CLI PID (Principal ID)/PNID grabber and match logger for Splatoon.
|
|
5
5
|
Author-email: Shadow Doggo <shadowdoggo@protonmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
|
-
Project-URL: Source Code, https://
|
|
7
|
+
Project-URL: Source Code, https://codeberg.org/ShadowDoggo/Splatlogger
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Requires-Python: >=3.11
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
Requires-Dist: userpaths
|
|
13
13
|
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: psutil
|
|
15
|
+
Requires-Dist: pymemoryeditor
|
|
14
16
|
|
|
15
17
|
# Splatlogger
|
|
16
|
-
A PID/PNID grabber and match logger for Splatoon
|
|
18
|
+
A CLI PID (Principal ID)/PNID grabber and match logger for Splatoon, written in Python.
|
|
17
19
|
|
|
18
20
|
## Prerequisites
|
|
19
|
-
|
|
20
|
-
(
|
|
21
|
+
To install the package, you'll need to have Python 3.11 or newer installed on your system
|
|
22
|
+
(on Android devices you can use Termux).
|
|
23
|
+
Python is not required for the Windows executable.
|
|
24
|
+
|
|
25
|
+
Both the Wii U and Cemu (Windows and Linux only) are supported.
|
|
26
|
+
|
|
27
|
+
Your Wii U will need to have either the Tiramisu or Aroma environment set up or have another way to run homebrew
|
|
28
|
+
(e.g. Haxchi, browser exploit).
|
|
21
29
|
|
|
22
30
|
On Tiramisu, use [TCPGecko](https://github.com/BullyWiiPlaza/tcpgecko) (Geckiine or SDGeckiine will work as well).
|
|
23
31
|
|
|
24
|
-
On Aroma, install the [TCPGecko Aroma plugin](https://
|
|
32
|
+
On Aroma, install the [TCPGecko Aroma plugin](https://codeberg.org/ShadowDoggo/TCPGeckoAroma).
|
|
25
33
|
|
|
26
34
|
## Installation
|
|
27
35
|
To install the package from PyPI, run:
|
|
@@ -34,7 +42,8 @@ Or download the latest release from GitHub and run:
|
|
|
34
42
|
python -m pip install /path/to/Splatlogger-1.x.zip
|
|
35
43
|
```
|
|
36
44
|
|
|
37
|
-
On Windows,
|
|
45
|
+
On Windows, use the executable provided on the releases page.
|
|
46
|
+
Though if you already have Python installed, it's recommended to use the package.
|
|
38
47
|
|
|
39
48
|
If you do have Python installed but do not wish to install the package, you can run the module directly from the source code:
|
|
40
49
|
```
|
|
@@ -67,11 +76,20 @@ Options:
|
|
|
67
76
|
|
|
68
77
|
- `-aroma` - Switch to Aroma mode. Enable if using the TCPGecko Aroma plugin.
|
|
69
78
|
|
|
79
|
+
- `-cemu [PID]` - Switch to Cemu mode. The `-ip` argument is not required and will be ignored.
|
|
80
|
+
- `PID` - PID of the Cemu process (optional).
|
|
81
|
+
|
|
82
|
+
- `-stack Address (in hex)` - (Cemu only) Beginning address of the stack space for Default Core 1 (Debug > View PPC threads).
|
|
83
|
+
Reuired for logging stats, not needed otherwise.
|
|
84
|
+
|
|
85
|
+
<img src="stack.png" width="600" alt="(example)">
|
|
86
|
+
|
|
70
87
|
- `-silent` - Disable printing logs to the console.
|
|
71
88
|
|
|
72
89
|
Logs are saved in `/[User]/Documents/Splatlogger/logs/[Date]`.
|
|
73
90
|
|
|
74
|
-
To always run with the same IP and options without having to type them in manually, create an `args.txt` file in
|
|
91
|
+
To always run with the same IP and options without having to type them in manually, create an `args.txt` file in
|
|
92
|
+
`/[User]/Documents/Splatlogger` and put the arguments following `splatlogger` in there.
|
|
75
93
|
Afterward any arguments passed through the command line will be ignored.
|
|
76
94
|
|
|
77
95
|
Only one program can be connected to TCPGecko at a time. If you have something else connected, disconnect it beforehand.
|
|
@@ -1,27 +1,19 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: Splatlogger
|
|
3
|
-
Version: 1.5.3
|
|
4
|
-
Summary: A PID/PNID grabber and match logger for Splatoon using TCPGecko.
|
|
5
|
-
Author-email: Shadow Doggo <shadowdoggo@protonmail.com>
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Source Code, https://github.com/spoongaming61/Splatlogger
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Operating System :: OS Independent
|
|
10
|
-
Requires-Python: >=3.11
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
Requires-Dist: userpaths
|
|
13
|
-
Requires-Dist: requests
|
|
14
|
-
|
|
15
1
|
# Splatlogger
|
|
16
|
-
A PID/PNID grabber and match logger for Splatoon
|
|
2
|
+
A CLI PID (Principal ID)/PNID grabber and match logger for Splatoon, written in Python.
|
|
17
3
|
|
|
18
4
|
## Prerequisites
|
|
19
|
-
|
|
20
|
-
(
|
|
5
|
+
To install the package, you'll need to have Python 3.11 or newer installed on your system
|
|
6
|
+
(on Android devices you can use Termux).
|
|
7
|
+
Python is not required for the Windows executable.
|
|
8
|
+
|
|
9
|
+
Both the Wii U and Cemu (Windows and Linux only) are supported.
|
|
10
|
+
|
|
11
|
+
Your Wii U will need to have either the Tiramisu or Aroma environment set up or have another way to run homebrew
|
|
12
|
+
(e.g. Haxchi, browser exploit).
|
|
21
13
|
|
|
22
14
|
On Tiramisu, use [TCPGecko](https://github.com/BullyWiiPlaza/tcpgecko) (Geckiine or SDGeckiine will work as well).
|
|
23
15
|
|
|
24
|
-
On Aroma, install the [TCPGecko Aroma plugin](https://
|
|
16
|
+
On Aroma, install the [TCPGecko Aroma plugin](https://codeberg.org/ShadowDoggo/TCPGeckoAroma).
|
|
25
17
|
|
|
26
18
|
## Installation
|
|
27
19
|
To install the package from PyPI, run:
|
|
@@ -34,7 +26,8 @@ Or download the latest release from GitHub and run:
|
|
|
34
26
|
python -m pip install /path/to/Splatlogger-1.x.zip
|
|
35
27
|
```
|
|
36
28
|
|
|
37
|
-
On Windows,
|
|
29
|
+
On Windows, use the executable provided on the releases page.
|
|
30
|
+
Though if you already have Python installed, it's recommended to use the package.
|
|
38
31
|
|
|
39
32
|
If you do have Python installed but do not wish to install the package, you can run the module directly from the source code:
|
|
40
33
|
```
|
|
@@ -67,11 +60,20 @@ Options:
|
|
|
67
60
|
|
|
68
61
|
- `-aroma` - Switch to Aroma mode. Enable if using the TCPGecko Aroma plugin.
|
|
69
62
|
|
|
63
|
+
- `-cemu [PID]` - Switch to Cemu mode. The `-ip` argument is not required and will be ignored.
|
|
64
|
+
- `PID` - PID of the Cemu process (optional).
|
|
65
|
+
|
|
66
|
+
- `-stack Address (in hex)` - (Cemu only) Beginning address of the stack space for Default Core 1 (Debug > View PPC threads).
|
|
67
|
+
Reuired for logging stats, not needed otherwise.
|
|
68
|
+
|
|
69
|
+
<img src="stack.png" width="600" alt="(example)">
|
|
70
|
+
|
|
70
71
|
- `-silent` - Disable printing logs to the console.
|
|
71
72
|
|
|
72
73
|
Logs are saved in `/[User]/Documents/Splatlogger/logs/[Date]`.
|
|
73
74
|
|
|
74
|
-
To always run with the same IP and options without having to type them in manually, create an `args.txt` file in
|
|
75
|
+
To always run with the same IP and options without having to type them in manually, create an `args.txt` file in
|
|
76
|
+
`/[User]/Documents/Splatlogger` and put the arguments following `splatlogger` in there.
|
|
75
77
|
Afterward any arguments passed through the command line will be ignored.
|
|
76
78
|
|
|
77
79
|
Only one program can be connected to TCPGecko at a time. If you have something else connected, disconnect it beforehand.
|
|
@@ -4,16 +4,18 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "Splatlogger"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.6"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Shadow Doggo", email="shadowdoggo@protonmail.com" }
|
|
10
10
|
]
|
|
11
|
-
description = "A PID/PNID grabber and match logger for Splatoon
|
|
11
|
+
description = "A CLI PID (Principal ID)/PNID grabber and match logger for Splatoon."
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.11"
|
|
14
14
|
dependencies = [
|
|
15
15
|
"userpaths",
|
|
16
|
-
"requests"
|
|
16
|
+
"requests",
|
|
17
|
+
"psutil",
|
|
18
|
+
"pymemoryeditor"
|
|
17
19
|
]
|
|
18
20
|
classifiers = [
|
|
19
21
|
"Programming Language :: Python :: 3",
|
|
@@ -23,7 +25,7 @@ license = "MIT"
|
|
|
23
25
|
license-files = ["LICENSE"]
|
|
24
26
|
|
|
25
27
|
[project.urls]
|
|
26
|
-
"Source Code" = "https://
|
|
28
|
+
"Source Code" = "https://codeberg.org/ShadowDoggo/Splatlogger"
|
|
27
29
|
|
|
28
30
|
[project.scripts]
|
|
29
31
|
splatlogger = "splatlogger.__main__:main"
|
|
@@ -1,13 +1,35 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: Splatlogger
|
|
3
|
+
Version: 1.6
|
|
4
|
+
Summary: A CLI PID (Principal ID)/PNID grabber and match logger for Splatoon.
|
|
5
|
+
Author-email: Shadow Doggo <shadowdoggo@protonmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Source Code, https://codeberg.org/ShadowDoggo/Splatlogger
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: userpaths
|
|
13
|
+
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: psutil
|
|
15
|
+
Requires-Dist: pymemoryeditor
|
|
16
|
+
|
|
1
17
|
# Splatlogger
|
|
2
|
-
A PID/PNID grabber and match logger for Splatoon
|
|
18
|
+
A CLI PID (Principal ID)/PNID grabber and match logger for Splatoon, written in Python.
|
|
3
19
|
|
|
4
20
|
## Prerequisites
|
|
5
|
-
|
|
6
|
-
(
|
|
21
|
+
To install the package, you'll need to have Python 3.11 or newer installed on your system
|
|
22
|
+
(on Android devices you can use Termux).
|
|
23
|
+
Python is not required for the Windows executable.
|
|
24
|
+
|
|
25
|
+
Both the Wii U and Cemu (Windows and Linux only) are supported.
|
|
26
|
+
|
|
27
|
+
Your Wii U will need to have either the Tiramisu or Aroma environment set up or have another way to run homebrew
|
|
28
|
+
(e.g. Haxchi, browser exploit).
|
|
7
29
|
|
|
8
30
|
On Tiramisu, use [TCPGecko](https://github.com/BullyWiiPlaza/tcpgecko) (Geckiine or SDGeckiine will work as well).
|
|
9
31
|
|
|
10
|
-
On Aroma, install the [TCPGecko Aroma plugin](https://
|
|
32
|
+
On Aroma, install the [TCPGecko Aroma plugin](https://codeberg.org/ShadowDoggo/TCPGeckoAroma).
|
|
11
33
|
|
|
12
34
|
## Installation
|
|
13
35
|
To install the package from PyPI, run:
|
|
@@ -20,7 +42,8 @@ Or download the latest release from GitHub and run:
|
|
|
20
42
|
python -m pip install /path/to/Splatlogger-1.x.zip
|
|
21
43
|
```
|
|
22
44
|
|
|
23
|
-
On Windows,
|
|
45
|
+
On Windows, use the executable provided on the releases page.
|
|
46
|
+
Though if you already have Python installed, it's recommended to use the package.
|
|
24
47
|
|
|
25
48
|
If you do have Python installed but do not wish to install the package, you can run the module directly from the source code:
|
|
26
49
|
```
|
|
@@ -53,11 +76,20 @@ Options:
|
|
|
53
76
|
|
|
54
77
|
- `-aroma` - Switch to Aroma mode. Enable if using the TCPGecko Aroma plugin.
|
|
55
78
|
|
|
79
|
+
- `-cemu [PID]` - Switch to Cemu mode. The `-ip` argument is not required and will be ignored.
|
|
80
|
+
- `PID` - PID of the Cemu process (optional).
|
|
81
|
+
|
|
82
|
+
- `-stack Address (in hex)` - (Cemu only) Beginning address of the stack space for Default Core 1 (Debug > View PPC threads).
|
|
83
|
+
Reuired for logging stats, not needed otherwise.
|
|
84
|
+
|
|
85
|
+
<img src="stack.png" width="600" alt="(example)">
|
|
86
|
+
|
|
56
87
|
- `-silent` - Disable printing logs to the console.
|
|
57
88
|
|
|
58
89
|
Logs are saved in `/[User]/Documents/Splatlogger/logs/[Date]`.
|
|
59
90
|
|
|
60
|
-
To always run with the same IP and options without having to type them in manually, create an `args.txt` file in
|
|
91
|
+
To always run with the same IP and options without having to type them in manually, create an `args.txt` file in
|
|
92
|
+
`/[User]/Documents/Splatlogger` and put the arguments following `splatlogger` in there.
|
|
61
93
|
Afterward any arguments passed through the command line will be ignored.
|
|
62
94
|
|
|
63
95
|
Only one program can be connected to TCPGecko at a time. If you have something else connected, disconnect it beforehand.
|
|
@@ -5,19 +5,20 @@ import ipaddress
|
|
|
5
5
|
import os
|
|
6
6
|
import sys
|
|
7
7
|
import time
|
|
8
|
-
import xml.etree.ElementTree as ElementTree
|
|
9
8
|
from datetime import datetime
|
|
10
9
|
from typing import NamedTuple
|
|
10
|
+
from xml.etree import ElementTree
|
|
11
11
|
|
|
12
12
|
import requests
|
|
13
13
|
import userpaths # type: ignore[import-untyped]
|
|
14
14
|
|
|
15
|
+
from .cemu_gecko import CemuGecko, CemuGeckoException
|
|
15
16
|
from .data import Pointers, Offsets, PlayerInfo
|
|
16
17
|
from .match_logger import MatchLogger
|
|
17
18
|
from .tcpgecko import TCPGecko, TCPGeckoException
|
|
18
19
|
from .tcpgecko_aroma import TCPGeckoAroma
|
|
19
20
|
|
|
20
|
-
_VERSION
|
|
21
|
+
_VERSION = "1.6"
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class Options(NamedTuple):
|
|
@@ -26,6 +27,8 @@ class Options(NamedTuple):
|
|
|
26
27
|
log_latest: bool
|
|
27
28
|
silent_logging: bool
|
|
28
29
|
aroma: bool
|
|
30
|
+
cemu: bool
|
|
31
|
+
stack: int | None
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
class AccountInfo(NamedTuple):
|
|
@@ -37,70 +40,106 @@ class AccountInfo(NamedTuple):
|
|
|
37
40
|
def main() -> None:
|
|
38
41
|
print(f"Splatlogger v{_VERSION} by Shadow Doggo\n")
|
|
39
42
|
|
|
40
|
-
parser
|
|
41
|
-
|
|
42
|
-
)
|
|
43
|
-
parser.add_argument("-ip", help="Your Wii U's LAN IP address.", required=True)
|
|
43
|
+
parser = argparse.ArgumentParser(description="A CLI PID (Principal ID)/PNID grabber and match logger for Splatoon.")
|
|
44
|
+
parser.add_argument("-ip", help="Your Wii U's LAN IP address.")
|
|
44
45
|
parser.add_argument("-log-level", help="Set how much data should be logged (see README).",
|
|
45
|
-
choices=["none", "standard", "extended", "stats"],
|
|
46
|
-
nargs="?", const="standard", default="standard")
|
|
46
|
+
choices=["none", "standard", "extended", "stats"], default="standard")
|
|
47
47
|
parser.add_argument("-auto",
|
|
48
48
|
help="Enable auto logging (see README).",
|
|
49
49
|
choices=["all", "latest"], nargs="?", const="all")
|
|
50
50
|
parser.add_argument("-aroma", help="Switch to Aroma mode. Enable if using the TCPGecko Aroma plugin.",
|
|
51
51
|
action="store_true")
|
|
52
|
+
parser.add_argument("-cemu",
|
|
53
|
+
help="Switch to Cemu mode (optional: PID of the Cemu process).", metavar="PID",
|
|
54
|
+
nargs="?", const=-1)
|
|
55
|
+
parser.add_argument("-stack",
|
|
56
|
+
help="Beginning address of the stack space for Default Core 1 (Cemu only, see README).",
|
|
57
|
+
metavar="Address (in hex)")
|
|
52
58
|
parser.add_argument("-silent", help="Disable printing logs to the console.", action="store_true")
|
|
53
59
|
|
|
54
|
-
args: argparse.Namespace
|
|
55
60
|
if os.path.isfile(f"{userpaths.get_my_documents()}/Splatlogger/args.txt"):
|
|
56
61
|
with open(f"{userpaths.get_my_documents()}/Splatlogger/args.txt", encoding="utf-8") as f:
|
|
57
62
|
args = parser.parse_args(f.read().split())
|
|
58
63
|
else:
|
|
59
64
|
args = parser.parse_args()
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
log_latest=args.auto == "latest",
|
|
64
|
-
silent_logging=args.silent,
|
|
65
|
-
aroma=args.aroma)
|
|
66
|
-
|
|
67
|
-
gecko: TCPGecko
|
|
68
|
-
try:
|
|
69
|
-
ipaddress.ip_address(args.ip)
|
|
70
|
-
print(f"Connecting to: {args.ip}")
|
|
71
|
-
gecko = TCPGeckoAroma(args.ip) if options.aroma else TCPGecko(args.ip)
|
|
72
|
-
except ValueError:
|
|
73
|
-
print("Please provide a valid IP address.\nExiting.")
|
|
74
|
-
sys.exit()
|
|
75
|
-
except OSError as e:
|
|
76
|
-
print(f"Failed to connect: {e}\nExiting.")
|
|
66
|
+
if args.aroma and args.cemu:
|
|
67
|
+
print("Cannot use both Aroma and Cemu modes.")
|
|
77
68
|
sys.exit()
|
|
78
69
|
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
stack_addr: int | None = None
|
|
71
|
+
if args.stack:
|
|
72
|
+
try:
|
|
73
|
+
stack_addr = int(args.stack, 16)
|
|
74
|
+
except ValueError:
|
|
75
|
+
print("Invalid stack address. Please provide a valid hex value.")
|
|
76
|
+
sys.exit()
|
|
77
|
+
|
|
78
|
+
options = Options(log_level=args.log_level,
|
|
79
|
+
auto_logging=args.auto in ("all", "latest"),
|
|
80
|
+
log_latest=args.auto == "latest",
|
|
81
|
+
silent_logging=args.silent,
|
|
82
|
+
aroma=args.aroma,
|
|
83
|
+
cemu=args.cemu is not None,
|
|
84
|
+
stack=stack_addr)
|
|
85
|
+
|
|
86
|
+
ptr_offset = 0
|
|
87
|
+
gecko: TCPGecko | TCPGeckoAroma | CemuGecko
|
|
88
|
+
if args.cemu:
|
|
89
|
+
ptr_offset = 0x503000
|
|
90
|
+
if args.log_level == "stats" and not args.stack:
|
|
91
|
+
print("Stack space address must be specified for stats logging on Cemu (see README).")
|
|
92
|
+
sys.exit()
|
|
93
|
+
try:
|
|
94
|
+
print("Opening Cemu process...")
|
|
95
|
+
gecko = CemuGecko(int(args.cemu))
|
|
96
|
+
except CemuGeckoException as e:
|
|
97
|
+
print(e)
|
|
98
|
+
sys.exit()
|
|
99
|
+
else:
|
|
100
|
+
if not args.ip:
|
|
101
|
+
print('No IP address was provided. Run "splatlogger -h" for help.')
|
|
102
|
+
sys.exit()
|
|
103
|
+
try:
|
|
104
|
+
ipaddress.ip_address(args.ip)
|
|
105
|
+
print(f"Connecting to: {args.ip}")
|
|
106
|
+
gecko = TCPGeckoAroma(args.ip) if options.aroma else TCPGecko(args.ip)
|
|
107
|
+
except ValueError:
|
|
108
|
+
print("Please provide a valid IP address.")
|
|
109
|
+
sys.exit()
|
|
110
|
+
except TCPGeckoException as e:
|
|
111
|
+
print(e)
|
|
112
|
+
sys.exit()
|
|
113
|
+
|
|
81
114
|
for _ in range(3):
|
|
82
115
|
try:
|
|
83
|
-
static_mem_adr = gecko.peek32(Pointers.STATIC_MEM)
|
|
84
|
-
player_info_ary_adr = gecko.peek32(static_mem_adr + Offsets.
|
|
85
|
-
|
|
116
|
+
static_mem_adr = gecko.peek32(Pointers.STATIC_MEM - ptr_offset)
|
|
117
|
+
player_info_ary_adr = gecko.peek32(static_mem_adr + Offsets.STATICMEM_PLAYER_INFO_ARY)
|
|
118
|
+
assert static_mem_adr != 0 and player_info_ary_adr != 0
|
|
119
|
+
|
|
120
|
+
print("Success.\n" if options.cemu else "Connected.\n")
|
|
86
121
|
break
|
|
87
122
|
except (OSError, TimeoutError, TCPGeckoException) as e:
|
|
88
123
|
print(f"\nAn error has occurred: {e}\nRetrying in 10 seconds...")
|
|
89
124
|
time.sleep(10)
|
|
125
|
+
except AssertionError:
|
|
126
|
+
print("\nUnable to find StaticMem and PlayerInfoAry address.\nRetrying in 10 seconds...")
|
|
127
|
+
time.sleep(10)
|
|
90
128
|
else:
|
|
91
|
-
print("\nFailed after 3 tries
|
|
129
|
+
print("\nFailed after 3 tries.")
|
|
92
130
|
sys.exit()
|
|
93
131
|
|
|
94
132
|
match_logger: MatchLogger | None = None
|
|
95
133
|
if options.log_level != "none":
|
|
96
|
-
match_logger = MatchLogger(gecko, options.log_level, options.auto_logging, options.aroma,
|
|
134
|
+
match_logger = MatchLogger(gecko, options.log_level, options.auto_logging, options.aroma,
|
|
135
|
+
options.cemu, options.stack, static_mem_adr)
|
|
97
136
|
try:
|
|
98
137
|
match_logger.create_new_log()
|
|
99
138
|
except OSError as e:
|
|
100
|
-
print(f"Failed to create log file: {e}
|
|
139
|
+
print(f"Failed to create log file: {e}")
|
|
101
140
|
sys.exit()
|
|
102
141
|
|
|
103
|
-
if options.auto_logging and match_logger
|
|
142
|
+
if options.auto_logging and match_logger:
|
|
104
143
|
print("Auto logging enabled.")
|
|
105
144
|
_auto_log(options, gecko, match_logger, player_info_ary_adr)
|
|
106
145
|
|
|
@@ -112,18 +151,19 @@ def main() -> None:
|
|
|
112
151
|
print(f"\nAn error has occurred: {e}\nRetrying in 10 seconds...")
|
|
113
152
|
time.sleep(10)
|
|
114
153
|
else:
|
|
115
|
-
print("\nFailed after 3 tries
|
|
154
|
+
print("\nFailed after 3 tries.")
|
|
116
155
|
sys.exit()
|
|
117
156
|
|
|
118
157
|
|
|
119
|
-
def _auto_log(options: Options, gecko: TCPGecko, match_logger: MatchLogger,
|
|
158
|
+
def _auto_log(options: Options, gecko: TCPGecko | CemuGecko, match_logger: MatchLogger,
|
|
120
159
|
player_info_ary_adr: int) -> None:
|
|
121
|
-
match_in_progress
|
|
122
|
-
count
|
|
123
|
-
retry_count
|
|
160
|
+
match_in_progress = False
|
|
161
|
+
count = 0
|
|
162
|
+
retry_count = 0
|
|
163
|
+
ptr_offset = 0x503000 if options.cemu else 0
|
|
124
164
|
while True:
|
|
125
165
|
try:
|
|
126
|
-
main_mgr_vs_game_adr
|
|
166
|
+
main_mgr_vs_game_adr = gecko.peek32(Pointers.MAIN_MGR_VS_GAME - ptr_offset)
|
|
127
167
|
if main_mgr_vs_game_adr == 0:
|
|
128
168
|
match_in_progress = False
|
|
129
169
|
|
|
@@ -138,6 +178,7 @@ def _auto_log(options: Options, gecko: TCPGecko, match_logger: MatchLogger,
|
|
|
138
178
|
print(f"\nMatch {count}\n")
|
|
139
179
|
|
|
140
180
|
time.sleep(1)
|
|
181
|
+
|
|
141
182
|
for _ in range(3):
|
|
142
183
|
try:
|
|
143
184
|
_splatlog(options, gecko, match_logger, player_info_ary_adr, count)
|
|
@@ -146,7 +187,7 @@ def _auto_log(options: Options, gecko: TCPGecko, match_logger: MatchLogger,
|
|
|
146
187
|
print(f"\nAn error has occurred: {e}\nRetrying in 10 seconds...")
|
|
147
188
|
time.sleep(10)
|
|
148
189
|
else:
|
|
149
|
-
print("\nFailed after 3 tries
|
|
190
|
+
print("\nFailed after 3 tries.")
|
|
150
191
|
sys.exit()
|
|
151
192
|
|
|
152
193
|
retry_count = 0
|
|
@@ -160,61 +201,60 @@ def _auto_log(options: Options, gecko: TCPGecko, match_logger: MatchLogger,
|
|
|
160
201
|
retry_count += 1
|
|
161
202
|
time.sleep(10)
|
|
162
203
|
else:
|
|
163
|
-
print("\nFailed after 3 tries
|
|
204
|
+
print("\nFailed after 3 tries.")
|
|
164
205
|
sys.exit()
|
|
165
206
|
|
|
166
207
|
|
|
167
|
-
def _splatlog(options: Options, gecko: TCPGecko, match_logger: MatchLogger | None,
|
|
208
|
+
def _splatlog(options: Options, gecko: TCPGecko | CemuGecko, match_logger: MatchLogger | None,
|
|
168
209
|
player_info_ary_adr: int, match_count: int) -> None:
|
|
169
|
-
session_id
|
|
170
|
-
player_count
|
|
210
|
+
session_id = 0
|
|
211
|
+
player_count = 8
|
|
171
212
|
request_errors: list[str] = []
|
|
172
213
|
player_info_list: list[PlayerInfo] = []
|
|
173
|
-
|
|
214
|
+
date = datetime.now()
|
|
215
|
+
ptr_offset = 0x503000 if options.cemu else 0
|
|
174
216
|
|
|
175
|
-
session_adr = gecko.peek32(Pointers.SESSION)
|
|
217
|
+
session_adr = gecko.peek32(Pointers.SESSION - ptr_offset)
|
|
176
218
|
if session_adr != 0:
|
|
177
|
-
session_id_idx
|
|
178
|
-
|
|
219
|
+
session_id_idx = int.from_bytes(gecko.peek_raw(session_adr + Offsets.SESSION_ID_IDX,
|
|
220
|
+
length=1), byteorder="big")
|
|
179
221
|
session_id = gecko.peek32(session_adr + session_id_idx * 4 + Offsets.SESSION_ID)
|
|
180
222
|
|
|
181
|
-
main_mgr_base_adr
|
|
182
|
-
if main_mgr_base_adr != 0 and gecko.peek8(Pointers.MAIN_MGR_VS_GAME) != 0:
|
|
183
|
-
player_mgr_adr
|
|
223
|
+
main_mgr_base_adr = gecko.peek32(Pointers.MAIN_MGR_BASE - ptr_offset)
|
|
224
|
+
if main_mgr_base_adr != 0 and gecko.peek8(Pointers.MAIN_MGR_VS_GAME - ptr_offset) != 0:
|
|
225
|
+
player_mgr_adr = gecko.peek32(main_mgr_base_adr + Offsets.MAINMGR_PLAYER_MGR)
|
|
184
226
|
if player_mgr_adr != 0:
|
|
185
|
-
player_count = gecko.peek8(player_mgr_adr + Offsets.
|
|
227
|
+
player_count = gecko.peek8(player_mgr_adr + Offsets.PLAYERMGR_PLAYER_COUNT)
|
|
186
228
|
else:
|
|
187
229
|
print("You are currently not in a match. Logging previous match data "
|
|
188
230
|
"(Player 1 data will be your own, Session ID will be the current one).\n")
|
|
189
231
|
|
|
190
|
-
print(f"{'Player'
|
|
232
|
+
print(f"{'Player':<8} | {'PID (Hex)':<10} | {'PID (Dec)':<10} | {'PNID':<16} | Name\n"
|
|
191
233
|
f"{'-' * 73}")
|
|
192
234
|
|
|
193
|
-
player_idx: int
|
|
194
235
|
for player_idx in range(player_count):
|
|
195
|
-
player_info_raw
|
|
196
|
-
|
|
236
|
+
player_info_raw = gecko.peek_raw(gecko.peek32(player_info_ary_adr + player_idx * 0x4),
|
|
237
|
+
length=0xD4)
|
|
197
238
|
|
|
198
|
-
player_pid
|
|
239
|
+
player_pid = int.from_bytes(player_info_raw[0xD0:0xD4], byteorder="big")
|
|
199
240
|
if player_pid != 0:
|
|
200
|
-
player_name
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
241
|
+
player_name = (player_info_raw[0x6:0x26]
|
|
242
|
+
.decode("utf-16-be")
|
|
243
|
+
.split("\u0000")[0]
|
|
244
|
+
.replace("\n", "")
|
|
245
|
+
.replace("\r", ""))
|
|
205
246
|
|
|
206
|
-
act_info
|
|
247
|
+
act_info = _get_act_info(player_pid)
|
|
207
248
|
if act_info.request_error != "":
|
|
208
249
|
request_errors.append(act_info.request_error)
|
|
209
250
|
|
|
210
251
|
if options.log_level != "none":
|
|
211
|
-
player_info
|
|
252
|
+
player_info = PlayerInfo(
|
|
212
253
|
idx=player_idx,
|
|
213
254
|
pid=player_pid,
|
|
214
255
|
pnid=act_info.pnid,
|
|
215
256
|
name=player_name,
|
|
216
257
|
mii_name=act_info.mii_name,
|
|
217
|
-
region=int.from_bytes(player_info_raw[0x2C:0x30], byteorder="big"),
|
|
218
258
|
team=int.from_bytes(player_info_raw[0x33:0x34], byteorder="big"),
|
|
219
259
|
gender=int.from_bytes(player_info_raw[0x37:0x38], byteorder="big"),
|
|
220
260
|
skin_tone=int.from_bytes(player_info_raw[0x3B:0x3C], byteorder="big"),
|
|
@@ -232,9 +272,9 @@ def _splatlog(options: Options, gecko: TCPGecko, match_logger: MatchLogger | Non
|
|
|
232
272
|
|
|
233
273
|
if not options.silent_logging:
|
|
234
274
|
print(f"Player {player_idx + 1} | "
|
|
235
|
-
f"0x{f'{player_pid:X}'
|
|
275
|
+
f"0x{f'{player_pid:X}':<8} | "
|
|
236
276
|
f"{player_pid:<10} | "
|
|
237
|
-
f"{act_info.pnid
|
|
277
|
+
f"{act_info.pnid:<16} | "
|
|
238
278
|
f"{player_name} {f'({act_info.mii_name})' if player_name != act_info.mii_name else ''}")
|
|
239
279
|
|
|
240
280
|
if not options.silent_logging:
|
|
@@ -248,29 +288,35 @@ def _splatlog(options: Options, gecko: TCPGecko, match_logger: MatchLogger | Non
|
|
|
248
288
|
else:
|
|
249
289
|
print("Session ID: None")
|
|
250
290
|
|
|
251
|
-
print(f"Fetched at: {
|
|
291
|
+
print(f"Fetched at: {date.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
252
292
|
|
|
253
|
-
if options.log_level != "none" and match_logger
|
|
254
|
-
match_logger.log_match(
|
|
293
|
+
if options.log_level != "none" and match_logger:
|
|
294
|
+
match_logger.log_match(date, session_id, match_count, player_info_list)
|
|
255
295
|
|
|
256
296
|
|
|
257
297
|
def _get_act_info(player_pid: int) -> AccountInfo:
|
|
258
|
-
pnid
|
|
259
|
-
mii_name
|
|
260
|
-
request_error
|
|
298
|
+
pnid = ""
|
|
299
|
+
mii_name = ""
|
|
300
|
+
request_error = ""
|
|
261
301
|
|
|
262
302
|
for _ in range(3):
|
|
263
303
|
try:
|
|
264
|
-
response
|
|
304
|
+
response = requests.get(
|
|
265
305
|
f"https://account.pretendo.cc/v1/api/miis?pids={player_pid}",
|
|
266
306
|
headers={"X-Nintendo-Client-ID": "a2efa818a34fa16b8afbc8a74eba3eda",
|
|
267
307
|
"X-Nintendo-Client-Secret": "c91cdb5658bd4954ade78533a339cf9a"},
|
|
268
|
-
timeout=
|
|
308
|
+
timeout=5
|
|
269
309
|
)
|
|
270
310
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
311
|
+
try:
|
|
312
|
+
root = ElementTree.fromstring(response.text)
|
|
313
|
+
except ElementTree.ParseError:
|
|
314
|
+
return AccountInfo("", "", "Got empty or invalid response.")
|
|
315
|
+
|
|
316
|
+
if not (isinstance(root[0].find("user_id"), ElementTree.Element) and
|
|
317
|
+
isinstance(root[0].find("name"), ElementTree.Element)):
|
|
318
|
+
return AccountInfo("", "", "Got empty or invalid response.")
|
|
319
|
+
|
|
274
320
|
pnid = root[0].find("user_id").text # type: ignore
|
|
275
321
|
mii_name = (root[0].find("name") # type: ignore
|
|
276
322
|
.text
|