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.
@@ -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
+ ![](logo.png?raw=true)
@@ -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
+ ![](logo.png?raw=true)
@@ -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
+ pyneofile
@@ -0,0 +1,2 @@
1
+ A tar like file format name NeoFile
2
+ ![](logo.png?raw=true)
@@ -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())