From 7f873901025f728bd253ceafcb561cb3fdf80b4a Mon Sep 17 00:00:00 2001 From: PhoenixBound Date: Wed, 30 Oct 2024 13:49:30 -0500 Subject: [PATCH] Fix TODOs in blocks code (untested) --- coilsnake/model/common/blocks.py | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/coilsnake/model/common/blocks.py b/coilsnake/model/common/blocks.py index 805582d..35572da 100644 --- a/coilsnake/model/common/blocks.py +++ b/coilsnake/model/common/blocks.py @@ -1,4 +1,5 @@ import array +from bisect import bisect import copy import os from zlib import crc32 @@ -262,11 +263,32 @@ def is_allocated(self, range): def deallocate(self, range): check_range_validity(range, self.size) - # TODO do some check so that unallocated ranges don't overlap - # TODO attach contiguous unallocated ranges if possible - - self.unallocated_ranges.append(range) - self.unallocated_ranges.sort() + # Unallocated ranges should never overlap; otherwise we may write + # multiple pieces of data to the same addresses! + # Since we keep self.unallocated_ranges in sorted order, it's okay to + # use bisect (binary search) to find the place at which to insert it + insertion_point = bisect(self.unallocated_ranges, range) + previous_range = self.unallocated_ranges[insertion_point - 1] if insertion_point > 0 else None + next_range = self.unallocated_ranges[insertion_point] if insertion_point < len(self.unallocated_ranges) else None + if previous_range is not None and range[0] <= previous_range[1]: + raise InvalidArgumentError(f"Range {range} overlaps with existing unallocated range {previous_range}!") + if next_range is not None and next_range[0] <= range[1]: + raise InvalidArgumentError(f"Range {range} overlaps with existing unallocated range {next_range}!") + + # Attach contiguous unallocated ranges if possible (not across banks) + # This is kinda SNES specific, and (Ex)HiROM specific... but this is an + # EarthBound hacking tool, so that should be fine + merge_with_previous = (range[0] & 0xFFFF) != 0 and previous_range is not None and (previous_range[1] == range[0] - 1) + merge_with_next = (range[1] & 0xFFFF) != 0xFFFF and next_range is not None and (range[1] + 1 == next_range[0]) + if merge_with_previous and merge_with_next: + self.unallocated_ranges[insertion_point - 1] = (previous_range[0], next_range[1]) + del self.unallocated_ranges[insertion_point] + elif merge_with_previous: + self.unallocated_ranges[insertion_point - 1] = (previous_range[0], range[1]) + elif merge_with_next: + self.unallocated_ranges[insertion_point] = (range[0], next_range[1]) + else: + self.unallocated_ranges.insert(insertion_point, range) def allocate(self, data=None, size=None, can_write_to=None): if data is None and size is None: