PyNeoFile 0.19.8__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.
- pyneofile-0.19.8/LICENSE +28 -0
- pyneofile-0.19.8/PKG-INFO +24 -0
- pyneofile-0.19.8/PyNeoFile.egg-info/PKG-INFO +24 -0
- pyneofile-0.19.8/PyNeoFile.egg-info/SOURCES.txt +12 -0
- pyneofile-0.19.8/PyNeoFile.egg-info/dependency_links.txt +1 -0
- pyneofile-0.19.8/PyNeoFile.egg-info/top_level.txt +1 -0
- pyneofile-0.19.8/PyNeoFile.egg-info/zip-safe +1 -0
- pyneofile-0.19.8/README.md +2 -0
- pyneofile-0.19.8/neofile.py +249 -0
- pyneofile-0.19.8/pyneofile.py +1430 -0
- pyneofile-0.19.8/pyproject.toml +11 -0
- pyneofile-0.19.8/setup.cfg +8 -0
- pyneofile-0.19.8/setup.py +166 -0
pyneofile-0.19.8/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Game Maker 2k
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyNeoFile
|
|
3
|
+
Version: 0.19.8
|
|
4
|
+
Summary: A tar like file format name archivefile.
|
|
5
|
+
Home-page: https://github.com/GameMaker2k/PyArchiveFile
|
|
6
|
+
Download-URL: https://github.com/GameMaker2k/PyArchiveFile/neo/master.tar.gz
|
|
7
|
+
Author: Kazuki Przyborowski
|
|
8
|
+
Author-email: Kazuki Przyborowski <kazuki.przyborowski@gmail.com>
|
|
9
|
+
Maintainer: Kazuki Przyborowski
|
|
10
|
+
Maintainer-email: kazuki.przyborowski@gmail.com
|
|
11
|
+
License: BSD-3-Clause
|
|
12
|
+
Platform: OS Independent
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Dynamic: author
|
|
16
|
+
Dynamic: download-url
|
|
17
|
+
Dynamic: home-page
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
Dynamic: maintainer
|
|
20
|
+
Dynamic: maintainer-email
|
|
21
|
+
Dynamic: platform
|
|
22
|
+
|
|
23
|
+
A tar like file format name NeoFile
|
|
24
|
+

|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyNeoFile
|
|
3
|
+
Version: 0.19.8
|
|
4
|
+
Summary: A tar like file format name archivefile.
|
|
5
|
+
Home-page: https://github.com/GameMaker2k/PyArchiveFile
|
|
6
|
+
Download-URL: https://github.com/GameMaker2k/PyArchiveFile/neo/master.tar.gz
|
|
7
|
+
Author: Kazuki Przyborowski
|
|
8
|
+
Author-email: Kazuki Przyborowski <kazuki.przyborowski@gmail.com>
|
|
9
|
+
Maintainer: Kazuki Przyborowski
|
|
10
|
+
Maintainer-email: kazuki.przyborowski@gmail.com
|
|
11
|
+
License: BSD-3-Clause
|
|
12
|
+
Platform: OS Independent
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Dynamic: author
|
|
16
|
+
Dynamic: download-url
|
|
17
|
+
Dynamic: home-page
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
Dynamic: maintainer
|
|
20
|
+
Dynamic: maintainer-email
|
|
21
|
+
Dynamic: platform
|
|
22
|
+
|
|
23
|
+
A tar like file format name NeoFile
|
|
24
|
+

|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
neofile.py
|
|
4
|
+
pyneofile.py
|
|
5
|
+
pyproject.toml
|
|
6
|
+
setup.cfg
|
|
7
|
+
setup.py
|
|
8
|
+
PyNeoFile.egg-info/PKG-INFO
|
|
9
|
+
PyNeoFile.egg-info/SOURCES.txt
|
|
10
|
+
PyNeoFile.egg-info/dependency_links.txt
|
|
11
|
+
PyNeoFile.egg-info/top_level.txt
|
|
12
|
+
PyNeoFile.egg-info/zip-safe
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyneofile
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
|
|
2
|
+
#!/usr/bin/env python
|
|
3
|
+
# -*- coding: utf-8 -*-
|
|
4
|
+
|
|
5
|
+
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
neofile.py — CLI for the PyNeoFile format (.neo).
|
|
9
|
+
|
|
10
|
+
New:
|
|
11
|
+
- Input '-' for list/validate/extract/repack/convert reads archive bytes from stdin.
|
|
12
|
+
- Output '-' for create/repack/convert writes archive bytes to stdout.
|
|
13
|
+
- Extract with '-o -' streams a TAR archive to stdout (use: `... -e -i in.neo -o - > out.tar`).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os, sys, argparse, tempfile, tarfile, io, base64
|
|
17
|
+
import pyneofile as N
|
|
18
|
+
|
|
19
|
+
__project__ = pyneofile.__project__
|
|
20
|
+
__program_name__ = pyneofile.__program_name__
|
|
21
|
+
__file_format_name__ = pyneofile.__file_format_name__
|
|
22
|
+
__file_format_magic__ = pyneofile.__file_format_magic__
|
|
23
|
+
__file_format_len__ = pyneofile.__file_format_len__
|
|
24
|
+
__file_format_hex__ = pyneofile.__file_format_hex__
|
|
25
|
+
__file_format_delimiter__ = pyneofile.__file_format_delimiter__
|
|
26
|
+
__file_format_dict__ = pyneofile.__file_format_dict__
|
|
27
|
+
__file_format_default__ = pyneofile.__file_format_default__
|
|
28
|
+
__file_format_multi_dict__ = pyneofile.__file_format_multi_dict__
|
|
29
|
+
__use_new_style__ = pyneofile.__use_new_style__
|
|
30
|
+
__use_advanced_list__ = pyneofile.__use_advanced_list__
|
|
31
|
+
__use_alt_inode__ = pyneofile.__use_alt_inode__
|
|
32
|
+
__project_url__ = pyneofile.__project_url__
|
|
33
|
+
__version_info__ = pyneofile.__version_info__
|
|
34
|
+
__version_date_info__ = pyneofile.__version_date_info__
|
|
35
|
+
__version_date__ = pyneofile.__version_date__
|
|
36
|
+
__version_date_plusrc__ = pyneofile.__version_date_plusrc__
|
|
37
|
+
__version__ = pyneofile.__version__
|
|
38
|
+
|
|
39
|
+
def _stdout_bin():
|
|
40
|
+
return getattr(sys.stdout, "buffer", sys.stdout)
|
|
41
|
+
|
|
42
|
+
def _stdin_bin():
|
|
43
|
+
return getattr(sys.stdin, "buffer", sys.stdin)
|
|
44
|
+
|
|
45
|
+
def _build_formatspecs_from_args(args):
|
|
46
|
+
if args.format is None or args.format.lower() == "auto":
|
|
47
|
+
return None
|
|
48
|
+
return {
|
|
49
|
+
"format_name": args.format,
|
|
50
|
+
"format_magic": args.format,
|
|
51
|
+
"format_ver": (args.formatver or "001"),
|
|
52
|
+
"format_delimiter": (args.delimiter or "\x00"),
|
|
53
|
+
"new_style": True,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def _convert_or_fail(infile, outpath, formatspecs, checksum, compression, level):
|
|
57
|
+
try:
|
|
58
|
+
return N.convert_foreign_to_neo(infile, outpath, formatspecs=formatspecs,
|
|
59
|
+
checksumtypes=(checksum, checksum, checksum),
|
|
60
|
+
compression=compression, compression_level=level)
|
|
61
|
+
except RuntimeError as e:
|
|
62
|
+
msg = str(e)
|
|
63
|
+
if "rarfile" in msg.lower():
|
|
64
|
+
sys.stderr.write("error: RAR support requires 'rarfile'. Install via: pip install rarfile\n")
|
|
65
|
+
elif "py7zr" in msg.lower():
|
|
66
|
+
sys.stderr.write("error: 7z support requires 'py7zr'. Install via: pip install py7zr\n")
|
|
67
|
+
else:
|
|
68
|
+
sys.stderr.write("convert error: %s\n" % msg)
|
|
69
|
+
return None
|
|
70
|
+
except Exception as e:
|
|
71
|
+
sys.stderr.write("convert error: %s\n" % e)
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
def _emit_tar_stream_from_array(arr, outfp):
|
|
75
|
+
"""Write a tar stream to outfp from the parsed archive array (no re-compress)."""
|
|
76
|
+
tf = tarfile.open(fileobj=outfp, mode='w|') # stream mode
|
|
77
|
+
try:
|
|
78
|
+
for ent in arr['ffilelist']:
|
|
79
|
+
name = ent['fname'].lstrip('./')
|
|
80
|
+
if ent['ftype'] == 5:
|
|
81
|
+
ti = tarfile.TarInfo(name=name.rstrip('/') + '/')
|
|
82
|
+
ti.type = tarfile.DIRTYPE
|
|
83
|
+
ti.mode = ent.get('fmode', 0o755) & 0o777
|
|
84
|
+
ti.mtime = ent.get('fmtime', 0)
|
|
85
|
+
ti.size = 0
|
|
86
|
+
tf.addfile(ti)
|
|
87
|
+
else:
|
|
88
|
+
data = ent.get('fcontent') or b''
|
|
89
|
+
bio = io.BytesIO(data)
|
|
90
|
+
ti = tarfile.TarInfo(name=name)
|
|
91
|
+
ti.type = tarfile.REGTYPE
|
|
92
|
+
ti.mode = ent.get('fmode', 0o644) & 0o777
|
|
93
|
+
ti.mtime = ent.get('fmtime', 0)
|
|
94
|
+
ti.size = len(data)
|
|
95
|
+
tf.addfile(ti, fileobj=bio)
|
|
96
|
+
finally:
|
|
97
|
+
tf.close()
|
|
98
|
+
|
|
99
|
+
def main(argv=None):
|
|
100
|
+
p = argparse.ArgumentParser(description="PyNeoFile (.neo) archiver", add_help=True)
|
|
101
|
+
p.add_argument("-V","--version", action="version", version=__program_name__ + " " + __version__)
|
|
102
|
+
|
|
103
|
+
p.add_argument("-i","--input", nargs="+", required=True, help="Input files/dirs or archive file ('-' = stdin for archive bytes or newline-separated paths with -c)")
|
|
104
|
+
p.add_argument("-o","--output", default=None, help="Output file or directory ('-'=stdout for archive bytes; on -e streams a TAR)")
|
|
105
|
+
|
|
106
|
+
p.add_argument("-c","--create", action="store_true", help="Create a .neo archive from inputs")
|
|
107
|
+
p.add_argument("-e","--extract", action="store_true", help="Extract an archive to --output (or stream TAR to stdout with -o -)")
|
|
108
|
+
p.add_argument("-r","--repack", action="store_true", help="Repack an archive (change compression)")
|
|
109
|
+
p.add_argument("-l","--list", action="store_true", help="List entries")
|
|
110
|
+
p.add_argument("-v","--validate", action="store_true", help="Validate checksums")
|
|
111
|
+
p.add_argument("-t","--convert", action="store_true", help="Convert zip/tar/rar/7z → .neo first")
|
|
112
|
+
|
|
113
|
+
p.add_argument("-F","--format", default="auto", help="Format magic (default 'auto' via pyneofile.ini)")
|
|
114
|
+
p.add_argument("-D","--delimiter", default=None, help="Delimiter (when not using 'auto')")
|
|
115
|
+
p.add_argument("-m","--formatver", default=None, help="Version digits (e.g. 001)")
|
|
116
|
+
|
|
117
|
+
p.add_argument("-P","--compression", default="auto", help="Compression: none|zlib|gzip|bz2|xz|auto")
|
|
118
|
+
p.add_argument("-L","--level", default=None, help="Compression level/preset")
|
|
119
|
+
p.add_argument("-C","--checksum", default="crc32", help="Checksum algorithm")
|
|
120
|
+
p.add_argument("-s","--skipchecksum", action="store_true", help="Skip checks while reading")
|
|
121
|
+
p.add_argument("-d","--verbose", action="store_true", help="Verbose listing")
|
|
122
|
+
p.add_argument("-T","--text", action="store_true", help="Treat -i - as newline-separated path list when used with -c/--create")
|
|
123
|
+
|
|
124
|
+
args = p.parse_args(argv)
|
|
125
|
+
|
|
126
|
+
formatspecs = _build_formatspecs_from_args(args)
|
|
127
|
+
inputs = args.input
|
|
128
|
+
infile0 = inputs[0]
|
|
129
|
+
compression = args.compression
|
|
130
|
+
level = None if args.level in (None, "",) else int(args.level)
|
|
131
|
+
checksum = args.checksum
|
|
132
|
+
|
|
133
|
+
# Determine active action
|
|
134
|
+
actions = ["create","extract","repack","list","validate"]
|
|
135
|
+
active = next((a for a in actions if getattr(args, a)), None)
|
|
136
|
+
if not active:
|
|
137
|
+
p.error("one of --create/--extract/--repack/--list/--validate is required")
|
|
138
|
+
|
|
139
|
+
# Helper: read archive bytes from stdin for non-create ops
|
|
140
|
+
def _maybe_archive_bytes():
|
|
141
|
+
if infile0 == '-':
|
|
142
|
+
return _stdin_bin().read()
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
if args.create:
|
|
146
|
+
if infile0 == '-' and not args.text:
|
|
147
|
+
# read newline-separated paths from stdin
|
|
148
|
+
items = [line.strip() for line in sys.stdin if line.strip() and not line.startswith('#')]
|
|
149
|
+
else:
|
|
150
|
+
items = inputs
|
|
151
|
+
|
|
152
|
+
if args.convert:
|
|
153
|
+
if not args.output:
|
|
154
|
+
p.error("--output is required (use '-' to stream to stdout)")
|
|
155
|
+
data = _convert_or_fail(infile0, (None if args.output == '-' else args.output),
|
|
156
|
+
formatspecs, checksum, compression, level)
|
|
157
|
+
if data is None:
|
|
158
|
+
return 1
|
|
159
|
+
if args.output == '-':
|
|
160
|
+
_stdout_bin().write(data)
|
|
161
|
+
return 0
|
|
162
|
+
|
|
163
|
+
if not args.output:
|
|
164
|
+
p.error("--output is required for --create (use '-' to stream)")
|
|
165
|
+
out_bytes = (args.output == '-')
|
|
166
|
+
if out_bytes:
|
|
167
|
+
data = N.pack_neo(items, None, formatspecs=formatspecs,
|
|
168
|
+
checksumtypes=(checksum, checksum, checksum),
|
|
169
|
+
compression=compression, compression_level=level)
|
|
170
|
+
_stdout_bin().write(data)
|
|
171
|
+
else:
|
|
172
|
+
N.pack_neo(items, args.output, formatspecs=formatspecs,
|
|
173
|
+
checksumtypes=(checksum, checksum, checksum),
|
|
174
|
+
compression=compression, compression_level=level)
|
|
175
|
+
if args.verbose: sys.stderr.write("created: %s\n" % args.output)
|
|
176
|
+
return 0
|
|
177
|
+
|
|
178
|
+
if args.repack:
|
|
179
|
+
src = _maybe_archive_bytes() or infile0
|
|
180
|
+
if args.convert:
|
|
181
|
+
if not args.output:
|
|
182
|
+
p.error("--output is required (use '-' to stream)")
|
|
183
|
+
data = _convert_or_fail(src, (None if args.output == '-' else args.output),
|
|
184
|
+
formatspecs, checksum, compression, level)
|
|
185
|
+
if data is None:
|
|
186
|
+
return 1
|
|
187
|
+
if args.output == '-':
|
|
188
|
+
_stdout_bin().write(data)
|
|
189
|
+
return 0
|
|
190
|
+
|
|
191
|
+
if not args.output:
|
|
192
|
+
p.error("--output is required for --repack (use '-' to stream)")
|
|
193
|
+
if args.output == '-':
|
|
194
|
+
data = N.repack_neo(src, None, formatspecs=formatspecs,
|
|
195
|
+
checksumtypes=(checksum, checksum, checksum),
|
|
196
|
+
compression=compression, compression_level=level)
|
|
197
|
+
_stdout_bin().write(data)
|
|
198
|
+
else:
|
|
199
|
+
N.repack_neo(src, args.output, formatspecs=formatspecs,
|
|
200
|
+
checksumtypes=(checksum, checksum, checksum),
|
|
201
|
+
compression=compression, compression_level=level)
|
|
202
|
+
if args.verbose: sys.stderr.write("repacked: %s -> %s\n" % (('<stdin>' if infile0 == '-' else infile0), args.output))
|
|
203
|
+
return 0
|
|
204
|
+
|
|
205
|
+
if args.extract:
|
|
206
|
+
src = _maybe_archive_bytes() or infile0
|
|
207
|
+
if args.output in (None, '.') and infile0 == '-':
|
|
208
|
+
# default would attempt to mkdir '.'; fine
|
|
209
|
+
pass
|
|
210
|
+
if args.output == '-':
|
|
211
|
+
# stream TAR to stdout
|
|
212
|
+
arr = N.archive_to_array_neo(src, formatspecs=formatspecs, listonly=False,
|
|
213
|
+
skipchecksum=args.skipchecksum, uncompress=True)
|
|
214
|
+
_emit_tar_stream_from_array(arr, _stdout_bin())
|
|
215
|
+
return 0
|
|
216
|
+
outdir = args.output or "."
|
|
217
|
+
N.unpack_neo(src, outdir, formatspecs=formatspecs, skipchecksum=args.skipchecksum, uncompress=True)
|
|
218
|
+
if args.verbose: sys.stderr.write("extracted → %s\n" % outdir)
|
|
219
|
+
return 0
|
|
220
|
+
|
|
221
|
+
if args.list:
|
|
222
|
+
src = _maybe_archive_bytes() or infile0
|
|
223
|
+
names = N.archivefilelistfiles_neo(src, formatspecs=formatspecs, advanced=args.verbose, include_dirs=True)
|
|
224
|
+
if not args.verbose:
|
|
225
|
+
for n in names: sys.stdout.write(n + "\n")
|
|
226
|
+
else:
|
|
227
|
+
for ent in names:
|
|
228
|
+
sys.stdout.write("%s\t%s\t%s\t%s\n" % (
|
|
229
|
+
ent['type'], ent['compression'], ent['size'], ent['name']
|
|
230
|
+
))
|
|
231
|
+
return 0
|
|
232
|
+
|
|
233
|
+
if args.validate:
|
|
234
|
+
src = _maybe_archive_bytes() or infile0
|
|
235
|
+
ok, details = N.archivefilevalidate_neo(src, formatspecs=formatspecs, verbose=args.verbose, return_details=True)
|
|
236
|
+
if not args.verbose:
|
|
237
|
+
sys.stdout.write("valid: %s\n" % ("yes" if ok else "no"))
|
|
238
|
+
else:
|
|
239
|
+
sys.stdout.write("valid: %s (entries: %d)\n" % ("yes" if ok else "no", len(details)))
|
|
240
|
+
for d in details:
|
|
241
|
+
sys.stdout.write("%4d %s h:%s j:%s c:%s\n" % (
|
|
242
|
+
d['index'], d['name'], d['header_ok'], d['json_ok'], d['content_ok']
|
|
243
|
+
))
|
|
244
|
+
return 0
|
|
245
|
+
|
|
246
|
+
p.error("one of --create/--extract/--repack/--list/--validate is required")
|
|
247
|
+
|
|
248
|
+
if __name__ == "__main__":
|
|
249
|
+
sys.exit(main())
|