mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-24 14:46:53 +01:00
disk stuff cleanup
This commit is contained in:
parent
8479c80ebe
commit
1300f8ac33
@ -1,7 +1,6 @@
|
|||||||
- [First time setup](#first-time-setup)
|
- [First time setup](#first-time-setup)
|
||||||
- [Firmware backup/update](#firmware-backupupdate)
|
- [Firmware backup/update](#firmware-backupupdate)
|
||||||
- [Internal flashcart state](#internal-flashcart-state)
|
- [Uploading game and/or save](#uploading-game-andor-save)
|
||||||
- [Uploading game/save](#uploading-gamesave)
|
|
||||||
- [Downloading save](#downloading-save)
|
- [Downloading save](#downloading-save)
|
||||||
- [Running 64DD games](#running-64dd-games)
|
- [Running 64DD games](#running-64dd-games)
|
||||||
- [Direct boot option](#direct-boot-option)
|
- [Direct boot option](#direct-boot-option)
|
||||||
@ -12,81 +11,96 @@
|
|||||||
|
|
||||||
## First time setup
|
## First time setup
|
||||||
|
|
||||||
**Windows platform: replace `./sc64` in examples below with `sc64.exe`**
|
**Windows platform: replace `./sc64deployer` in examples below with `sc64deployer.exe`**
|
||||||
|
|
||||||
1. Download the latest `sc64-{os}-{version}.{ext}` (choose OS matching your system) and `sc64-firmware-{version}.bin` from GitHub releases page
|
1. Download the latest deployer tool (`sc64-deployer-{os}-{version}.{ext}`) and firmware (`sc64-firmware-{version}.bin`) from GitHub releases page
|
||||||
2. Extract `sc64-{os}-{version}.{ext}` package contents to a folder and place `sc64-firmware-{version}.bin` inside it
|
2. Extract deployer tool package contents to a folder and place firmware file inside it
|
||||||
3. Update SC64 firmware to the latest version with `./sc64 --update-firmware sc64-firmware-{version}.bin`
|
3. Connect SC64 device to your computer with USB type C cable
|
||||||
4. Run `./sc64 --print-state` to check if update process finished successfully and SC64 is detected correctly
|
4. Run `./sc64deployer list` to check if device is detected in the system
|
||||||
|
5. Update SC64 firmware to the latest version with `./sc64deployer firmware update sc64-firmware-{version}.bin`
|
||||||
|
6. Run `./sc64deployer info` to check if update process finished successfully and SC64 is detected correctly
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Firmware backup/update
|
## Firmware backup/update
|
||||||
|
|
||||||
Keeping SC64 firmware up to date is highly recommended. `sc64` executable is tightly coupled with specific firmware versions and will error out when it detects unsupported firmware version.
|
Keeping SC64 firmware up to date is highly recommended.
|
||||||
|
`sc64deployer` application is tightly coupled with specific firmware versions and will error out when it detects unsupported firmware version.
|
||||||
|
|
||||||
To download and backup current version of SC64 firmware run `./sc64 --backup-firmware sc64-firmware-backup.bin`
|
To download and backup current version of the SC64 firmware run `./sc64deployer firmware backup sc64-firmware-backup.bin`
|
||||||
|
|
||||||
To update SC64 firmware run `./sc64 --update-firmware sc64-firmware-{version}.bin`
|
To update SC64 firmware run `./sc64deployer firmware update sc64-firmware-{version}.bin`
|
||||||
|
|
||||||
|
To print firmware metadata run `./sc64deployer firmware info sc64-firmware-{version}.bin`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Internal flashcart state
|
## Uploading game and/or save
|
||||||
|
|
||||||
SC64 holds some internal configuration options after `sc64` executable finished running. To reset it simply run: `./sc64 --reset-state`. Internal flashcart state can be checked by running: `./sc64 --print-state`
|
`./sc64deployer upload path_to_rom.n64 --save-type eeprom4k --save path_to_save.sav`
|
||||||
|
|
||||||
---
|
Replace `path_to_rom.n64` / `eeprom4k` / `path_to_save.sav` with appropriate values for desired game.
|
||||||
|
Application will try to autodetect used save type so explicitly setting save type usually isn't needed.
|
||||||
## Uploading game/save
|
Check included help in the application to list available save types.
|
||||||
|
Arguments `--save-type` and/or `--save` can be omitted if game doesn't require any save or you want to start with fresh save file.
|
||||||
`./sc64 --boot rom path_to_rom.n64 --save-type eeprom-4k --save path_to_save.sav`
|
|
||||||
|
|
||||||
Replace `path_to_rom.n64` / `eeprom-4k` / `path_to_save.sav` with appropriate values for desired game. Script will try to autodetect used save type so explicitly setting save type usually isn't needed. Check included help in program to list available save types.
|
|
||||||
Arguments `--save-type` and/or `--save` can be omitted if game doesn't require any save or you want to start fresh.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Downloading save
|
## Downloading save
|
||||||
|
|
||||||
`./sc64 --backup-save path_to_save.sav`
|
`./sc64deployer download save path_to_save.sav`
|
||||||
|
|
||||||
Replace `path_to_save.sav` with appropriate value. Specifying save type isn't required when set correctly previously.
|
Replace `path_to_save.sav` with appropriate value.
|
||||||
|
Command will raise error when no save type is currently enabled in the SC64 device.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Running 64DD games
|
## Running 64DD games
|
||||||
|
|
||||||
64DD games require DDIPL ROM and disk images. To run disk game type `./sc64 --boot ddipl --ddipl path_to_ddipl.n64 --disk path_to_disk_1.ndd --disk path_to_disk_2.ndd`.
|
64DD games require DDIPL ROM and disk images.
|
||||||
|
To run disk game type `./sc64deployer 64dd path_to_ddipl.n64 path_to_disk_1.ndd path_to_disk_2.ndd`.
|
||||||
|
|
||||||
Replace `path_to_ddipl.n64` / `path_to_disk_x.ndd` with appropriate values. Argument `--disk` can be specified multiple times. Only `.ndd` disk format is supported currently. To change inserted disk press button on the back of SC64 flashcart. Make sure retail and development disks aren't mixed together. 64DD IPL can handle only one drive type at a time.
|
Replace `path_to_ddipl.n64` / `path_to_disk_x.ndd` with appropriate values.
|
||||||
|
Multiple disk files can be passed to the command.
|
||||||
|
Only `.ndd` disk format is supported.
|
||||||
|
To change inserted disk press button on the back of SC64 device.
|
||||||
|
Make sure retail and development disks formats aren't mixed together.
|
||||||
|
64DD IPL can handle only one drive type at a time.
|
||||||
|
|
||||||
|
If disk game supports running in conjunction with cartridge game then `--rom path_to_rom.n64` argument can be added to command above.
|
||||||
|
N64 will boot cartridge game instead of 64DD IPL.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Direct boot option
|
## Direct boot option
|
||||||
|
|
||||||
If booting game through included bootloader isn't a desired option then flashcart can be put in special mode that omits this step.
|
If booting game through included bootloader isn't a desired option then flashcart can be put in special mode that omits this step.
|
||||||
Run `./sc64 --boot direct-rom path_to_rom.n64` to disable bootloader during boot and console reset. This option is useful only for very specific cases (e.g. testing custom IPL3 or running SC64 on top of GameShark).
|
Pass `--direct` option in `upload` or `64dd` command to disable bootloader during boot and console reset.
|
||||||
|
This option is useful only for very specific cases (e.g. testing custom IPL3 or running SC64 on top of GameShark).
|
||||||
|
TV type cannot be forced when direct boot mode is enabled.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Debug terminal
|
## Debug terminal
|
||||||
|
|
||||||
`sc64` executable supports UNFLoader protocol and has same functionality implemented as aforementioned program. Use argument `--debug` to activate it.
|
`sc64deployer` application supports UNFLoader protocol and has same functionality implemented as aforementioned program.
|
||||||
|
Type `./sc64deployer debug` to activate it.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## LED blink patters
|
## LED blink patters
|
||||||
|
|
||||||
LED on SC64 board can blink in certain situations. Most of them during normal use are related to SD card access. Here's list of blink patters meaning:
|
LED on SC64 board can blink in certain situations. Most of them during normal use are related to SD card access. Here's list of blink patterns:
|
||||||
|
|
||||||
| Pattern | Meaning |
|
| Pattern | Meaning |
|
||||||
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
|
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
|
||||||
| Nx [Short ON - Short OFF] | SD card is being accessed (initialization or data read/write) or save writeback is in progress |
|
| Nx [Short ON - Short OFF] | SD card access is in progress (initialization or data read/write) or save writeback is in progress |
|
||||||
| Nx [Medium ON - Long OFF] | CIC region didn't match, please power off console and power on again |
|
| Nx [Medium ON - Long OFF] | CIC region did not match, please power off console and power on again |
|
||||||
| 2x [Very short ON - Short OFF] | Pattern used during firmware update process, it means that specific part of firmware has started programming |
|
| 2x [Very short ON - Short OFF] | Pattern used during firmware update process, it means that specific part of firmware has started programming |
|
||||||
| 10x [Very short ON - Very short OFF] | Firmware has been successfully updated |
|
| 10x [Very short ON - Very short OFF] | Firmware has been successfully updated |
|
||||||
| 30x [Long ON - Long OFF] | There was serious problem during firmware update, device is most likely bricked |
|
| 30x [Long ON - Long OFF] | There was serious problem during firmware update, device is most likely bricked |
|
||||||
|
|
||||||
Nx means that blink count is varied.
|
Nx means that blink count is varied.
|
||||||
|
|
||||||
LED blinking on SD card access can be disabled through `sc64` executable. Please refer to included help for option to change the LED behavior.
|
LED blinking on SD card access can be disabled through `sc64deployer` application.
|
||||||
|
Please refer to included help for option to change the LED behavior.
|
||||||
|
@ -58,7 +58,7 @@ macro_rules! zone {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const DISK_ZONES: [DiskZone; 16] = [
|
const ZONE_MAPPING: [DiskZone; 16] = [
|
||||||
zone!(0, 232, 158, 0),
|
zone!(0, 232, 158, 0),
|
||||||
zone!(0, 216, 158, 158),
|
zone!(0, 216, 158, 158),
|
||||||
zone!(0, 208, 149, 316),
|
zone!(0, 208, 149, 316),
|
||||||
@ -67,17 +67,17 @@ const DISK_ZONES: [DiskZone; 16] = [
|
|||||||
zone!(0, 160, 149, 763),
|
zone!(0, 160, 149, 763),
|
||||||
zone!(0, 144, 149, 912),
|
zone!(0, 144, 149, 912),
|
||||||
zone!(0, 128, 114, 1061),
|
zone!(0, 128, 114, 1061),
|
||||||
zone!(1, 216, 158, 157),
|
zone!(1, 216, 158, 0),
|
||||||
zone!(1, 208, 158, 315),
|
zone!(1, 208, 158, 158),
|
||||||
zone!(1, 192, 149, 464),
|
zone!(1, 192, 149, 316),
|
||||||
zone!(1, 176, 149, 613),
|
zone!(1, 176, 149, 465),
|
||||||
zone!(1, 160, 149, 762),
|
zone!(1, 160, 149, 614),
|
||||||
zone!(1, 144, 149, 911),
|
zone!(1, 144, 149, 763),
|
||||||
zone!(1, 128, 149, 1060),
|
zone!(1, 128, 149, 912),
|
||||||
zone!(1, 112, 114, 1174),
|
zone!(1, 112, 114, 1061),
|
||||||
];
|
];
|
||||||
|
|
||||||
const DISK_VZONE_TO_PZONE: [[usize; 16]; 7] = [
|
const VZONE_TO_PZONE: [[usize; 16]; 7] = [
|
||||||
[0, 1, 2, 9, 8, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10],
|
[0, 1, 2, 9, 8, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10],
|
||||||
[0, 1, 2, 3, 10, 9, 8, 4, 5, 6, 7, 15, 14, 13, 12, 11],
|
[0, 1, 2, 3, 10, 9, 8, 4, 5, 6, 7, 15, 14, 13, 12, 11],
|
||||||
[0, 1, 2, 3, 4, 11, 10, 9, 8, 5, 6, 7, 15, 14, 13, 12],
|
[0, 1, 2, 3, 4, 11, 10, 9, 8, 5, 6, 7, 15, 14, 13, 12],
|
||||||
@ -87,6 +87,8 @@ const DISK_VZONE_TO_PZONE: [[usize; 16]; 7] = [
|
|||||||
[0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8],
|
[0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const ROM_ZONES: [usize; 7] = [5, 7, 9, 11, 13, 15, 16];
|
||||||
|
|
||||||
struct Mapping {
|
struct Mapping {
|
||||||
lba: usize,
|
lba: usize,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
@ -162,51 +164,47 @@ pub fn open(path: &str) -> Result<Disk, Error> {
|
|||||||
|
|
||||||
pub fn open_multiple(paths: &[String]) -> Result<Vec<Disk>, Error> {
|
pub fn open_multiple(paths: &[String]) -> Result<Vec<Disk>, Error> {
|
||||||
let mut disks: Vec<Disk> = Vec::new();
|
let mut disks: Vec<Disk> = Vec::new();
|
||||||
|
|
||||||
for path in paths {
|
for path in paths {
|
||||||
let disk = open(path)?;
|
let disk = open(path)?;
|
||||||
disks.push(disk);
|
disks.push(disk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !disks.windows(2).all(|d| d[0].format == d[1].format) {
|
if !disks.windows(2).all(|d| d[0].format == d[1].format) {
|
||||||
return Err(Error::new("Disk format mismatch"));
|
return Err(Error::new("Disk format mismatch"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(disks)
|
Ok(disks)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_ndd(file: &mut File) -> Result<(Format, HashMap<usize, Mapping>), Error> {
|
fn load_ndd(file: &mut File) -> Result<(Format, HashMap<usize, Mapping>), Error> {
|
||||||
let mut disk_format: Option<Format> = None;
|
let mut disk_format: Option<Format> = None;
|
||||||
let mut disk_type: u8 = 0;
|
let mut disk_type: usize = 0;
|
||||||
let mut sys_data = vec![0u8; SYSTEM_SECTOR_LENGTH];
|
let mut sys_data = vec![0u8; SYSTEM_SECTOR_LENGTH];
|
||||||
let mut bad_lba: Vec<usize> = Vec::new();
|
let mut bad_lbas: Vec<usize> = Vec::new();
|
||||||
|
|
||||||
for info in SYSTEM_AREA {
|
for info in SYSTEM_AREA {
|
||||||
bad_lba.clear();
|
bad_lbas.clear();
|
||||||
for &lba in info.sys_lba {
|
for &lba in info.sys_lba {
|
||||||
let data = load_sys_lba(file, lba)?;
|
let data = load_sys_lba(file, lba)?;
|
||||||
if verify_sys_lba(&data, info.sector_length) {
|
if verify_sys_lba(&data, info.sector_length) {
|
||||||
if (data[4] != 0x10) || ((data[5] & 0xF0) != 0x10) {
|
if (data[4] != 0x10) || ((data[5] & 0xF0) != 0x10) {
|
||||||
bad_lba.push(lba);
|
bad_lbas.push(lba);
|
||||||
} else {
|
} else {
|
||||||
disk_format = Some(info.format);
|
disk_format = Some(info.format);
|
||||||
disk_type = data[5] & 0x0F;
|
disk_type = (data[5] & 0x0F) as usize;
|
||||||
sys_data = data[0..SYSTEM_SECTOR_LENGTH].to_vec();
|
sys_data = data[0..SYSTEM_SECTOR_LENGTH].to_vec();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bad_lba.push(lba);
|
bad_lbas.push(lba);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if disk_format.is_some() {
|
if disk_format.is_some() {
|
||||||
bad_lba.append(&mut info.bad_lba.to_vec());
|
bad_lbas.append(&mut info.bad_lba.to_vec());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if disk_format.is_none() {
|
if disk_format.is_none() {
|
||||||
return Err(Error::new("Provided 64DD disk file is not valid"));
|
return Err(Error::new("Provided 64DD disk file is not valid"));
|
||||||
}
|
}
|
||||||
if disk_type >= DISK_VZONE_TO_PZONE.len() as u8 {
|
if disk_type >= VZONE_TO_PZONE.len() {
|
||||||
return Err(Error::new("Unknown disk type"));
|
return Err(Error::new("Unknown disk type"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,18 +213,17 @@ fn load_ndd(file: &mut File) -> Result<(Format, HashMap<usize, Mapping>), Error>
|
|||||||
let data = load_sys_lba(file, lba)?;
|
let data = load_sys_lba(file, lba)?;
|
||||||
let valid = verify_sys_lba(&data, SYSTEM_SECTOR_LENGTH);
|
let valid = verify_sys_lba(&data, SYSTEM_SECTOR_LENGTH);
|
||||||
if !valid {
|
if !valid {
|
||||||
bad_lba.push(lba);
|
bad_lbas.push(lba);
|
||||||
}
|
}
|
||||||
id_lba_valid |= valid;
|
id_lba_valid |= valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !id_lba_valid {
|
if !id_lba_valid {
|
||||||
return Err(Error::new("No valid ID LBA found"));
|
return Err(Error::new("No valid ID LBA found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut zone_bad_tracks: Vec<Vec<usize>> = Vec::new();
|
let mut zone_bad_tracks: Vec<Vec<usize>> = Vec::new();
|
||||||
|
|
||||||
for (zone, info) in DISK_ZONES.iter().enumerate() {
|
for (zone, info) in ZONE_MAPPING.iter().enumerate() {
|
||||||
let mut bad_tracks: Vec<usize> = Vec::new();
|
let mut bad_tracks: Vec<usize> = Vec::new();
|
||||||
let start = if zone == 0 { 0 } else { sys_data[0x07 + zone] };
|
let start = if zone == 0 { 0 } else { sys_data[0x07 + zone] };
|
||||||
let stop = sys_data[0x07 + zone + 1];
|
let stop = sys_data[0x07 + zone + 1];
|
||||||
@ -241,60 +238,47 @@ fn load_ndd(file: &mut File) -> Result<(Format, HashMap<usize, Mapping>), Error>
|
|||||||
|
|
||||||
let mut mapping = HashMap::new();
|
let mut mapping = HashMap::new();
|
||||||
|
|
||||||
let mut current_lba: usize = 0;
|
let mut lba: usize = 0;
|
||||||
|
let mut offset: usize = 0;
|
||||||
let mut starting_block: usize = 0;
|
let mut starting_block: usize = 0;
|
||||||
let mut file_offset: usize = 0;
|
|
||||||
|
|
||||||
for zone in DISK_VZONE_TO_PZONE[disk_type as usize] {
|
for (vzone, &pzone) in VZONE_TO_PZONE[disk_type].iter().enumerate() {
|
||||||
let DiskZone {
|
let DiskZone {
|
||||||
head,
|
head,
|
||||||
sector_length,
|
sector_length,
|
||||||
tracks,
|
tracks,
|
||||||
track_offset,
|
track_offset,
|
||||||
} = DISK_ZONES[zone];
|
} = ZONE_MAPPING[pzone];
|
||||||
|
|
||||||
let mut track = track_offset;
|
let zone_tracks: Box<dyn Iterator<Item = usize>> = if head == 0 {
|
||||||
|
Box::new(0..tracks)
|
||||||
|
} else {
|
||||||
|
Box::new((0..tracks).rev())
|
||||||
|
};
|
||||||
|
|
||||||
for zone_track in 0..tracks {
|
for zone_track in zone_tracks {
|
||||||
let current_zone_track = if head == 0 {
|
if !zone_bad_tracks[pzone].contains(&zone_track) {
|
||||||
zone_track
|
for block in 0..BLOCKS_PER_TRACK {
|
||||||
} else {
|
let track = track_offset + zone_track;
|
||||||
(tracks - 1) - zone_track
|
let location = (track << 2) | (head << 1) | (starting_block ^ block);
|
||||||
};
|
let length = sector_length * SECTORS_PER_BLOCK;
|
||||||
|
if !bad_lbas.contains(&lba) {
|
||||||
if zone_bad_tracks[zone].contains(¤t_zone_track) {
|
let writable = vzone >= ROM_ZONES[disk_type];
|
||||||
track = if head == 0 {
|
mapping.insert(
|
||||||
track + 1
|
location,
|
||||||
} else {
|
Mapping {
|
||||||
track.saturating_sub(1)
|
lba,
|
||||||
};
|
offset,
|
||||||
continue;
|
length,
|
||||||
}
|
writable,
|
||||||
|
},
|
||||||
for block in 0..BLOCKS_PER_TRACK {
|
);
|
||||||
let location = track << 2 | head << 1 | (starting_block ^ block);
|
}
|
||||||
let length = sector_length * SECTORS_PER_BLOCK;
|
lba += 1;
|
||||||
if !bad_lba.contains(¤t_lba) {
|
offset += length;
|
||||||
mapping.insert(
|
|
||||||
location,
|
|
||||||
Mapping {
|
|
||||||
lba: current_lba,
|
|
||||||
offset: file_offset,
|
|
||||||
length,
|
|
||||||
writable: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
file_offset += length;
|
starting_block ^= 1;
|
||||||
current_lba += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
track = if head == 0 {
|
|
||||||
track + 1
|
|
||||||
} else {
|
|
||||||
track.saturating_sub(1)
|
|
||||||
};
|
|
||||||
starting_block ^= 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,14 +501,14 @@ fn handle_64dd_command(connection: Connection, args: &_64DDArgs) -> Result<(), s
|
|||||||
if let Some(ref mut disk) = selected_disk {
|
if let Some(ref mut disk) = selected_disk {
|
||||||
let reply_packet = match packet.kind {
|
let reply_packet = match packet.kind {
|
||||||
sc64::DiskPacketKind::Read => {
|
sc64::DiskPacketKind::Read => {
|
||||||
print!("[R]");
|
print!("{}", "[R]".cyan());
|
||||||
disk.read_block(track, head, block)?.map(|data| {
|
disk.read_block(track, head, block)?.map(|data| {
|
||||||
packet.info.set_data(&data);
|
packet.info.set_data(&data);
|
||||||
packet
|
packet
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sc64::DiskPacketKind::Write => {
|
sc64::DiskPacketKind::Write => {
|
||||||
print!("[W]");
|
print!("{}", "[W]".yellow());
|
||||||
let data = &packet.info.data;
|
let data = &packet.info.data;
|
||||||
disk.write_block(track, head, block, data)?.map(|_| packet)
|
disk.write_block(track, head, block, data)?.map(|_| packet)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user