mirror of
https://github.com/Brawl345/N64Swap.git
synced 2024-11-23 08:39:15 +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