mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-22 22:19:14 +01:00
ff69030643
* isv support + usb/dd improvements
* make room for saves
* update offset
* fixed debug address
* idk
* exception
* ironed out all broken stuff
* cleanup
* return epc fix
* better
* more cleanup
* even more cleanup
* mooore cleanup
* fixed printf
* no assert
* improved docker build, pyft232 instead of pyserial
* fixed displaying long message strings
description test
* just straight cleanup
* smallest cleanup
* PAL
* cpu buffer
* n64 bootloader done
* super slow usb storage reading implemented
* reduced buffer size
* usb gets fast
* little cleanup
* double buffered reads
* removed separate event id
* ISV in hardware finally
* small exception changes
* mac testing
* py spacing
* fsd write, rtc, isv and reset fixes
* fixxx
* good stopping point
* usb fixed?
* pretend we have 128 MB sdram
* backup
* chmod
* test
* test done
* more tests
* user rm
* help
* final fix
* updated component values
* nice asset names
* cic 64dd support
* ddipl enable separation
* pre DMA rewrite, created dedicated buffer memory space, simplified code
* dma rewrite, needs testing
* moved xml
* dd basics
* timing
* 64dd working yet again, isv brought back, dma fixes, usb path rewrite, pc code rewrite
* added usb read functionality, general cleanup
* changed mem addressing
* added fpga flash update access
* added mcu update
* chmod
* little cleanup
* update format and stuff
* fixes
* uninitialized fix
* small fixes
* update fixes
* update stuff done
* fpga update tested
* build time fix
* boot fix
* test timing
* readme test
* test 2
* reports
* testseet
* final
* build test
* forgot
* button and naming
* General cleanup
And multiline commit message test
* Exception screen UI touch ups
* display separation and tests beginning
* pc software update
* pc software done
* timing test
* delete launch.json
* sw fixes
* fixed button hole diameter in shell
* small cleanup, rpi testing
* shell fillet fix, pc rtc printing
* added cfg lock mechanism
* moved lock to cfg address space
* extended ROM and ISV fixes
* preliminary sd card support
* little sd card cleanup
* sd menu fixes
* 5 second limit
* reduced shell thickness
* basic led act blinking
* faster sd menu loading
* inst cache invalidate
* sd card writing is working
* SD card CSD and CID registers
* wait for previous command
* led error codes
* fixed cfg_translate_address use
* 64dd from sd card working
* 64dd speedup and button handling
* delayed address latching cycle - might break other builds, needs testing
* bootloader improvements
* small fixes
* return previous cfg when setting new
* cache stuff
* unfloader debug protocol support
* UNFLoader style debug command line support
* requirements.txt
* shell groove fillet
* reset state inside controller
* fixed fast PI read, added PI R/W fifo debug info
* PI access prioritize
* SD clock stop when RX FIFO is more than half full
* flash erase method change
* CFG error handling, TLOZ MM debug ISV support
* CIC5167 support
* general fixes
* USB unplugged cable handling
* turn off led when changing between error/act modes
* rtc 2 bit clock stop support
* line endings
* Revert "line endings"
This reverts commit d0ddfe5ec7
.
* PI address debug
* readme test
* diagram update
* diagram background
* diagram background
* diagram background
* updated readme
229 lines
7.5 KiB
Python
Executable File
229 lines
7.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import math
|
|
import os
|
|
import platform
|
|
import sys
|
|
from binascii import crc32
|
|
from datetime import datetime
|
|
from io import BufferedRandom
|
|
|
|
|
|
|
|
class JedecError(Exception):
|
|
pass
|
|
|
|
|
|
class JedecFile:
|
|
__fuse_length: int = 0
|
|
__fuse_offset: int = 0
|
|
__fuse_data: bytes = b''
|
|
__byte_buffer: int = 0
|
|
|
|
def __handle_q_field(self, f: BufferedRandom) -> None:
|
|
type = f.read(1)
|
|
if (type == b'F'):
|
|
value = b''
|
|
while (True):
|
|
data = f.read(1)
|
|
if (data == b'*'):
|
|
value = value.decode('ascii', errors='backslashreplace')
|
|
if (not value.isdecimal()):
|
|
raise JedecError('Invalid Q field data')
|
|
self.__fuse_length = int(value)
|
|
break
|
|
else:
|
|
value += data
|
|
else:
|
|
self.__ignore_field(f)
|
|
|
|
def __handle_l_field(self, f: BufferedRandom) -> None:
|
|
if (self.__fuse_length <= 0):
|
|
raise JedecError('Found fuse data before declaring fuse count')
|
|
|
|
offset = b''
|
|
while (True):
|
|
data = f.read(1)
|
|
if (data >= b'0' and data <= b'9'):
|
|
offset += data
|
|
elif (data == b'\r' or data == b'\n'):
|
|
offset = offset.decode('ascii', errors='backslashreplace')
|
|
if (not offset.isdecimal()):
|
|
raise JedecError('Invalid L field offset data')
|
|
offset = int(offset)
|
|
if (offset != self.__fuse_offset):
|
|
raise JedecError('Fuse data is not continuous')
|
|
break
|
|
else:
|
|
raise JedecError('Unexpected byte inside L field offset data')
|
|
|
|
data = b''
|
|
while (True):
|
|
data = f.read(1)
|
|
if (data == b'0' or data == b'1'):
|
|
shift = (7 - (self.__fuse_offset % 8))
|
|
self.__byte_buffer |= (1 if data == b'1' else 0) << shift
|
|
if (((self.__fuse_offset % 8) == 7) or (self.__fuse_offset == (self.__fuse_length - 1))):
|
|
self.__fuse_data += int.to_bytes(self.__byte_buffer, 1, byteorder='little')
|
|
self.__byte_buffer = 0
|
|
self.__fuse_offset += 1
|
|
elif (data == b'\r' or data == b'\n'):
|
|
pass
|
|
elif (data == b'*'):
|
|
break
|
|
elif (data == b''):
|
|
raise JedecError('Unexpected end of file')
|
|
else:
|
|
raise JedecError('Unexpected byte inside L field fuse data')
|
|
|
|
def __ignore_field(self, f: BufferedRandom) -> None:
|
|
data = None
|
|
while (data != b'*'):
|
|
data = f.read(1)
|
|
if (data == b''):
|
|
raise JedecError('Unexpected end of file')
|
|
|
|
def parse(self, path: str) -> bytes:
|
|
self.__fuse_length = 0
|
|
self.__fuse_offset = 0
|
|
self.__fuse_data = b''
|
|
self.__byte_buffer = 0
|
|
|
|
field = None
|
|
with open(path, 'rb+') as f:
|
|
while (True):
|
|
field = f.read(1)
|
|
if (field == b'\x02'):
|
|
f.seek(-1, os.SEEK_CUR)
|
|
break
|
|
elif (field == b''):
|
|
raise JedecError('Unexpected end of file')
|
|
|
|
while (True):
|
|
field = f.read(1)
|
|
if (field == b'Q'):
|
|
self.__handle_q_field(f)
|
|
elif (field == b'L'):
|
|
self.__handle_l_field(f)
|
|
elif (field == b'\r' or field == b'\n'):
|
|
pass
|
|
elif (field == b'\x03'):
|
|
break
|
|
elif (field == b''):
|
|
raise JedecError('Unexpected end of file')
|
|
else:
|
|
self.__ignore_field(f)
|
|
|
|
if (self.__fuse_length <= 0):
|
|
raise JedecError('No fuse data found')
|
|
|
|
if (self.__fuse_offset != self.__fuse_length):
|
|
raise JedecError('Missing fuse data inside JEDEC file')
|
|
|
|
if (len(self.__fuse_data) != math.ceil(self.__fuse_length / 8)):
|
|
raise JedecError('Missing fuse data inside JEDEC file')
|
|
|
|
return self.__fuse_data
|
|
|
|
|
|
class SC64UpdateData:
|
|
__UPDATE_TOKEN = b'SC64 Update v2.0'
|
|
|
|
__CHUNK_ID_UPDATE_INFO = 1
|
|
__CHUNK_ID_MCU_DATA = 2
|
|
__CHUNK_ID_FPGA_DATA = 3
|
|
__CHUNK_ID_BOOTLOADER_DATA = 4
|
|
|
|
__data = b''
|
|
|
|
def __int_to_bytes(self, value: int) -> bytes:
|
|
return value.to_bytes(4, byteorder='little')
|
|
|
|
def __align(self, value: int) -> int:
|
|
if (value % 16 != 0):
|
|
value += (16 - (value % 16))
|
|
return value
|
|
|
|
def __add_chunk(self, id: int, data: bytes) -> None:
|
|
chunk = b''
|
|
chunk_length = (16 + len(data))
|
|
aligned_length = self.__align(chunk_length)
|
|
chunk += self.__int_to_bytes(id)
|
|
chunk += self.__int_to_bytes(aligned_length - 8)
|
|
chunk += self.__int_to_bytes(crc32(data))
|
|
chunk += self.__int_to_bytes(len(data))
|
|
chunk += data
|
|
chunk += bytes([0] * (aligned_length - chunk_length))
|
|
self.__data += chunk
|
|
|
|
def create_update_data(self) -> None:
|
|
self.__data = self.__UPDATE_TOKEN
|
|
|
|
def add_update_info(self, data: bytes) -> None:
|
|
self.__add_chunk(self.__CHUNK_ID_UPDATE_INFO, data)
|
|
|
|
def add_mcu_data(self, data: bytes) -> None:
|
|
self.__add_chunk(self.__CHUNK_ID_MCU_DATA, data)
|
|
|
|
def add_fpga_data(self, data: bytes) -> None:
|
|
self.__add_chunk(self.__CHUNK_ID_FPGA_DATA, data)
|
|
|
|
def add_bootloader_data(self, data: bytes) -> None:
|
|
self.__add_chunk(self.__CHUNK_ID_BOOTLOADER_DATA, data)
|
|
|
|
def get_update_data(self) -> bytes:
|
|
return self.__data
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description='SC64 update file generator')
|
|
parser.add_argument('--git', metavar='git', required=False, help='git text to embed in update info')
|
|
parser.add_argument('--mcu', metavar='mcu_path', required=False, help='path to MCU update data')
|
|
parser.add_argument('--fpga', metavar='fpga_path', required=False, help='path to FPGA update data')
|
|
parser.add_argument('--boot', metavar='bootloader_path', required=False, help='path to N64 bootloader update data')
|
|
parser.add_argument('output', metavar='output_path', help='path to final update data')
|
|
|
|
if (len(sys.argv) <= 1):
|
|
parser.print_help()
|
|
parser.exit()
|
|
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
update = SC64UpdateData()
|
|
update.create_update_data()
|
|
|
|
hostname = platform.node()
|
|
creation_datetime = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
|
|
info = [
|
|
f'build system: [{hostname}]',
|
|
f'creation datetime: [{creation_datetime}]',
|
|
]
|
|
if (args.git):
|
|
info.append(args.git)
|
|
update_info = ' '.join(info)
|
|
print(update_info)
|
|
update.add_update_info(update_info.encode())
|
|
|
|
if (args.mcu):
|
|
with open(args.mcu, 'rb+') as f:
|
|
update.add_mcu_data(f.read())
|
|
|
|
if (args.fpga):
|
|
update.add_fpga_data(JedecFile().parse(args.fpga))
|
|
|
|
if (args.boot):
|
|
with open(args.boot, 'rb+') as f:
|
|
update.add_bootloader_data(f.read())
|
|
|
|
with open(args.output, 'wb+') as f:
|
|
f.write(update.get_update_data())
|
|
except JedecError as e:
|
|
print(f'Error while parsing FPGA update data: {e}')
|
|
exit(-1)
|
|
except IOError as e:
|
|
print(f'IOError: {e}')
|
|
exit(-1)
|