pup_module.py Bug Report
Closed this issue · 0 comments
seregonwar commented
Here are the bugs I found in the code:
- In the
Pup
class constructor, themagic
,version
,mode
,entry_table_offset
, andentry_table_count
parameters should not have default values. These values are extracted from the file and should be passed as arguments when creating aPup
object. - In the
extract_pup_file
function, thefile_path
variable is assigned the result of theselect_file
function, but the function call is missing parentheses. It should befile_path = select_file()
. - In the
Pup
class constructor, theentry_table
attribute is initialized as an empty list, but it is never populated with actual entry data. This will cause an error when trying to iterate overpup.entry_table
later in the code. - In the
extract_pup_file
function, theentry_table_size
variable is extracted from the file buffer, but it is not used to calculate the number of entries in the entry table. Instead, the number of entries is calculated asentry_table_size // 24
, which assumes that each entry is exactly 24 bytes long. This may not be true, so it's better to usestruct.unpack
to extract the actual number of entries from the buffer. - In the
extract_pup_file
function, theentry_data
variable is assigned the result of decompressing the entry data using thelzma.decompress
function. However, if the entry is not compressed (i.e.,entry_compression
isFalse
), this will cause an error. Instead,entry_data
should be assignedbuffer[entry_data_offset:entry_data_offset+entry_data_size]
in this case. - The
extract_pup_file
function is called unconditionally at the end of the script, even if the user does not select a file. This will cause an error. Instead, the function call should be inside aif __name__ == '__main__':
block to ensure that it is only called when the script is run directly.
Here's the corrected code:
import os
import struct
import lzma
import tkinter as tk
from tkinter import filedialog, messagebox
class Pup:
MAGIC = b'MYPUP123'
def __init__(self, file_path, magic, version, mode, entry_table_offset, entry_table_count, entry_table):
self.file_path = file_path
self.magic = magic
self.version = version
self.mode = mode
self.entry_table_offset = entry_table_offset
self.entry_table_count = entry_table_count
self.entry_table = entry_table
def select_file():
root = tk.Tk()
root.withdraw()
return filedialog.askopenfilename(filetypes=[('PUP files', '*.pup')])
def extract_pup_file():
file_path = select_file()
# Extract information from the file buffer
with open(file_path, 'rb') as f:
buffer = f.read()
padding_len = len(buffer) % 16
if padding_len != 0:
raise ValueError(f"The file {os.path.basename(file_path)} has incorrect padding.")
magic = buffer[:8]
version = buffer[8:12]
mode = buffer[12:16]
entry_table_offset = struct.unpack("<Q", buffer[32:40])[0]
entry_table_count = struct.unpack("<I", buffer[48:52])[0]
if magic != Pup.MAGIC:
raise ValueError(f"The file {os.path.basename(file_path)} has an incorrect magic number.")
# Extract the entry table
entry_table = []
for i in range(entry_table_count):
offset = entry_table_offset + i * 24
entry = struct.unpack("<6sIHQQI", buffer[offset:offset+24])
entry_table.append(entry)
# Create a Pup object with the extracted information
pup = Pup(file_path, magic, version, mode, entry_table_offset, entry_table_count, entry_table)
# Extract the entry data
dir_path = os.path.dirname(file_path)
pup_name = os.path.basename(file_path)[:-4]
pup_dir_path = os.path.join(dir_path, pup_name)
if not os.path.exists(pup_dir_path):
os.makedirs(pup_dir_path)
for entry in pup.entry_table:
entry_type = entry[0]
entry_flags = entry[1]
entry_compression = entry[2]
entry_uncompressed_size = entry[3]
entry_compressed_size = entry[4]
entry_hash = entry[5]
entry_data_offset = entry[6]
entry_data_size = entry_compressed_size if entry_compression else entry_uncompressed_size
entry_data_offset = entry_data_offset + len(buffer) if entry_data_offset > 0 else entry_data_offset
if entry_compression:
entry_data = lzma.decompress(buffer[entry_data_offset:entry_data_offset+entry_compressed_size])
else:
entry_data = buffer[entry_data_offset:entry_data_offset+entry_data_size]
# Write the entry data to a file
file_name = f"{i:06d}.bin"
file_path = os.path.join(pup_dir_path, file_name)
with open(file_path, 'wb') as f:
f.write(entry_data)
messagebox.showinfo("Information", "Extraction completed successfully.")
if __name__ == '__main__':
extract_pup_file()