145 lines
4.4 KiB
Python
145 lines
4.4 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
import argparse
|
||
|
|
import json
|
||
|
|
import random
|
||
|
|
import sys
|
||
|
|
|
||
|
|
# --- Configuration for Crop Fuzzing ---
|
||
|
|
MAX_SUM = 0.99
|
||
|
|
MIN_CROP_SIZE = 0.05
|
||
|
|
PRECISION = 4
|
||
|
|
|
||
|
|
# --- Configuration for Canvas Fuzzing ---
|
||
|
|
# The desired size of the fuzzed rectangle.
|
||
|
|
FUZZED_RECT_WIDTH = 12
|
||
|
|
FUZZED_RECT_HEIGHT = 8
|
||
|
|
|
||
|
|
# The boundaries of the entire canvas.
|
||
|
|
# A=1, B=2, ..., Z=26, AA=27, ..., AE=31
|
||
|
|
CANVAS_MIN_COL = 1 # 'A'
|
||
|
|
CANVAS_MIN_ROW = 1
|
||
|
|
CANVAS_MAX_COL = 31 # 'AE'
|
||
|
|
CANVAS_MAX_ROW = 55
|
||
|
|
|
||
|
|
|
||
|
|
def num_to_col_str(n):
|
||
|
|
"""Converts a 1-indexed column number to its spreadsheet-style string.
|
||
|
|
e.g., 1 -> 'A', 27 -> 'AA'
|
||
|
|
"""
|
||
|
|
string = ""
|
||
|
|
while n > 0:
|
||
|
|
n, remainder = divmod(n - 1, 26)
|
||
|
|
string = chr(65 + remainder) + string
|
||
|
|
return string
|
||
|
|
|
||
|
|
|
||
|
|
def generate_fuzzed_crop_values():
|
||
|
|
"""
|
||
|
|
Generates a random offset and size that adhere to the constraint:
|
||
|
|
offset + size <= MAX_SUM
|
||
|
|
"""
|
||
|
|
offset = random.uniform(0.0, MAX_SUM - MIN_CROP_SIZE)
|
||
|
|
max_size_allowed = MAX_SUM - offset
|
||
|
|
size = random.uniform(MIN_CROP_SIZE, max_size_allowed)
|
||
|
|
return round(offset, PRECISION), round(size, PRECISION)
|
||
|
|
|
||
|
|
|
||
|
|
def generate_fuzzed_canvas_rect():
|
||
|
|
"""
|
||
|
|
Generates a random canvas_rect of a fixed size (12x8) within the
|
||
|
|
overall canvas boundary (A1:AE55).
|
||
|
|
"""
|
||
|
|
# 1. Determine the valid range for the top-left corner.
|
||
|
|
# To fit a 12-wide rect inside a 31-wide canvas, the top-left
|
||
|
|
# column can start at 1 and must end early enough.
|
||
|
|
# Last possible start col = 31 - 12 + 1 = 20
|
||
|
|
max_start_col = CANVAS_MAX_COL - FUZZED_RECT_WIDTH + 1
|
||
|
|
|
||
|
|
# Same logic for rows.
|
||
|
|
# Last possible start row = 55 - 8 + 1 = 48
|
||
|
|
max_start_row = CANVAS_MAX_ROW - FUZZED_RECT_HEIGHT + 1
|
||
|
|
|
||
|
|
# 2. Pick a random top-left corner from the valid range.
|
||
|
|
start_col = random.randint(CANVAS_MIN_COL, max_start_col)
|
||
|
|
start_row = random.randint(CANVAS_MIN_ROW, max_start_row)
|
||
|
|
|
||
|
|
# 3. Calculate the bottom-right corner based on the fuzzed size.
|
||
|
|
end_col = start_col + FUZZED_RECT_WIDTH - 1
|
||
|
|
end_row = start_row + FUZZED_RECT_HEIGHT - 1
|
||
|
|
|
||
|
|
# 4. Convert numerical columns back to string representation.
|
||
|
|
start_col_str = num_to_col_str(start_col)
|
||
|
|
end_col_str = num_to_col_str(end_col)
|
||
|
|
|
||
|
|
# 5. Format the final string.
|
||
|
|
return f"{start_col_str}{start_row}:{end_col_str}{end_row}"
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
"""
|
||
|
|
Main function to read a JSON file, process the data, and either print
|
||
|
|
to the console or write to an output file.
|
||
|
|
"""
|
||
|
|
parser = argparse.ArgumentParser(
|
||
|
|
description="Fuzzes 'canvas_rect', 'crop_offset', and 'crop_size' in a JSON file.",
|
||
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
||
|
|
)
|
||
|
|
parser.add_argument(
|
||
|
|
"input_file", help="Path to the input JSON file to be processed."
|
||
|
|
)
|
||
|
|
parser.add_argument(
|
||
|
|
"-o",
|
||
|
|
"--output_file",
|
||
|
|
help="Path to the output JSON file. If not provided, the result is printed to the console.",
|
||
|
|
)
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
try:
|
||
|
|
with open(args.input_file, "r") as f:
|
||
|
|
print(f"Reading data from '{args.input_file}'...")
|
||
|
|
data = json.load(f)
|
||
|
|
except FileNotFoundError:
|
||
|
|
print(f"Error: The file '{args.input_file}' was not found.", file=sys.stderr)
|
||
|
|
sys.exit(1)
|
||
|
|
except json.JSONDecodeError:
|
||
|
|
print(
|
||
|
|
f"Error: The file '{args.input_file}' is not a valid JSON file.",
|
||
|
|
file=sys.stderr,
|
||
|
|
)
|
||
|
|
sys.exit(1)
|
||
|
|
except Exception as e:
|
||
|
|
print(f"An unexpected error occurred: {e}", file=sys.stderr)
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
# Iterate through each item and update it with fuzzed values
|
||
|
|
for item in data:
|
||
|
|
# Fuzz crop offset and size
|
||
|
|
x_offset, x_size = generate_fuzzed_crop_values()
|
||
|
|
y_offset, y_size = generate_fuzzed_crop_values()
|
||
|
|
|
||
|
|
item["crop_offset"] = [x_offset, y_offset]
|
||
|
|
item["crop_size"] = [x_size, y_size]
|
||
|
|
|
||
|
|
# Fuzz the canvas rect
|
||
|
|
item["canvas_rect"] = generate_fuzzed_canvas_rect()
|
||
|
|
|
||
|
|
# Handle the output
|
||
|
|
if args.output_file:
|
||
|
|
try:
|
||
|
|
with open(args.output_file, "w") as f:
|
||
|
|
json.dump(data, f, indent=2)
|
||
|
|
print(f"Successfully wrote fuzzed data to '{args.output_file}'.")
|
||
|
|
except IOError as e:
|
||
|
|
print(
|
||
|
|
f"Error: Could not write to file '{args.output_file}': {e}",
|
||
|
|
file=sys.stderr,
|
||
|
|
)
|
||
|
|
sys.exit(1)
|
||
|
|
else:
|
||
|
|
print("\n--- Fuzzed JSON ---")
|
||
|
|
print(json.dumps(data, indent=2))
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|