Small update
[PyCatFile.git] / catfile.py
blob9006332a31d8bd36b12f3d20a7e1c47f1f117158
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
4 '''
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the Revised BSD License.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 Revised BSD License for more details.
13 Copyright 2018-2024 Cool Dude 2k - http://idb.berlios.de/
14 Copyright 2018-2024 Game Maker 2k - http://intdb.sourceforge.net/
15 Copyright 2018-2024 Kazuki Przyborowski - https://github.com/KazukiPrzyborowski
17 $FileInfo: catfile.py - Last Update: 7/10/2024 Ver. 0.13.12 RC 1 - Author: cooldude2k $
18 '''
20 from __future__ import absolute_import, division, print_function, unicode_literals, generators, with_statement, nested_scopes
21 import os
22 import sys
23 import argparse
24 import pycatfile
25 import binascii
27 # Conditional import and signal handling for Unix-like systems
28 if os.name != 'nt': # Not Windows
29 import signal
31 def handler(signum, frame):
32 pycatfile.VerbosePrintOut(
33 "Received SIGPIPE, exiting gracefully.", "info")
34 sys.exit(0)
35 signal.signal(signal.SIGPIPE, handler)
37 rarfile_support = pycatfile.rarfile_support
38 py7zr_support = pycatfile.py7zr_support
40 if(sys.version[0] == "2"):
41 try:
42 from io import StringIO, BytesIO
43 except ImportError:
44 try:
45 from cStringIO import StringIO
46 from cStringIO import StringIO as BytesIO
47 except ImportError:
48 from StringIO import StringIO
49 from StringIO import StringIO as BytesIO
50 elif(sys.version[0] >= "3"):
51 from io import StringIO, BytesIO
52 else:
53 teststringio = 0
54 if(teststringio <= 0):
55 try:
56 from cStringIO import StringIO as BytesIO
57 teststringio = 1
58 except ImportError:
59 teststringio = 0
60 if(teststringio <= 0):
61 try:
62 from StringIO import StringIO as BytesIO
63 teststringio = 2
64 except ImportError:
65 teststringio = 0
66 if(teststringio <= 0):
67 try:
68 from io import BytesIO
69 teststringio = 3
70 except ImportError:
71 teststringio = 0
73 __project__ = pycatfile.__project__
74 __program_name__ = pycatfile.__program_name__
75 __file_format_name__ = pycatfile.__file_format_name__
76 __file_format_lower__ = pycatfile.__file_format_lower__
77 __file_format_magic__ = pycatfile.__file_format_magic__
78 __file_format_len__ = pycatfile.__file_format_len__
79 __file_format_hex__ = pycatfile.__file_format_hex__
80 __file_format_delimiter__ = pycatfile.__file_format_delimiter__
81 __file_format_list__ = pycatfile.__file_format_list__
82 __use_new_style__ = pycatfile.__use_new_style__
83 __use_advanced_list__ = pycatfile.__use_advanced_list__
84 __use_alt_inode__ = pycatfile.__use_alt_inode__
85 __project_url__ = pycatfile.__project_url__
86 __version_info__ = pycatfile.__version_info__
87 __version_date_info__ = pycatfile.__version_date_info__
88 __version_date__ = pycatfile.__version_date__
89 __version_date_plusrc__ = pycatfile.__version_date_plusrc__
90 __version__ = pycatfile.__version__
92 # Initialize the argument parser
93 argparser = argparse.ArgumentParser(
94 description="Manipulate concatenated files.", conflict_handler="resolve", add_help=True)
96 # Version information
97 argparser.add_argument("-V", "--version", action="version",
98 version=__program_name__ + " " + __version__)
99 # Input and output specifications
100 argparser.add_argument(
101 "-i", "--input", help="Specify the file(s) to concatenate or the concatenated file to extract.", required=True)
102 argparser.add_argument("-o", "--output", default=None,
103 help="Specify the name for the extracted or output concatenated files.")
104 # Operations
105 argparser.add_argument("-c", "--create", action="store_true",
106 help="Perform only the concatenation operation.")
107 argparser.add_argument("-e", "--extract", action="store_true",
108 help="Perform only the extraction operation.")
109 argparser.add_argument("-t", "--convert", action="store_true",
110 help="Convert a tar/zip/rar/7zip file to a concatenated file.")
111 argparser.add_argument("-r", "--repack", action="store_true",
112 help="Re-concatenate files, fixing checksum errors if any.")
113 # File manipulation options
114 argparser.add_argument(
115 "-F", "--format", default=__file_format_list__[0], help="Specify the format to use.")
116 argparser.add_argument(
117 "-D", "--delimiter", default=__file_format_list__[5], help="Specify the delimiter to use.")
118 argparser.add_argument(
119 "-m", "--formatver", default=__file_format_list__[6], help="Specify the format version.")
120 argparser.add_argument("-l", "--list", action="store_true",
121 help="List files included in the concatenated file.")
122 # Compression options
123 argparser.add_argument("-P", "--compression", default="auto",
124 help="Specify the compression method to use for concatenation.")
125 argparser.add_argument("-L", "--level", default=None,
126 help="Specify the compression level for concatenation.")
127 argparser.add_argument("-W", "--wholefile", action="store_true",
128 help="Whole file compression method to use for concatenation.")
129 # Checksum and validation
130 argparser.add_argument("-v", "--validate", action="store_true",
131 help="Validate concatenated file checksums.")
132 argparser.add_argument("-C", "--checksum", default="crc32",
133 help="Specify the type of checksum to use. The default is crc32.")
134 argparser.add_argument("-s", "--skipchecksum", action="store_true",
135 help="Skip the checksum check of files.")
136 # Permissions and metadata
137 argparser.add_argument("-p", "--preserve", action="store_false",
138 help="Do not preserve permissions and timestamps of files.")
139 # Miscellaneous
140 argparser.add_argument("-d", "--verbose", action="store_true",
141 help="Enable verbose mode to display various debugging information.")
142 argparser.add_argument("-T", "--text", action="store_true",
143 help="Read file locations from a text file.")
144 # Parse the arguments
145 getargs = argparser.parse_args()
147 fname = getargs.format
148 fnamelower = fname.lower()
149 fnamemagic = fname
150 fnamelen = len(fname)
151 fnamehex = binascii.hexlify(fname.encode("UTF-8")).decode("UTF-8")
152 fnamesty = __use_new_style__
153 fnamelst = __use_advanced_list__
154 fnameino = __use_alt_inode__
155 fnamelist = [fname, fnamemagic, fnamelower, fnamelen, fnamehex,
156 getargs.delimiter, getargs.formatver, fnamesty, fnamelst, fnameino]
157 fnamedict = {'format_name': fname, 'format_magic': fnamemagic, 'format_lower': fnamelower, 'format_len': fnamelen, 'format_hex': fnamehex,
158 'format_delimiter': getargs.delimiter, 'format_ver': getargs.formatver, 'new_style': fnamesty, 'use_advanced_list': fnamelst, 'use_alt_inode': fnameino}
160 # Determine the primary action based on user input
161 actions = ['create', 'extract', 'list', 'repack', 'validate']
162 active_action = next(
163 (action for action in actions if getattr(getargs, action)), None)
165 # Execute the appropriate functions based on determined actions and arguments
166 if active_action:
167 if active_action == 'create':
168 if getargs.convert:
169 checkcompressfile = pycatfile.CheckCompressionSubType(
170 getargs.input, fnamedict, True)
171 if(checkcompressfile == "catfile"):
172 tmpout = pycatfile.RePackArchiveFile(getargs.input, getargs.output, getargs.compression, getargs.wholefile,
173 getargs.level, False, 0, 0, getargs.checksum, getargs.skipchecksum, [], fnamedict, getargs.verbose, False)
174 else:
175 tmpout = pycatfile.PackArchiveFileFromInFile(
176 getargs.input, getargs.output, getargs.compression, getargs.wholefile, getargs.level, getargs.checksum, [], fnamedict, getargs.verbose, False)
177 if(not tmpout):
178 sys.exit(1)
179 else:
180 pycatfile.PackArchiveFile(getargs.input, getargs.output, getargs.text, getargs.compression,
181 getargs.wholefile, getargs.level, False, getargs.checksum, [], fnamedict, getargs.verbose, False)
182 elif active_action == 'repack':
183 if getargs.convert:
184 checkcompressfile = pycatfile.CheckCompressionSubType(
185 getargs.input, fnamedict, True)
186 if(checkcompressfile == "catfile"):
187 pycatfile.RePackArchiveFile(getargs.input, getargs.output, getargs.compression, getargs.wholefile, getargs.level,
188 False, 0, 0, getargs.checksum, getargs.skipchecksum, [], fnamedict, getargs.verbose, False)
189 else:
190 pycatfile.PackArchiveFileFromInFile(getargs.input, getargs.output, getargs.compression,
191 getargs.wholefile, getargs.level, getargs.checksum, [], fnamedict, getargs.verbose, False)
192 if(not tmpout):
193 sys.exit(1)
194 else:
195 pycatfile.RePackArchiveFile(getargs.input, getargs.output, getargs.compression, getargs.wholefile, getargs.level,
196 False, 0, 0, getargs.checksum, getargs.skipchecksum, [], fnamedict, getargs.verbose, False)
197 elif active_action == 'extract':
198 if getargs.convert:
199 checkcompressfile = pycatfile.CheckCompressionSubType(
200 getargs.input, fnamedict, True)
201 tempout = BytesIO()
202 if(checkcompressfile == "catfile"):
203 tmpout = pycatfile.RePackArchiveFile(getargs.input, tempout, getargs.compression, getargs.wholefile,
204 getargs.level, False, 0, 0, getargs.checksum, getargs.skipchecksum, [], fnamedict, False, False)
205 else:
206 tmpout = pycatfile.PackArchiveFileFromInFile(
207 getargs.input, tempout, getargs.compression, getargs.wholefile, getargs.level, getargs.checksum, [], fnamedict, False, False)
208 if(not tmpout):
209 sys.exit(1)
210 getargs.input = tempout
211 pycatfile.UnPackArchiveFile(getargs.input, getargs.output, False, 0, 0, getargs.skipchecksum,
212 fnamedict, getargs.verbose, getargs.preserve, getargs.preserve, False)
213 elif active_action == 'list':
214 if getargs.convert:
215 checkcompressfile = pycatfile.CheckCompressionSubType(
216 getargs.input, fnamedict, True)
217 if(checkcompressfile == "catfile"):
218 tmpout = pycatfile.ArchiveFileListFiles(
219 getargs.input, 0, 0, getargs.skipchecksum, fnamedict, getargs.verbose, False)
220 else:
221 tmpout = pycatfile.InFileListFiles(
222 getargs.input, getargs.verbose, fnamedict, False)
223 if(not tmpout):
224 sys.exit(1)
225 else:
226 pycatfile.ArchiveFileListFiles(
227 getargs.input, 0, 0, getargs.skipchecksum, fnamedict, getargs.verbose, False)
228 elif active_action == 'validate':
229 if getargs.convert:
230 checkcompressfile = pycatfile.CheckCompressionSubType(
231 getargs.input, fnamedict, True)
232 tempout = BytesIO()
233 if(checkcompressfile == "catfile"):
234 tmpout = pycatfile.RePackArchiveFile(getargs.input, tempout, getargs.compression, getargs.wholefile,
235 getargs.level, False, 0, 0, getargs.checksum, getargs.skipchecksum, [], fnamedict, False, False)
236 else:
237 tmpout = pycatfile.PackArchiveFileFromInFile(
238 getargs.input, tempout, getargs.compression, getargs.wholefile, getargs.level, getargs.checksum, [], fnamedict, False, False)
239 getargs.input = tempout
240 if(not tmpout):
241 sys.exit(1)
242 fvalid = pycatfile.ArchiveFileValidate(
243 getargs.input, fnamedict, getargs.verbose, False)
244 if(not getargs.verbose):
245 import sys
246 import logging
247 logging.basicConfig(format="%(message)s",
248 stream=sys.stdout, level=logging.DEBUG)
249 if(fvalid):
250 pycatfile.VerbosePrintOut("File is valid: \n" + str(getargs.input))
251 else:
252 pycatfile.VerbosePrintOut(
253 "File is invalid: \n" + str(getargs.input))