Splatlogger 1.3.0__tar.gz → 1.3.1__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.3.0/src/Splatlogger.egg-info → splatlogger-1.3.1}/PKG-INFO +10 -10
- {splatlogger-1.3.0 → splatlogger-1.3.1}/README.md +9 -9
- {splatlogger-1.3.0 → splatlogger-1.3.1}/pyproject.toml +1 -1
- {splatlogger-1.3.0 → splatlogger-1.3.1/src/Splatlogger.egg-info}/PKG-INFO +10 -10
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/__init__.py +1 -1
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/cemu_gecko.py +0 -2
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/match_logger.py +5 -2
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/splatlogger.py +12 -6
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/tcpgecko.py +9 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/utility_functions.py +48 -27
- {splatlogger-1.3.0 → splatlogger-1.3.1}/LICENSE +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/setup.cfg +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/Splatlogger.egg-info/SOURCES.txt +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/Splatlogger.egg-info/dependency_links.txt +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/Splatlogger.egg-info/entry_points.txt +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/Splatlogger.egg-info/requires.txt +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/Splatlogger.egg-info/top_level.txt +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/__main__.py +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/cli.py +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/enums.py +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/errors.py +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/resources/names.json +0 -0
- {splatlogger-1.3.0 → splatlogger-1.3.1}/src/splatlogger/tuples.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Splatlogger
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: PID Grabber and player/match information logging tool for Splatoon
|
|
5
5
|
Author-email: Shadow Doggo <shadowdoggo@protonmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -50,8 +50,13 @@ or any other app with the TCPGecko server.
|
|
|
50
50
|
On Aroma, use [TCPGecko-Plugin](https://github.com/PinkDiamondTeam/TCPGecko-Plugin).
|
|
51
51
|
|
|
52
52
|
## Usage
|
|
53
|
+
Splatlogger uses a command-line interface (CLI), you will need to run it within a terminal.
|
|
54
|
+
|
|
55
|
+
On Windows, it's best to use [Windows Terminal](https://learn.microsoft.com/en-us/windows/terminal/install)
|
|
56
|
+
or [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-windows), as the Command Prompt can't properly display certain characters.
|
|
57
|
+
|
|
53
58
|
### Running the executable
|
|
54
|
-
Windows and
|
|
59
|
+
Executables for Windows, Linux and macOS are provided on the [Releases](https://codeberg.org/ShadowDoggo/Splatlogger/releases/latest) page.
|
|
55
60
|
|
|
56
61
|
Run Splatlogger with:
|
|
57
62
|
```
|
|
@@ -60,9 +65,9 @@ path/to/Splatlogger-executable --ip IP [options] / --cemu [PID] [options]
|
|
|
60
65
|
Use the `--ip` target where `IP` is your Wii U's LAN IP address,
|
|
61
66
|
or the `-c` / `--cemu` target to enable Cemu mode where `PID` is the process ID of the Cemu process (optional, should be detected automatically).
|
|
62
67
|
|
|
63
|
-
|
|
68
|
+
Drag the executable into the terminal window to automatically get the path.
|
|
64
69
|
|
|
65
|
-
On Linux, you might need to make the file executable first with `chmod +x path/to/Splatlogger-executable`
|
|
70
|
+
On Linux and macOS, you might need to make the file executable first with `chmod +x path/to/Splatlogger-executable`
|
|
66
71
|
|
|
67
72
|
### Running with Python
|
|
68
73
|
You can install Splatlogger as a package from PyPI:
|
|
@@ -174,9 +179,6 @@ If it keeps failing to connect, make sure that:
|
|
|
174
179
|
- Port 7331 isn't blocked by the firewall.
|
|
175
180
|
- TCPGecko is running.
|
|
176
181
|
|
|
177
|
-
There's an issue with TCPGecko where opening an applet such as the Friend List and going back into the game causes memory
|
|
178
|
-
reads to fail. If auto-logging is enabled, you'll need to restart Splatlogger after returning to the game.
|
|
179
|
-
|
|
180
182
|
For Cemu users on Linux, if you have ptrace protection enabled, you'll need to run Python as root.
|
|
181
183
|
In that case, it's best to run the package from source or use the executable instead.
|
|
182
184
|
|
|
@@ -200,9 +202,7 @@ python scripts/build.py [--upx-dir path/to/upx]
|
|
|
200
202
|
```
|
|
201
203
|
use `--upx-dir` if you're using UPX to compress the executable.
|
|
202
204
|
|
|
203
|
-
You can also use `build.bat` on Windows and `build.sh` on Linux to handle everything automatically.
|
|
204
|
-
|
|
205
|
-
(No builds or build script are provided for macOS as I don't have a Mac to test, and my attempts at virtualizing macOS have been unsuccessful.)
|
|
205
|
+
You can also use `build.bat` on Windows and `build.sh` on Linux/macOS to handle everything automatically.
|
|
206
206
|
|
|
207
207
|
## Acknowledgments
|
|
208
208
|
- NWPlayer123 for the reference implementation of a TCPGecko library.
|
|
@@ -25,8 +25,13 @@ or any other app with the TCPGecko server.
|
|
|
25
25
|
On Aroma, use [TCPGecko-Plugin](https://github.com/PinkDiamondTeam/TCPGecko-Plugin).
|
|
26
26
|
|
|
27
27
|
## Usage
|
|
28
|
+
Splatlogger uses a command-line interface (CLI), you will need to run it within a terminal.
|
|
29
|
+
|
|
30
|
+
On Windows, it's best to use [Windows Terminal](https://learn.microsoft.com/en-us/windows/terminal/install)
|
|
31
|
+
or [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-windows), as the Command Prompt can't properly display certain characters.
|
|
32
|
+
|
|
28
33
|
### Running the executable
|
|
29
|
-
Windows and
|
|
34
|
+
Executables for Windows, Linux and macOS are provided on the [Releases](https://codeberg.org/ShadowDoggo/Splatlogger/releases/latest) page.
|
|
30
35
|
|
|
31
36
|
Run Splatlogger with:
|
|
32
37
|
```
|
|
@@ -35,9 +40,9 @@ path/to/Splatlogger-executable --ip IP [options] / --cemu [PID] [options]
|
|
|
35
40
|
Use the `--ip` target where `IP` is your Wii U's LAN IP address,
|
|
36
41
|
or the `-c` / `--cemu` target to enable Cemu mode where `PID` is the process ID of the Cemu process (optional, should be detected automatically).
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
Drag the executable into the terminal window to automatically get the path.
|
|
39
44
|
|
|
40
|
-
On Linux, you might need to make the file executable first with `chmod +x path/to/Splatlogger-executable`
|
|
45
|
+
On Linux and macOS, you might need to make the file executable first with `chmod +x path/to/Splatlogger-executable`
|
|
41
46
|
|
|
42
47
|
### Running with Python
|
|
43
48
|
You can install Splatlogger as a package from PyPI:
|
|
@@ -149,9 +154,6 @@ If it keeps failing to connect, make sure that:
|
|
|
149
154
|
- Port 7331 isn't blocked by the firewall.
|
|
150
155
|
- TCPGecko is running.
|
|
151
156
|
|
|
152
|
-
There's an issue with TCPGecko where opening an applet such as the Friend List and going back into the game causes memory
|
|
153
|
-
reads to fail. If auto-logging is enabled, you'll need to restart Splatlogger after returning to the game.
|
|
154
|
-
|
|
155
157
|
For Cemu users on Linux, if you have ptrace protection enabled, you'll need to run Python as root.
|
|
156
158
|
In that case, it's best to run the package from source or use the executable instead.
|
|
157
159
|
|
|
@@ -175,9 +177,7 @@ python scripts/build.py [--upx-dir path/to/upx]
|
|
|
175
177
|
```
|
|
176
178
|
use `--upx-dir` if you're using UPX to compress the executable.
|
|
177
179
|
|
|
178
|
-
You can also use `build.bat` on Windows and `build.sh` on Linux to handle everything automatically.
|
|
179
|
-
|
|
180
|
-
(No builds or build script are provided for macOS as I don't have a Mac to test, and my attempts at virtualizing macOS have been unsuccessful.)
|
|
180
|
+
You can also use `build.bat` on Windows and `build.sh` on Linux/macOS to handle everything automatically.
|
|
181
181
|
|
|
182
182
|
## Acknowledgments
|
|
183
183
|
- NWPlayer123 for the reference implementation of a TCPGecko library.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Splatlogger
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: PID Grabber and player/match information logging tool for Splatoon
|
|
5
5
|
Author-email: Shadow Doggo <shadowdoggo@protonmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -50,8 +50,13 @@ or any other app with the TCPGecko server.
|
|
|
50
50
|
On Aroma, use [TCPGecko-Plugin](https://github.com/PinkDiamondTeam/TCPGecko-Plugin).
|
|
51
51
|
|
|
52
52
|
## Usage
|
|
53
|
+
Splatlogger uses a command-line interface (CLI), you will need to run it within a terminal.
|
|
54
|
+
|
|
55
|
+
On Windows, it's best to use [Windows Terminal](https://learn.microsoft.com/en-us/windows/terminal/install)
|
|
56
|
+
or [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-windows), as the Command Prompt can't properly display certain characters.
|
|
57
|
+
|
|
53
58
|
### Running the executable
|
|
54
|
-
Windows and
|
|
59
|
+
Executables for Windows, Linux and macOS are provided on the [Releases](https://codeberg.org/ShadowDoggo/Splatlogger/releases/latest) page.
|
|
55
60
|
|
|
56
61
|
Run Splatlogger with:
|
|
57
62
|
```
|
|
@@ -60,9 +65,9 @@ path/to/Splatlogger-executable --ip IP [options] / --cemu [PID] [options]
|
|
|
60
65
|
Use the `--ip` target where `IP` is your Wii U's LAN IP address,
|
|
61
66
|
or the `-c` / `--cemu` target to enable Cemu mode where `PID` is the process ID of the Cemu process (optional, should be detected automatically).
|
|
62
67
|
|
|
63
|
-
|
|
68
|
+
Drag the executable into the terminal window to automatically get the path.
|
|
64
69
|
|
|
65
|
-
On Linux, you might need to make the file executable first with `chmod +x path/to/Splatlogger-executable`
|
|
70
|
+
On Linux and macOS, you might need to make the file executable first with `chmod +x path/to/Splatlogger-executable`
|
|
66
71
|
|
|
67
72
|
### Running with Python
|
|
68
73
|
You can install Splatlogger as a package from PyPI:
|
|
@@ -174,9 +179,6 @@ If it keeps failing to connect, make sure that:
|
|
|
174
179
|
- Port 7331 isn't blocked by the firewall.
|
|
175
180
|
- TCPGecko is running.
|
|
176
181
|
|
|
177
|
-
There's an issue with TCPGecko where opening an applet such as the Friend List and going back into the game causes memory
|
|
178
|
-
reads to fail. If auto-logging is enabled, you'll need to restart Splatlogger after returning to the game.
|
|
179
|
-
|
|
180
182
|
For Cemu users on Linux, if you have ptrace protection enabled, you'll need to run Python as root.
|
|
181
183
|
In that case, it's best to run the package from source or use the executable instead.
|
|
182
184
|
|
|
@@ -200,9 +202,7 @@ python scripts/build.py [--upx-dir path/to/upx]
|
|
|
200
202
|
```
|
|
201
203
|
use `--upx-dir` if you're using UPX to compress the executable.
|
|
202
204
|
|
|
203
|
-
You can also use `build.bat` on Windows and `build.sh` on Linux to handle everything automatically.
|
|
204
|
-
|
|
205
|
-
(No builds or build script are provided for macOS as I don't have a Mac to test, and my attempts at virtualizing macOS have been unsuccessful.)
|
|
205
|
+
You can also use `build.bat` on Windows and `build.sh` on Linux/macOS to handle everything automatically.
|
|
206
206
|
|
|
207
207
|
## Acknowledgments
|
|
208
208
|
- NWPlayer123 for the reference implementation of a TCPGecko library.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "1.3.
|
|
1
|
+
__version__ = "1.3.1"
|
|
2
2
|
__author__ = "Shadow Doggo"
|
|
@@ -74,11 +74,9 @@ class CemuGecko:
|
|
|
74
74
|
def resolve_pointer_chain(self, base_address: int, offsets: list[int]) -> int:
|
|
75
75
|
"""Follow a pointer chain to its destination address starting from base_address and applying offsets."""
|
|
76
76
|
current_address = self.peek32(base_address)
|
|
77
|
-
|
|
78
77
|
for offset in offsets[:-1]:
|
|
79
78
|
if current_address == 0:
|
|
80
79
|
return 0
|
|
81
|
-
|
|
82
80
|
current_address = self.peek32(current_address + offset)
|
|
83
81
|
|
|
84
82
|
if current_address != 0 and offsets:
|
|
@@ -101,8 +101,11 @@ class MatchLogger:
|
|
|
101
101
|
if self._ctx.options.log_level == "full" and not disconnect:
|
|
102
102
|
if (player_stats := stats_by_idx.get(player_info.idx)) is not None:
|
|
103
103
|
match_log += self._format_stats_log(
|
|
104
|
-
player_info,
|
|
105
|
-
|
|
104
|
+
player_info,
|
|
105
|
+
match_results.winning_team if match_results else None,
|
|
106
|
+
versus_rule,
|
|
107
|
+
player_stats,
|
|
108
|
+
dcd_players
|
|
106
109
|
)
|
|
107
110
|
|
|
108
111
|
if match_results:
|
|
@@ -152,7 +152,7 @@ def create_tcpgecko_instance(options: Options) -> TCPGecko | CemuGecko:
|
|
|
152
152
|
uf.print_error(str(e))
|
|
153
153
|
continue
|
|
154
154
|
|
|
155
|
-
raise SplatloggerError("\nFailed after
|
|
155
|
+
raise SplatloggerError("\nFailed after two attempts.")
|
|
156
156
|
|
|
157
157
|
|
|
158
158
|
@uf.handle_gecko_errors
|
|
@@ -160,10 +160,10 @@ def get_static_mem_adr(gecko: TCPGecko | CemuGecko, options: Options) -> int:
|
|
|
160
160
|
static_mem_adr = gecko.peek32(Pointers.STATIC_MEM.convert(options.platform))
|
|
161
161
|
if static_mem_adr == 0:
|
|
162
162
|
msg = (
|
|
163
|
-
"Successfully opened Cemu process but could not read StaticMem address
|
|
163
|
+
"Successfully opened Cemu process but could not read StaticMem address.\n"
|
|
164
164
|
"This is likely caused by ptrace protection (see README)."
|
|
165
165
|
if options.platform == "cemu" else
|
|
166
|
-
"Connected successfully but could not find StaticMem address
|
|
166
|
+
"Connected successfully but could not find StaticMem address.\n"
|
|
167
167
|
"Please open Splatoon first."
|
|
168
168
|
)
|
|
169
169
|
raise SplatloggerError(msg)
|
|
@@ -256,7 +256,7 @@ def get_player_count(ctx: AppContext, main_mgr_base_adr: int) -> int:
|
|
|
256
256
|
if 1 <= player_count <= 8:
|
|
257
257
|
return player_count
|
|
258
258
|
else:
|
|
259
|
-
uf.print_warning("You are currently not in a match
|
|
259
|
+
uf.print_warning("You are currently not in a match.\nLogging previous match data "
|
|
260
260
|
"(Player 1 data will be your own, Session ID will be the current one).\n")
|
|
261
261
|
|
|
262
262
|
return 8
|
|
@@ -462,8 +462,14 @@ def get_altendo_account_info(player_pid: int) -> AccountInfo:
|
|
|
462
462
|
|
|
463
463
|
try:
|
|
464
464
|
mii_data = b64decode(mii_data_response.text.replace("\\n", "").replace("\\r", ""))
|
|
465
|
-
mii_name = clean(
|
|
466
|
-
|
|
465
|
+
mii_name = clean(
|
|
466
|
+
mii_data[26:46]
|
|
467
|
+
.decode("utf-16", errors="replace")
|
|
468
|
+
.split("\u0000")[0],
|
|
469
|
+
to_ascii=False,
|
|
470
|
+
lower=False,
|
|
471
|
+
no_line_breaks=True
|
|
472
|
+
)
|
|
467
473
|
except (binascii.Error, UnicodeDecodeError):
|
|
468
474
|
request_error = "Failed to decode Mii name."
|
|
469
475
|
time.sleep(1)
|
|
@@ -28,6 +28,8 @@ class TCPGecko:
|
|
|
28
28
|
"""Python library for use with TCPGecko."""
|
|
29
29
|
|
|
30
30
|
def __init__(self, ip: str, *, port: int = DEFAULT_PORT, timeout: int = DEFAULT_TIMEOUT) -> None:
|
|
31
|
+
self._address = (ip, port)
|
|
32
|
+
self._timeout = timeout
|
|
31
33
|
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
|
|
32
34
|
self._socket.settimeout(timeout)
|
|
33
35
|
try:
|
|
@@ -50,6 +52,13 @@ class TCPGecko:
|
|
|
50
52
|
"""Close the TCPGecko connection."""
|
|
51
53
|
self._socket.close()
|
|
52
54
|
|
|
55
|
+
def reconnect(self) -> None:
|
|
56
|
+
self.close()
|
|
57
|
+
try:
|
|
58
|
+
self._socket = socket.create_connection(self._address, timeout=self._timeout)
|
|
59
|
+
except OSError as e:
|
|
60
|
+
raise GeckoConnectionFailedError(f"Reconnection failed: {e}") from e
|
|
61
|
+
|
|
53
62
|
def peek_raw(self, address: int, length: int) -> bytes:
|
|
54
63
|
"""Read raw memory starting at address and ending at address + length.
|
|
55
64
|
Returns a bytes object.
|
|
@@ -14,7 +14,7 @@ import requests
|
|
|
14
14
|
from colorama import Fore, Style
|
|
15
15
|
|
|
16
16
|
from .errors import SplatloggerError
|
|
17
|
-
from .tcpgecko import GeckoConnectionClosedError, GeckoReadMemError, TCPGeckoError
|
|
17
|
+
from .tcpgecko import GeckoConnectionClosedError, GeckoReadMemError, TCPGecko, TCPGeckoError
|
|
18
18
|
from .tuples import AccountInfo, PlayerInfo
|
|
19
19
|
|
|
20
20
|
if TYPE_CHECKING:
|
|
@@ -47,34 +47,55 @@ def read_int(data: bytes, offset: int, length: int, *, signed: bool = False) ->
|
|
|
47
47
|
def handle_gecko_errors(func: Callable[..., Any]) -> Any:
|
|
48
48
|
@wraps(func)
|
|
49
49
|
def wrapper(*args: Any, **kwargs: Any):
|
|
50
|
-
|
|
50
|
+
first = args[0] if args else kwargs.get("ctx", kwargs.get("gecko"))
|
|
51
|
+
gecko = getattr(first, "gecko", first)
|
|
52
|
+
stop_event = getattr(first, "stop_event", None)
|
|
53
|
+
reconnect = False
|
|
54
|
+
last_error = None
|
|
55
|
+
|
|
56
|
+
for attempt in range(2):
|
|
51
57
|
try:
|
|
58
|
+
if reconnect:
|
|
59
|
+
gecko.reconnect()
|
|
60
|
+
reconnect = False
|
|
61
|
+
|
|
52
62
|
return func(*args, **kwargs)
|
|
53
|
-
except
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
63
|
+
except ProcessLookupError as ex:
|
|
64
|
+
raise SplatloggerError("Cemu process was closed.") from ex
|
|
65
|
+
except (OSError, TCPGeckoError) as ex:
|
|
66
|
+
last_error = ex
|
|
67
|
+
|
|
68
|
+
if isinstance(gecko, TCPGecko):
|
|
69
|
+
reconnect = True
|
|
70
|
+
try:
|
|
71
|
+
gecko.close()
|
|
72
|
+
except OSError:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
if attempt == 1:
|
|
76
|
+
break
|
|
77
|
+
|
|
78
|
+
if isinstance(ex, ConnectionResetError):
|
|
79
|
+
message = "Connection reset abruptly."
|
|
80
|
+
elif isinstance(ex, BrokenPipeError):
|
|
81
|
+
message = "Socket operation failed. Connection was closed abruptly."
|
|
82
|
+
elif isinstance(ex, GeckoConnectionClosedError):
|
|
83
|
+
message = "Connection closed by the server."
|
|
84
|
+
elif isinstance(ex, GeckoReadMemError):
|
|
85
|
+
message = f"Reading memory failed: {ex}"
|
|
86
|
+
elif isinstance(ex, TimeoutError):
|
|
87
|
+
message = "Socket operation timed out."
|
|
88
|
+
else:
|
|
89
|
+
message = f"An error has occurred: {ex}"
|
|
90
|
+
|
|
91
|
+
print_warning(f"\n{message}\nRetrying in 10 seconds... (Use 'q' or Ctrl+C to abort)")
|
|
92
|
+
|
|
93
|
+
if stop_event is None:
|
|
94
|
+
time.sleep(10)
|
|
95
|
+
elif stop_event.wait(10):
|
|
96
|
+
raise SplatloggerError("Stopped while waiting to retry.") from ex
|
|
97
|
+
|
|
98
|
+
raise SplatloggerError("Second attempt failed.") from last_error
|
|
78
99
|
|
|
79
100
|
return wrapper
|
|
80
101
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|