mirror of
https://github.com/Polprzewodnikowy/N64FlashcartMenu.git
synced 2024-11-25 03:56:54 +01:00
Merge branch 'main' into ed64-basic
This commit is contained in:
commit
7347de9bbb
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -28,8 +28,8 @@ body:
|
||||
- type: input
|
||||
id: menu_version
|
||||
attributes:
|
||||
label: Menu version used
|
||||
description: The menu version used
|
||||
label: Menu revision used
|
||||
description: Press (Start) button on the menu to show the current menu version.
|
||||
placeholder: e.g 1.2.3.456, N/A
|
||||
validations:
|
||||
required: false
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
/.vscode
|
||||
/build
|
||||
/filesystem
|
||||
/output
|
||||
/tools/sc64/*
|
||||
|
||||
|
38
Makefile
38
Makefile
@ -4,6 +4,7 @@ PROJECT_NAME = N64FlashcartMenu
|
||||
|
||||
SOURCE_DIR = src
|
||||
ASSETS_DIR = assets
|
||||
FILESYSTEM_DIR = filesystem
|
||||
BUILD_DIR = build
|
||||
OUTPUT_DIR = output
|
||||
|
||||
@ -17,7 +18,6 @@ SRCS = \
|
||||
main.c \
|
||||
boot/boot.c \
|
||||
boot/cic.c \
|
||||
boot/crc32.c \
|
||||
boot/reboot.S \
|
||||
flashcart/64drive/64drive_ll.c \
|
||||
flashcart/64drive/64drive.c \
|
||||
@ -64,28 +64,31 @@ SRCS = \
|
||||
menu/views/music_player.c \
|
||||
menu/views/startup.c \
|
||||
menu/views/system_info.c \
|
||||
menu/views/settings_editor.c \
|
||||
utils/fs.c
|
||||
|
||||
ASSETS = \
|
||||
FONTS = \
|
||||
FiraMonoBold.ttf
|
||||
|
||||
OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o,$(basename $(SRCS) $(ASSETS))))
|
||||
OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o,$(basename $(SRCS))))
|
||||
MINIZ_OBJS = $(filter $(BUILD_DIR)/libs/miniz/%.o,$(OBJS))
|
||||
SPNG_OBJS = $(filter $(BUILD_DIR)/libs/libspng/%.o,$(OBJS))
|
||||
DEPS = $(OBJS:.o=.d)
|
||||
|
||||
FILESYSTEM = \
|
||||
$(addprefix $(FILESYSTEM_DIR)/, $(notdir $(FONTS:%.ttf=%.font64)))
|
||||
|
||||
$(MINIZ_OBJS): N64_CFLAGS+=-DMINIZ_NO_TIME -fcompare-debug-second
|
||||
$(SPNG_OBJS): N64_CFLAGS+=-isystem $(SOURCE_DIR)/libs/miniz -DSPNG_USE_MINIZ -fcompare-debug-second
|
||||
$(BUILD_DIR)/FiraMonoBold.asset: MKFONT_FLAGS+=-c 0 --size 16 -r 20-7F -r 2026-2026 --ellipsis 2026,1
|
||||
$(FILESYSTEM_DIR)/FiraMonoBold.font64: MKFONT_FLAGS+=-c 1 --size 16 -r 20-7F -r 2026-2026 --ellipsis 2026,1
|
||||
|
||||
$(BUILD_DIR)/%.asset: $(ASSETS_DIR)/%.ttf
|
||||
@echo " [FONT] $(basename $@).font64"
|
||||
@$(N64_MKFONT) $(MKFONT_FLAGS) -o $(BUILD_DIR) "$<"
|
||||
@mv $(basename $@).font64 $@
|
||||
$(@info $(shell mkdir -p ./$(FILESYSTEM_DIR) &> /dev/null))
|
||||
|
||||
$(BUILD_DIR)/%.o: $(BUILD_DIR)/%.asset $(ASSETS_DIR)/assets.S
|
||||
@sed -e "s,@sym@,$*,g" -e "s,@file@,$(basename $<).asset," < $(ASSETS_DIR)/assets.S | \
|
||||
$(CC) -x assembler-with-cpp $(ASFLAGS) -c - -o $@
|
||||
$(FILESYSTEM_DIR)/%.font64: $(ASSETS_DIR)/%.ttf
|
||||
@echo " [FONT] $@"
|
||||
@$(N64_MKFONT) $(MKFONT_FLAGS) -o $(FILESYSTEM_DIR) "$<"
|
||||
|
||||
$(BUILD_DIR)/$(PROJECT_NAME).dfs: $(FILESYSTEM)
|
||||
|
||||
$(BUILD_DIR)/$(PROJECT_NAME).elf: $(OBJS)
|
||||
|
||||
@ -94,29 +97,26 @@ disassembly: $(BUILD_DIR)/$(PROJECT_NAME).elf
|
||||
.PHONY: disassembly
|
||||
|
||||
$(PROJECT_NAME).z64: N64_ROM_TITLE=$(PROJECT_NAME)
|
||||
$(PROJECT_NAME).z64: $(BUILD_DIR)/$(PROJECT_NAME).dfs
|
||||
|
||||
$(@info $(shell mkdir -p ./$(OUTPUT_DIR) &> /dev/null))
|
||||
|
||||
$(OUTPUT_DIR)/$(PROJECT_NAME).n64: $(PROJECT_NAME).z64
|
||||
@mv $< $@
|
||||
|
||||
$(BUILD_DIR)/$(PROJECT_NAME)_stripped.n64: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
|
||||
python3 ./tools/strip_debug_data.py $(BUILD_DIR)/$(PROJECT_NAME).elf $< $@
|
||||
@$(N64_CHKSUM) $@ > /dev/null
|
||||
|
||||
64drive: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
|
||||
@cp $< $(OUTPUT_DIR)/menu.bin
|
||||
.PHONY: 64drive
|
||||
|
||||
ed64: $(BUILD_DIR)/$(PROJECT_NAME)_stripped.n64
|
||||
ed64: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
|
||||
@cp $< $(OUTPUT_DIR)/OS64.v64
|
||||
.PHONY: ed64
|
||||
|
||||
ed64-clone: $(BUILD_DIR)/$(PROJECT_NAME)_stripped.n64
|
||||
ed64-clone: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
|
||||
@cp $< $(OUTPUT_DIR)/OS64P.v64
|
||||
.PHONY: ed64-clone
|
||||
|
||||
sc64: $(BUILD_DIR)/$(PROJECT_NAME)_stripped.n64
|
||||
sc64: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
|
||||
@cp $< $(OUTPUT_DIR)/sc64menu.n64
|
||||
.PHONY: sc64
|
||||
|
||||
@ -124,7 +124,7 @@ all: $(OUTPUT_DIR)/$(PROJECT_NAME).n64 64drive ed64 ed64-clone sc64
|
||||
.PHONY: all
|
||||
|
||||
clean:
|
||||
@rm -rf ./$(BUILD_DIR) ./$(OUTPUT_DIR)
|
||||
@rm -rf ./$(BUILD_DIR) ./$(FILESYSTEM_DIR) ./$(OUTPUT_DIR)
|
||||
.PHONY: clean
|
||||
|
||||
run: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
|
||||
|
@ -51,6 +51,10 @@ Menu currently supports the following emulators and associated ROM file names:
|
||||
- **Game Boy** / **GB Color**: [gb64](https://lambertjamesd.github.io/gb64/romwrapper/romwrapper.html) by *lambertjamesd* - `gb.v64` / `gbc.v64`
|
||||
- **Sega Master System** / **Sega Game Gear** / **Sg1000**: [TotalSMS](https://github.com/ITotalJustice/TotalSMS) - `TotalSMS.z64` (Currently broken)
|
||||
|
||||
### Menu Settings
|
||||
The Menu creates a `config.ini` file in `sd:/menu/` which contains various settings that are used by the menu.
|
||||
Currently these are read-only (can be viewed in the menu by pressing `L` on the Joypad).
|
||||
If required, you can manually adjust the file on the SD card using your computer.
|
||||
|
||||
### SC64 Specific
|
||||
- Ensure the cart has the latest [firmware](https://github.com/Polprzewodnikowy/SummerCart64/releases/latest) installed.
|
||||
|
@ -1,11 +0,0 @@
|
||||
.section .data.@sym@, "a", %progbits
|
||||
.type assets_@sym@, %object
|
||||
|
||||
.balign 16
|
||||
|
||||
.global assets_@sym@
|
||||
assets_@sym@:
|
||||
.incbin "@file@"
|
||||
|
||||
.global assets_@sym@_size
|
||||
.set assets_@sym@_size, . - assets_@sym@
|
@ -1,16 +0,0 @@
|
||||
#ifndef ASSETS_H__
|
||||
#define ASSETS_H__
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#define ASSET(n) \
|
||||
extern uint8_t assets_##n[] __attribute__((section(".data"))); \
|
||||
extern int assets_##n##_size[] __attribute__((section(".data")))
|
||||
|
||||
|
||||
ASSET(FiraMonoBold);
|
||||
|
||||
|
||||
#endif
|
@ -1 +1 @@
|
||||
Subproject commit 4b38fd5618007e7ed9040107d0f7e52f2de81a22
|
||||
Subproject commit 6323128d72fdf32dfaa134f40191ba72e5527076
|
@ -37,15 +37,26 @@ static void boot_detect_cic_seed (boot_params_t *params) {
|
||||
|
||||
void boot (boot_params_t *params) {
|
||||
if (params->tv_type == BOOT_TV_TYPE_PASSTHROUGH) {
|
||||
params->tv_type = OS_INFO->tv_type;
|
||||
switch (get_tv_type()) {
|
||||
case TV_PAL:
|
||||
params->tv_type = BOOT_TV_TYPE_PAL;
|
||||
break;
|
||||
case TV_NTSC:
|
||||
params->tv_type = BOOT_TV_TYPE_NTSC;
|
||||
break;
|
||||
case TV_MPAL:
|
||||
params->tv_type = BOOT_TV_TYPE_MPAL;
|
||||
break;
|
||||
default:
|
||||
params->tv_type = BOOT_TV_TYPE_NTSC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (params->detect_cic_seed) {
|
||||
boot_detect_cic_seed(params);
|
||||
}
|
||||
|
||||
OS_INFO->mem_size_6105 = OS_INFO->mem_size;
|
||||
|
||||
C0_WRITE_STATUS(C0_STATUS_CU1 | C0_STATUS_CU0 | C0_STATUS_FR);
|
||||
|
||||
while (!(cpu_io_read(&SP->SR) & SP_SR_HALT));
|
||||
@ -120,7 +131,7 @@ void boot (boot_params_t *params) {
|
||||
|
||||
boot_device = (params->device_type & 0x01);
|
||||
tv_type = (params->tv_type & 0x03);
|
||||
reset_type = BOOT_RESET_TYPE_NMI;
|
||||
reset_type = BOOT_RESET_TYPE_COLD;
|
||||
cic_seed = (params->cic_seed & 0xFF);
|
||||
version = (params->tv_type == BOOT_TV_TYPE_PAL) ? 6
|
||||
: (params->tv_type == BOOT_TV_TYPE_NTSC) ? 1
|
||||
|
@ -41,7 +41,6 @@ typedef struct {
|
||||
} boot_params_t;
|
||||
|
||||
|
||||
bool boot_is_warm (void);
|
||||
void boot (boot_params_t *params);
|
||||
|
||||
|
||||
|
@ -241,28 +241,6 @@ typedef struct {
|
||||
#define ROM_CART_BASE (0x10000000UL)
|
||||
#define ROM_CART ((io32_t *) ROM_CART_BASE)
|
||||
|
||||
/** @brief OS Information Structure. */
|
||||
typedef struct {
|
||||
uint32_t tv_type;
|
||||
uint32_t device_type;
|
||||
uint32_t device_base;
|
||||
uint32_t reset_type;
|
||||
uint32_t cic_id;
|
||||
uint32_t version;
|
||||
uint32_t mem_size;
|
||||
uint8_t app_nmi_buffer[64];
|
||||
uint32_t __reserved_1[37];
|
||||
uint32_t mem_size_6105;
|
||||
} os_info_t;
|
||||
|
||||
#define OS_INFO_BASE (0x80000300UL)
|
||||
#define OS_INFO ((os_info_t *) OS_INFO_BASE)
|
||||
|
||||
/** @brief The Console was powered on using the power switch. */
|
||||
#define OS_INFO_RESET_TYPE_COLD (0)
|
||||
/** @brief The Console was reset using the reset button. */
|
||||
#define OS_INFO_RESET_TYPE_NMI (1)
|
||||
|
||||
|
||||
static inline uint32_t cpu_io_read (io32_t *address) {
|
||||
io32_t *uncached = UNCACHED(address);
|
||||
|
141
src/boot/cic.c
141
src/boot/cic.c
@ -1,36 +1,127 @@
|
||||
#include "cic.h"
|
||||
#include "crc32.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
const uint32_t crc32;
|
||||
const cic_type_t type;
|
||||
} ipl3_crc32_t;
|
||||
static inline uint32_t _get (uint8_t *p, int index) {
|
||||
int i = index * 4;
|
||||
return (p[i] << 24 | p[i + 1] << 16 | p[i + 2] << 8 | p[i + 3]);
|
||||
}
|
||||
|
||||
static const ipl3_crc32_t ipl3_crc32[] = {
|
||||
{ .crc32 = 0x587BD543, .type = CIC_5101 },
|
||||
{ .crc32 = 0x0E018159, .type = CIC_5167 },
|
||||
{ .crc32 = 0x6170A4A1, .type = CIC_6101 },
|
||||
{ .crc32 = 0x009E9EA3, .type = CIC_7102 },
|
||||
{ .crc32 = 0x90BB6CB5, .type = CIC_6102_7101 },
|
||||
{ .crc32 = 0x0B050EE0, .type = CIC_x103 },
|
||||
{ .crc32 = 0x98BC2C86, .type = CIC_x105 },
|
||||
{ .crc32 = 0xACC8580A, .type = CIC_x106 },
|
||||
{ .crc32 = 0xBC605D0A, .type = CIC_8301 },
|
||||
{ .crc32 = 0x502C4466, .type = CIC_8302 },
|
||||
{ .crc32 = 0x0C965795, .type = CIC_8303 },
|
||||
{ .crc32 = 0x10C68B18, .type = CIC_8401 },
|
||||
{ .crc32 = 0x8FEBA21E, .type = CIC_8501 },
|
||||
static inline uint32_t _add (uint32_t a1, uint32_t a2) {
|
||||
return a1 + a2;
|
||||
}
|
||||
|
||||
static inline uint32_t _sub (uint32_t a1, uint32_t a2) {
|
||||
return a1 - a2;
|
||||
}
|
||||
|
||||
static inline uint32_t _mul (uint32_t a1, uint32_t a2) {
|
||||
return a1 * a2;
|
||||
}
|
||||
|
||||
static inline uint32_t _rol (uint32_t a, uint32_t s) {
|
||||
return ((a) << (s)) | ((a) >> (-(s) & 31));
|
||||
}
|
||||
|
||||
static inline uint32_t _ror (uint32_t a, uint32_t s) {
|
||||
return ((a) >> (s)) | ((a) << (-(s) & 31));
|
||||
}
|
||||
|
||||
static uint32_t _sum (uint32_t a0, uint32_t a1, uint32_t a2) {
|
||||
uint64_t prod = ((uint64_t) (a0)) * (a1 == 0 ? a2 : a1);
|
||||
uint32_t hi = (prod >> 32) & 0xFFFFFFFF;
|
||||
uint32_t lo = prod & 0xFFFFFFFF;
|
||||
uint32_t diff = hi - lo;
|
||||
return (diff == 0) ? a0 : diff;
|
||||
};
|
||||
|
||||
static uint64_t cic_calculate_ipl3_checksum (uint8_t *ipl3, uint8_t seed) {
|
||||
const uint32_t MAGIC = 0x6C078965;
|
||||
|
||||
uint32_t data, prev, next;
|
||||
data = prev = next = _get(ipl3, 0);
|
||||
|
||||
uint32_t init = _add(_mul(MAGIC, seed), 1) ^ data;
|
||||
|
||||
uint32_t buf[16];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
buf[i] = init;
|
||||
}
|
||||
|
||||
for (int i = 1; i <= 1008; i++) {
|
||||
prev = data;
|
||||
data = next;
|
||||
|
||||
buf[0] = _add(buf[0], _sum(_sub(1007, i), data, i));
|
||||
buf[1] = _sum(buf[1], data, i);
|
||||
buf[2] = buf[2] ^ data;
|
||||
buf[3] = _add(buf[3], _sum(_add(data, 5), MAGIC, i));
|
||||
buf[4] = _add(buf[4], _ror(data, prev & 0x1F));
|
||||
buf[5] = _add(buf[5], _rol(data, prev >> 27));
|
||||
buf[6] = (data < buf[6]) ? (_add(buf[3], buf[6]) ^ _add(data, i)) : (_add(buf[4], data) ^ buf[6]);
|
||||
buf[7] = _sum(buf[7], _rol(data, prev & 0x1F), i);
|
||||
buf[8] = _sum(buf[8], _ror(data, prev >> 27), i);
|
||||
buf[9] = (prev < data) ? _sum(buf[9], data, i) : _add(buf[9], data);
|
||||
|
||||
if (i == 1008) {
|
||||
break;
|
||||
}
|
||||
|
||||
next = _get(ipl3, i);
|
||||
|
||||
buf[10] = _sum(_add(buf[10], data), next, i);
|
||||
buf[11] = _sum(buf[11] ^ data, next, i);
|
||||
buf[12] = _add(buf[12], buf[8] ^ data);
|
||||
buf[13] = _add(buf[13], _add(_ror(data, data & 0x1F), _ror(next, next & 0x1F)));
|
||||
buf[14] = _sum(_sum(buf[14], _ror(data, prev & 0x1F), i), _ror(next, data & 0x1F), i);
|
||||
buf[15] = _sum(_sum(buf[15], _rol(data, prev >> 27), i), _rol(next, data >> 27), i);
|
||||
}
|
||||
|
||||
uint32_t final_buf[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final_buf[i] = buf[0];
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
uint32_t data = buf[i];
|
||||
final_buf[0] = _add(final_buf[0], _ror(data, data & 0x1F));
|
||||
final_buf[1] = (data < final_buf[0]) ? _add(final_buf[1], data) : _sum(final_buf[1], data, i);
|
||||
final_buf[2] = (((data & 0x02) >> 1) == (data & 0x01)) ? _add(final_buf[2], data) : _sum(final_buf[2], data, i);
|
||||
final_buf[3] = ((data & 0x01) == 0x01) ? (final_buf[3] ^ data) : _sum(final_buf[3], data, i);
|
||||
}
|
||||
|
||||
uint32_t final_sum = _sum(final_buf[0], final_buf[1], 16);
|
||||
uint32_t final_xor = final_buf[3] ^ final_buf[2];
|
||||
|
||||
uint64_t checksum = (((((uint64_t) (final_sum)) & 0xFFFF)) << 32) | (final_xor);
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
|
||||
cic_type_t cic_detect (uint8_t *ipl3) {
|
||||
uint32_t crc32 = crc32_calculate(ipl3, IPL3_LENGTH);
|
||||
|
||||
for (int i = 0; i < sizeof(ipl3_crc32) / sizeof(ipl3_crc32_t); i++) {
|
||||
if (ipl3_crc32[i].crc32 == crc32) {
|
||||
return ipl3_crc32[i].type;
|
||||
}
|
||||
switch (cic_calculate_ipl3_checksum(ipl3, 0x3F)) {
|
||||
case 0x45CC73EE317AULL: return CIC_6101; // 6101
|
||||
case 0x44160EC5D9AFULL: return CIC_7102; // 7102
|
||||
case 0xA536C0F1D859ULL: return CIC_6102_7101; // 6102 / 7101
|
||||
}
|
||||
switch (cic_calculate_ipl3_checksum(ipl3, 0x78)) {
|
||||
case 0x586FD4709867ULL: return CIC_x103; // 6103 / 7103
|
||||
}
|
||||
switch (cic_calculate_ipl3_checksum(ipl3, 0x85)) {
|
||||
case 0x2BBAD4E6EB74ULL: return CIC_x106; // 6106 / 7106
|
||||
}
|
||||
switch (cic_calculate_ipl3_checksum(ipl3, 0x91)) {
|
||||
case 0x8618A45BC2D3ULL: return CIC_x105; // 6105 / 7105
|
||||
}
|
||||
switch (cic_calculate_ipl3_checksum(ipl3, 0xDD)) {
|
||||
case 0x6EE8D9E84970ULL: return CIC_8401; // NDXJ0
|
||||
case 0x6C216495C8B9ULL: return CIC_8301; // NDDJ0
|
||||
case 0xE27F43BA93ACULL: return CIC_8302; // NDDJ1
|
||||
case 0x32B294E2AB90ULL: return CIC_8303; // NDDJ2
|
||||
case 0x083C6C77E0B1ULL: return CIC_5167; // 64DD Cartridge conversion
|
||||
}
|
||||
switch (cic_calculate_ipl3_checksum(ipl3, 0xDE)) {
|
||||
case 0x05BA2EF0A5F1ULL: return CIC_8501; // NDDE0
|
||||
}
|
||||
|
||||
return CIC_UNKNOWN;
|
||||
|
@ -1,50 +0,0 @@
|
||||
#include "crc32.h"
|
||||
|
||||
|
||||
static const uint32_t crc32_table[256] = {
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
|
||||
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
|
||||
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
|
||||
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
|
||||
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
|
||||
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
|
||||
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
|
||||
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
|
||||
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
|
||||
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
|
||||
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
|
||||
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
|
||||
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
|
||||
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
|
||||
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
|
||||
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
|
||||
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
|
||||
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
|
||||
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
|
||||
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
|
||||
};
|
||||
|
||||
|
||||
uint32_t crc32_calculate (void *buffer, size_t length) {
|
||||
uint32_t crc32 = 0xFFFFFFFFUL;
|
||||
|
||||
uint8_t *byte_pointer = (uint8_t *) (buffer);
|
||||
|
||||
while (length--) {
|
||||
crc32 = (crc32 >> 8) ^ crc32_table[(crc32 & 0xFF) ^ (*byte_pointer++)];
|
||||
}
|
||||
|
||||
return ~(crc32);
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
/**
|
||||
* @file crc32.h
|
||||
* @brief Flashcart Boot Checksum
|
||||
* @ingroup boot
|
||||
*/
|
||||
|
||||
#ifndef CRC32_H__
|
||||
#define CRC32_H__
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
uint32_t crc32_calculate (void *buffer, size_t length);
|
||||
|
||||
|
||||
#endif
|
@ -2,10 +2,15 @@
|
||||
#define REBOOT_ADDRESS 0xA4001000
|
||||
#define STACK_ADDRESS 0xA4001FF0
|
||||
|
||||
#define RI_ADDRESS 0xA4700000
|
||||
|
||||
#define RI_SELECT 0x0C
|
||||
#define RI_REFRESH 0x10
|
||||
|
||||
|
||||
.set noat
|
||||
.section .text.reboot, "ax", %progbits
|
||||
.type reboot, %object
|
||||
|
||||
reboot_start:
|
||||
.global reboot_start
|
||||
|
||||
@ -29,6 +34,15 @@ reboot_entry:
|
||||
|
||||
li $sp, STACK_ADDRESS
|
||||
|
||||
reset_rdram:
|
||||
bnez $s5, reset_rdram_skip
|
||||
|
||||
li $t0, RI_ADDRESS
|
||||
|
||||
sw $zero, RI_REFRESH($t0)
|
||||
sw $zero, RI_SELECT($t0)
|
||||
reset_rdram_skip:
|
||||
|
||||
detect_console_region:
|
||||
li $t0, 1
|
||||
beq $s4, $zero, pal_console
|
||||
@ -37,16 +51,16 @@ detect_console_region:
|
||||
|
||||
pal_console:
|
||||
li $ra, 0xA4001554
|
||||
b reset_registers
|
||||
b prepare_registers
|
||||
|
||||
ntsc_console:
|
||||
li $ra, 0xA4001550
|
||||
b reset_registers
|
||||
b prepare_registers
|
||||
|
||||
mpal_console:
|
||||
li $ra, 0xA4001554
|
||||
|
||||
reset_registers:
|
||||
prepare_registers:
|
||||
move $at, $zero
|
||||
move $v0, $zero
|
||||
move $v1, $zero
|
||||
@ -56,7 +70,7 @@ reset_registers:
|
||||
move $a3, $zero
|
||||
move $t0, $zero
|
||||
move $t1, $zero
|
||||
move $t2, $zero
|
||||
li $t2, 0x40
|
||||
move $t4, $zero
|
||||
move $t5, $zero
|
||||
move $t6, $zero
|
||||
|
@ -22,6 +22,7 @@ static void actions_clear (menu_t *menu) {
|
||||
menu->actions.options = false;
|
||||
menu->actions.system_info = false;
|
||||
menu->actions.settings = false;
|
||||
menu->actions.credits = false;
|
||||
}
|
||||
|
||||
static void actions_update_direction (menu_t *menu) {
|
||||
@ -90,9 +91,11 @@ static void actions_update_buttons (menu_t *menu) {
|
||||
menu->actions.back = true;
|
||||
} else if (pressed.r) {
|
||||
menu->actions.options = true;
|
||||
} else if (pressed.l) {
|
||||
} else if (pressed.z) {
|
||||
menu->actions.system_info = true;
|
||||
} else if (pressed.start) {
|
||||
menu->actions.credits = true;
|
||||
} else if (pressed.l) {
|
||||
menu->actions.settings = true;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#include "assets.h"
|
||||
#include "fonts.h"
|
||||
|
||||
|
||||
static void load_default_font (void) {
|
||||
rdpq_font_t *default_font = rdpq_font_load_buf(assets_FiraMonoBold, (int) (assets_FiraMonoBold_size));
|
||||
rdpq_font_t *default_font = rdpq_font_load("rom:/FiraMonoBold.font64");
|
||||
|
||||
rdpq_font_style(default_font, STL_DEFAULT, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF) }));
|
||||
rdpq_font_style(default_font, STL_DIRECTORY, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0xFF, 0x70, 0xFF) }));
|
||||
|
@ -59,7 +59,9 @@ static void menu_init (boot_params_t *boot_params) {
|
||||
rtc_init();
|
||||
rspq_init();
|
||||
rdpq_init();
|
||||
dfs_init(DFS_DEFAULT_LOCATION);
|
||||
|
||||
fonts_init();
|
||||
sound_init_default();
|
||||
|
||||
menu = calloc(1, sizeof(menu_t));
|
||||
@ -102,8 +104,6 @@ static void menu_init (boot_params_t *boot_params) {
|
||||
display_init(RESOLUTION_640x480, DEPTH_16_BPP, 2, GAMMA_NONE, FILTERS_DISABLED);
|
||||
|
||||
register_VI_handler(frame_counter_handler);
|
||||
|
||||
fonts_init();
|
||||
}
|
||||
|
||||
static void menu_deinit (menu_t *menu) {
|
||||
@ -146,6 +146,7 @@ static struct views_s {
|
||||
{ view_image_viewer_init, view_image_viewer_display }, // MENU_MODE_IMAGE_VIEWER
|
||||
{ view_music_player_init, view_music_player_display }, // MENU_MODE_MUSIC_PLAYER
|
||||
{ view_credits_init, view_credits_display }, // MENU_MODE_CREDITS
|
||||
{ view_settings_init, view_settings_display }, // MENU_MODE_SETTINGS_EDITOR
|
||||
{ view_load_rom_init, view_load_rom_display }, // MENU_MODE_LOAD_ROM
|
||||
{ view_load_disk_init, view_load_disk_display }, // MENU_MODE_LOAD_DISK
|
||||
{ view_load_emulator_init, view_load_emulator_display }, // MENU_MODE_LOAD_EMULATOR
|
||||
@ -157,7 +158,7 @@ static struct views_s {
|
||||
void menu_run (boot_params_t *boot_params) {
|
||||
menu_init(boot_params);
|
||||
|
||||
while (exception_reset_time() < RESET_TIME_LENGTH) {
|
||||
while (true) {
|
||||
surface_t *display = (frame_counter >= FRAMERATE_DIVIDER) ? display_try_get() : NULL;
|
||||
|
||||
if (display != NULL) {
|
||||
|
@ -31,6 +31,7 @@ typedef enum {
|
||||
MENU_MODE_IMAGE_VIEWER,
|
||||
MENU_MODE_MUSIC_PLAYER,
|
||||
MENU_MODE_CREDITS,
|
||||
MENU_MODE_SETTINGS_EDITOR,
|
||||
MENU_MODE_LOAD_ROM,
|
||||
MENU_MODE_LOAD_DISK,
|
||||
MENU_MODE_LOAD_EMULATOR,
|
||||
@ -84,6 +85,7 @@ typedef struct {
|
||||
bool options;
|
||||
bool system_info;
|
||||
bool settings;
|
||||
bool credits;
|
||||
} actions;
|
||||
|
||||
struct {
|
||||
|
@ -13,11 +13,12 @@ static settings_t init = {
|
||||
.hidden_files_enabled = false,
|
||||
.default_directory = "/",
|
||||
.use_saves_folder = true,
|
||||
.autodetect_rom_region = true,
|
||||
|
||||
/* Beta feature flags */
|
||||
/* Beta feature flags (should always init to off) */
|
||||
.bgm_enabled = false,
|
||||
.sound_enabled = false,
|
||||
.rumble_enabled = true,
|
||||
.rumble_enabled = false,
|
||||
};
|
||||
|
||||
|
||||
@ -32,6 +33,7 @@ void settings_load (settings_t *settings) {
|
||||
settings->hidden_files_enabled = mini_get_bool(ini, "menu", "show_hidden_files", init.hidden_files_enabled);
|
||||
settings->default_directory = strdup(mini_get_string(ini, "menu", "default_directory", init.default_directory));
|
||||
settings->use_saves_folder = mini_get_bool(ini, "menu", "use_saves_folder", init.use_saves_folder);
|
||||
settings->autodetect_rom_region = mini_get_bool(ini, "menu", "autodetect_rom_region", init.autodetect_rom_region);
|
||||
|
||||
/* Beta feature flags, they might not be in the file */
|
||||
settings->bgm_enabled = mini_get_bool(ini, "menu_beta_flag", "bgm_enabled", init.bgm_enabled);
|
||||
@ -49,11 +51,12 @@ void settings_save (settings_t *settings) {
|
||||
mini_set_bool(ini, "menu", "show_hidden_files", settings->hidden_files_enabled);
|
||||
mini_set_string(ini, "menu", "default_directory", settings->default_directory);
|
||||
mini_set_bool(ini, "menu", "use_saves_folder", settings->use_saves_folder);
|
||||
mini_set_bool(ini, "menu", "autodetect_rom_region", settings->autodetect_rom_region);
|
||||
|
||||
/* Beta feature flags, they should not save until production ready! */
|
||||
// mini_set_bool(ini, "menu_beta_flag", "bgm_enabled", init.bgm_enabled);
|
||||
// mini_set_bool(ini, "menu_beta_flag", "sound_enabled", init.sound_enabled);
|
||||
// mini_set_bool(ini, "menu_beta_flag", "rumble_enabled", init.rumble_enabled);
|
||||
// mini_set_bool(ini, "menu_beta_flag", "bgm_enabled", settings->bgm_enabled);
|
||||
// mini_set_bool(ini, "menu_beta_flag", "sound_enabled", settings->sound_enabled);
|
||||
// mini_set_bool(ini, "menu_beta_flag", "rumble_enabled", settings->rumble_enabled);
|
||||
|
||||
mini_save(ini, MINI_FLAGS_SKIP_EMPTY_GROUPS);
|
||||
|
||||
|
@ -22,6 +22,9 @@ typedef struct {
|
||||
/** @brief Put saves into separate directory */
|
||||
bool use_saves_folder;
|
||||
|
||||
/** @brief Enable forcing the (N64 system region) tv type to align with game region when booting the ROM. */
|
||||
bool autodetect_rom_region;
|
||||
|
||||
/** @brief Enable Background music */
|
||||
bool bgm_enabled;
|
||||
|
||||
|
@ -286,8 +286,10 @@ static void process (menu_t *menu) {
|
||||
component_context_menu_show(&entry_context_menu);
|
||||
} else if (menu->actions.system_info) {
|
||||
menu->next_mode = MENU_MODE_SYSTEM_INFO;
|
||||
} else if (menu->actions.settings) {
|
||||
} else if (menu->actions.credits) {
|
||||
menu->next_mode = MENU_MODE_CREDITS;
|
||||
} else if (menu->actions.settings) {
|
||||
menu->next_mode = MENU_MODE_SETTINGS_EDITOR;
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,7 +334,7 @@ static void draw (menu_t *menu, surface_t *d) {
|
||||
if (menu->current_time >= 0) {
|
||||
component_actions_bar_text_draw(
|
||||
ALIGN_CENTER, VALIGN_TOP,
|
||||
"\n"
|
||||
"Z: System Info | Start: Credits\n"
|
||||
"%s",
|
||||
ctime(&menu->current_time)
|
||||
);
|
||||
|
@ -107,6 +107,40 @@ static const char *format_cic_type (cic_type_t cic_type) {
|
||||
}
|
||||
}
|
||||
|
||||
static boot_tv_type_t determine_tv_boot_type (destination_type_t rom_destination_code) {
|
||||
// check the market type from the ROM destination_code and return best guess!
|
||||
switch (rom_destination_code) {
|
||||
case MARKET_NORTH_AMERICA:
|
||||
case MARKET_JAPANESE:
|
||||
case MARKET_JAPANESE_MULTI:
|
||||
case MARKET_GATEWAY64_NTSC:
|
||||
return BOOT_TV_TYPE_NTSC;
|
||||
case MARKET_BRAZILIAN:
|
||||
return BOOT_TV_TYPE_MPAL;
|
||||
case MARKET_GERMAN:
|
||||
case MARKET_FRENCH:
|
||||
case MARKET_DUTCH:
|
||||
case MARKET_ITALIAN:
|
||||
case MARKET_SPANISH:
|
||||
case MARKET_AUSTRALIAN:
|
||||
case MARKET_SCANDINAVIAN:
|
||||
case MARKET_GATEWAY64_PAL:
|
||||
case MARKET_EUROPEAN_BASIC:
|
||||
// FIXME: There might be some interesting errors with OTHER_X and OTHER_Y (e.g. TGR Asia).
|
||||
// But they are mainly PAL regions.
|
||||
case MARKET_OTHER_X:
|
||||
case MARKET_OTHER_Y:
|
||||
return BOOT_TV_TYPE_PAL;
|
||||
// FIXME: We cannot be sure on these markets, so just return the default for the moment!
|
||||
case MARKET_CHINESE:
|
||||
case MARKET_CANADIAN:
|
||||
case MARKET_KOREAN:
|
||||
case MARKET_OTHER_Z:
|
||||
default:
|
||||
return BOOT_TV_TYPE_PASSTHROUGH;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void process (menu_t *menu) {
|
||||
if (menu->actions.enter) {
|
||||
@ -206,8 +240,14 @@ static void load (menu_t *menu) {
|
||||
|
||||
menu->next_mode = MENU_MODE_BOOT;
|
||||
menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM;
|
||||
menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH;
|
||||
menu->boot_params->detect_cic_seed = true;
|
||||
|
||||
if (menu->settings.autodetect_rom_region) {
|
||||
menu->boot_params->tv_type = determine_tv_boot_type(menu->load.rom_info.destination_code);
|
||||
}
|
||||
else {
|
||||
menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH;
|
||||
}
|
||||
}
|
||||
|
||||
static void deinit (void) {
|
||||
|
70
src/menu/views/settings_editor.c
Normal file
70
src/menu/views/settings_editor.c
Normal file
@ -0,0 +1,70 @@
|
||||
#include "views.h"
|
||||
|
||||
static char *convert_boolean (int state) {
|
||||
switch (state) {
|
||||
case 0: return "Off";
|
||||
case 1: return "On";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void process (menu_t *menu) {
|
||||
if (menu->actions.back) {
|
||||
menu->next_mode = MENU_MODE_BROWSER;
|
||||
}
|
||||
}
|
||||
|
||||
static void draw (menu_t *menu, surface_t *d) {
|
||||
rdpq_attach(d, NULL);
|
||||
|
||||
component_background_draw();
|
||||
|
||||
component_layout_draw();
|
||||
|
||||
component_main_text_draw(
|
||||
ALIGN_CENTER, VALIGN_TOP,
|
||||
"SETTINGS EDITOR\n"
|
||||
"\n"
|
||||
);
|
||||
|
||||
component_main_text_draw(
|
||||
ALIGN_LEFT, VALIGN_TOP,
|
||||
"\n"
|
||||
"\n"
|
||||
"pal60_enabled: %s\n"
|
||||
"hidden_files_enabled: %s\n"
|
||||
"default_directory: %s\n"
|
||||
"use_saves_folder: %s\n"
|
||||
"autodetect_rom_region: %s\n"
|
||||
"bgm_enabled: %s\n"
|
||||
"sound_enabled: %s\n"
|
||||
"rumble_enabled: %s\n",
|
||||
convert_boolean(menu->settings.pal60_enabled),
|
||||
convert_boolean(menu->settings.hidden_files_enabled),
|
||||
menu->settings.default_directory,
|
||||
convert_boolean(menu->settings.use_saves_folder),
|
||||
convert_boolean(menu->settings.autodetect_rom_region),
|
||||
convert_boolean(menu->settings.bgm_enabled),
|
||||
convert_boolean(menu->settings.sound_enabled),
|
||||
convert_boolean(menu->settings.rumble_enabled)
|
||||
);
|
||||
|
||||
|
||||
component_actions_bar_text_draw(
|
||||
ALIGN_LEFT, VALIGN_TOP,
|
||||
"\n"
|
||||
"B: Back"
|
||||
);
|
||||
|
||||
rdpq_detach_show();
|
||||
}
|
||||
|
||||
|
||||
void view_settings_init (menu_t *menu) {
|
||||
// Nothing to initialize (yet)
|
||||
}
|
||||
|
||||
void view_settings_display (menu_t *menu, surface_t *display) {
|
||||
process(menu);
|
||||
draw(menu, display);
|
||||
}
|
@ -44,6 +44,9 @@ void view_load_rom_display (menu_t *menu, surface_t *display);
|
||||
void view_load_disk_init (menu_t *menu);
|
||||
void view_load_disk_display (menu_t *menu, surface_t *display);
|
||||
|
||||
void view_settings_init (menu_t *menu);
|
||||
void view_settings_display (menu_t *menu, surface_t *display);
|
||||
|
||||
void view_load_emulator_init (menu_t *menu);
|
||||
void view_load_emulator_display (menu_t *menu, surface_t *display);
|
||||
|
||||
|
@ -1,9 +0,0 @@
|
||||
# Source
|
||||
This Directory.
|
||||
|
||||
# License
|
||||
See root directory.
|
||||
|
||||
# Description
|
||||
`strip_debug_data.py`
|
||||
Removes unnecessary debug data from the end of ROM file to cut on menu load time from SD card.
|
@ -1,58 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
|
||||
def get_symbol_address(elf, symbol):
|
||||
p1 = Popen(f'readelf -s --wide {elf}'.split(), stdout=PIPE)
|
||||
p2 = Popen(f'grep -m 1 {symbol}'.split(), stdin=p1.stdout, stdout=PIPE)
|
||||
stdout, _ = p2.communicate()
|
||||
|
||||
symbol_data = stdout.decode('UTF-8').split()
|
||||
address = symbol_data[1]
|
||||
name = symbol_data[7]
|
||||
|
||||
if (symbol != name):
|
||||
raise Exception(f'Inexact symbol name found [{symbol} != {name}]')
|
||||
|
||||
return int(address, 16)
|
||||
|
||||
|
||||
def get_rom_data_end_offset(elf):
|
||||
ROM_ENTRY_OFFSET = 0x1000
|
||||
|
||||
libdragon_text_start = get_symbol_address(elf, '__libdragon_text_start')
|
||||
rom_end = get_symbol_address(elf, '__rom_end')
|
||||
|
||||
return ROM_ENTRY_OFFSET + (rom_end - libdragon_text_start)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if (len(sys.argv) != 4):
|
||||
print(f'Usage: python {sys.argv[0]} elf input output')
|
||||
sys.exit(1)
|
||||
|
||||
elf_file = sys.argv[1]
|
||||
input_file = sys.argv[2]
|
||||
output_file = sys.argv[3]
|
||||
|
||||
ALIGN = 512
|
||||
|
||||
try:
|
||||
length = get_rom_data_end_offset(elf_file)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
sys.exit(2)
|
||||
|
||||
stripped_data = b''
|
||||
with open(input_file, 'rb') as f:
|
||||
stripped_data = f.read(length)
|
||||
|
||||
modulo = (length % ALIGN)
|
||||
if (modulo > 0):
|
||||
stripped_data += b'\x00' * (ALIGN - modulo)
|
||||
|
||||
with open(output_file, 'wb') as f:
|
||||
f.write(stripped_data)
|
Loading…
Reference in New Issue
Block a user