mirror of
https://github.com/Brawl345/N64Swap.git
synced 2024-11-27 02:14:20 +01:00
Initial commit
This commit is contained in:
commit
99e0615212
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files / pip / IDE
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
src/
|
||||||
|
.idea/
|
||||||
|
*.n64
|
||||||
|
*.v64
|
||||||
|
*.z64
|
24
LICENSE
Normal file
24
LICENSE
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <https://unlicense.org>
|
19
README.md
Normal file
19
README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
N64Swap
|
||||||
|
=======
|
||||||
|
Swaps .n64, .v64 and .z64 roms. Written in Python 3. You must install `numpy` via e.g. pip. Code is kinda messy.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
```
|
||||||
|
n64swap.py [-h] input output
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical information
|
||||||
|
`.z64` is the correct format and should be used. This handy table shows the [difference](https://old.reddit.com/r/emulation/comments/7hrvzp/the_three_different_n64_rom_formats_explained_for/):
|
||||||
|
|
||||||
|
| ROM format | Type | First 4 bytes | Game Title in ROM |
|
||||||
|
|------------|---------------|---------------|--------------------|
|
||||||
|
| .z64 | Big Endian | 80 37 12 40 | `SUPER MARIO 64 ` |
|
||||||
|
| .v64 | Byteswapped | 37 80 40 12 | `USEP RAMIR O64 ` |
|
||||||
|
| .n64 | Little Endian | 40 12 37 80 | `EPUSAM R OIR 46` |
|
||||||
|
|
||||||
|
(Note that there are two spaces after both "64", they don't seem to show up here.)
|
95
n64swap.py
Normal file
95
n64swap.py
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
|
||||||
|
valid_ext = [".n64", ".v64", ".z64"]
|
||||||
|
parser = ArgumentParser()
|
||||||
|
parser.add_argument('input', type=str, help="Input File")
|
||||||
|
parser.add_argument('output', type=str, help="Output File")
|
||||||
|
arguments = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def read_in_chunks(file_object, chunk_size=1024):
|
||||||
|
"""Lazy function (generator) to read a file piece by piece.
|
||||||
|
Default chunk size: 1k."""
|
||||||
|
while True:
|
||||||
|
data = file_object.read(chunk_size)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
yield data
|
||||||
|
|
||||||
|
|
||||||
|
def get_rom_format(rom_header):
|
||||||
|
if rom_header == b"\x40\x12\x37\x80":
|
||||||
|
return ".n64"
|
||||||
|
elif rom_header == b"\x80\x37\x12\x40":
|
||||||
|
return ".z64"
|
||||||
|
elif rom_header == b"\x37\x80\x40\x12":
|
||||||
|
return ".v64"
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def swap_bytes(b):
|
||||||
|
chunk_bytearray = bytearray(b)
|
||||||
|
byteswapped = bytearray(len(b))
|
||||||
|
byteswapped[0::2] = chunk_bytearray[1::2]
|
||||||
|
byteswapped[1::2] = chunk_bytearray[0::2]
|
||||||
|
return byteswapped
|
||||||
|
|
||||||
|
|
||||||
|
def main(infile, outfile):
|
||||||
|
# TODO: check if input == output
|
||||||
|
|
||||||
|
output_ext = os.path.splitext(outfile)[1]
|
||||||
|
if output_ext.lower() not in valid_ext:
|
||||||
|
print("Output file must end with .n64, .v64 or .z64")
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(infile, "rb") as rom_file:
|
||||||
|
rom_header = rom_file.read(4)
|
||||||
|
rom_format = get_rom_format(rom_header)
|
||||||
|
if not rom_format:
|
||||||
|
print("Not an N64 rom.")
|
||||||
|
return
|
||||||
|
rom_file.seek(0)
|
||||||
|
|
||||||
|
print("{0} -> {1}".format(rom_format, output_ext))
|
||||||
|
with open(outfile, "wb") as out_file:
|
||||||
|
if rom_format == output_ext: # Just copy it
|
||||||
|
for chunk in read_in_chunks(rom_file):
|
||||||
|
out_file.write(chunk)
|
||||||
|
|
||||||
|
if rom_format == ".z64": # Big Endian
|
||||||
|
if output_ext == ".n64": # Big Endian -> Little Endian
|
||||||
|
for chunk in read_in_chunks(rom_file):
|
||||||
|
out_file.write(numpy.frombuffer(chunk, numpy.float32).byteswap())
|
||||||
|
elif output_ext == ".v64": # Big Endian -> Byteswapped
|
||||||
|
for chunk in read_in_chunks(rom_file):
|
||||||
|
out_file.write(swap_bytes(chunk))
|
||||||
|
|
||||||
|
elif rom_format == ".n64": # Little Endian
|
||||||
|
if output_ext == ".z64": # Little Endian -> Big Endian
|
||||||
|
for chunk in read_in_chunks(rom_file):
|
||||||
|
out_file.write(numpy.frombuffer(chunk, numpy.float32).byteswap())
|
||||||
|
elif output_ext == ".v64": # Little Endian -> Big Endian -> Byteswapped
|
||||||
|
for chunk in read_in_chunks(rom_file):
|
||||||
|
endian_swapped = numpy.frombuffer(chunk, numpy.float32).byteswap()
|
||||||
|
out_file.write(swap_bytes(endian_swapped.tobytes()))
|
||||||
|
|
||||||
|
elif rom_format == ".v64": # Byteswapped
|
||||||
|
if output_ext == ".n64": # Byteswapped -> Big Endian -> Little Endian
|
||||||
|
for chunk in read_in_chunks(rom_file):
|
||||||
|
endian_swapped = numpy.frombuffer(chunk, numpy.float32).byteswap()
|
||||||
|
out_file.write(swap_bytes(endian_swapped.tobytes()))
|
||||||
|
elif output_ext == ".z64": # Byteswapped -> Big Endian
|
||||||
|
for chunk in read_in_chunks(rom_file):
|
||||||
|
out_file.write(swap_bytes(chunk))
|
||||||
|
|
||||||
|
print("Conversion finished.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(infile=arguments.input, outfile=arguments.output)
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
numpy
|
Loading…
Reference in New Issue
Block a user