-
Notifications
You must be signed in to change notification settings - Fork 34
1) How UNFLoader works
UNFLoader starts by detecting the connected flashcart and then assigns function pointers to functions related to said cart. These function pointers provide a way to abstract which flashcart the tool is connected to, and makes it easier for developers to extend the tool's functionality without needing to know how each cart works.
UNFLoader provides the following functions (located in device.h) to abstract different processes of flashcart USB communication:
// Finds what cart is connected via USB and initializes the function pointers
void device_find(int automode);
// Opens the USB pipeline
void device_open();
// Sends a ROM via USB given a filepath
void device_sendrom(char* rompath);
// Checks whether this flashcart can use debug mode
bool device_testdebug();
// Uploads arbitrary data to the connected flashcart (More on this later)
void device_senddata(int datatype, char* data, u32 size);
// Checks if the USB pipeline is open
bool device_isopen();
// Closes the USB pipeline
void device_close();If the -d argument is provided to UNFLoader during intialization, once a ROM is uploaded to a flashcart, UNFLoader enters debug mode. In this state, the tool enters an loop where it periodically calls the FT_GetQueueStatus function to detect incoming data. This is handled by the debug_main function in debug.cpp. In order to access debug mode, device_testdebug must return true. Whether it does will depend on the flashcart or potentially its firmware.
How does the tool know what to do with the incoming data? How can it differentiate between prints and binary data? How does it know if the incoming data is part of a larger block? The answer is that UNFLoader uses a specific communication protocol to deal with these problems.
When uploading or receiving arbitrary data (Denoted hereafter as "data block"), UNFLoader expects it to be formatted like so:
- 4 Bytes with
'D' 'M' 'A' '@'to signalize data start. - 4 Bytes with the data header (more on this in a bit).
- N bytes with the data, where N is the size value provided in the data header.
- Finally, 4 bytes with
'C' 'M' 'P' 'H'to signalize data end [1].
device_senddata function in UNFLoader, it will automatically format the data to fit the communication protocol, the developer needn't worry about doing the formatting himself. Same applies for when calling usb_write or usb_read on the N64 side. The library will automatically format the data with this, and abstract it away from the developer. More on this (regarding the N64 library) in the next chapter.
The data header provides two important chunks of information. The first byte of the data header contains the type of data which is being sent/received, and the next 3 bytes state how large the incoming data is (Which can range between 1 byte to a maximum of 8MB). The data type mentioned in the first byte of the data header is up to the developer to implement, with the following data types already provided by default:
// Incoming data is text for printf
#define DATATYPE_TEXT 0x01
// Incoming data is raw binary
#define DATATYPE_RAWBINARY 0x02
// Incoming data describes contents of next incoming data
#define DATATYPE_HEADER 0x03
// Incoming data is a framebuffer
#define DATATYPE_SCREENSHOT 0x04The DATATYPE_HEADER type exists to allow the N64 to describe the data it's about to send in the next USB data block in more detail. For instance, a framebuffer can be of an arbitrary size (such as 320x240 for NTSC and 320×288 for PAL) and bit depth (16 bits or 32 bits). Because of this, before sending a data block with DATATYPE_SCREENSHOT, a DATATYPE_HEADER data block is sent first, containing 16 bytes of data (4 with DATATYPE_SCREENSHOT so that we know the next incoming block is a framebuffer, 4 for the width in pixels, 4 for the height in pixels, and another 4 with the bit depth). You can use this header data type to assist you when sending complex data through USB.
The user can type text into the command prompt in debug mode. When ENTER is pressed, this data is sent to the N64 using DATATYPE_TEXT. This is called a "command".
When sending commands, the user can choose to wrap a filepath with '@' characters. This instead will cause UNFLoader to open the file wrapped in the delimiter characters and append it to the data it'll send. If the sent data only contains a filepath with '@' characters, then UNFLoader will simply send the contents of the file with DATATYPE_RAWBINARY. However, the user can choose to append data with some text before or after it, for instance: texture @file.bin@ 32. In this case, UNFLoader will use DATATYPE_TEXT but replace file.bin with the size of the file in bytes, and the contents of the file will be appended after the second '@'. So in that example, if file.bin contained abc, then the data portion of the sent data block will be texture @3@abc 32.
There is no checksum in place to detect the authenticity of the data. This might be implemented at a later date, but for now it is unused to speed up USB operations. A very simple method for a checksum would be to add every single byte of the received data together, and might be adopted in the future...
[1] The CMP signal is currently not used when uploading arbitrary data to the 64Drive. The reason for this is explained in the 64Drive chapter.