mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
add virtual disadiff mounting code
This commit is contained in:
parent
7322e31f43
commit
1026a60597
247
arm9/source/virtual/vdisadiff.c
Normal file
247
arm9/source/virtual/vdisadiff.c
Normal file
@ -0,0 +1,247 @@
|
||||
#include "vdisadiff.h"
|
||||
#include "disadiff.h"
|
||||
#include "common.h"
|
||||
#include "image.h"
|
||||
#include "vtickdb.h" // So we can mount a file as vdisadiff and vtickdb simeltaneously
|
||||
|
||||
#define VFLAG_PARTITION_B (1 << 31)
|
||||
|
||||
#define MAX_IVFC_RANGES 1024
|
||||
|
||||
typedef struct {
|
||||
u32 offset;
|
||||
u32 size;
|
||||
} __attribute__((packed)) DisaDiffIvfcRange;
|
||||
|
||||
typedef struct {
|
||||
u32 n_ivfc_ranges;
|
||||
DisaDiffIvfcRange ivfc_lvl4_ranges[MAX_IVFC_RANGES];
|
||||
DisaDiffRWInfo rw_info;
|
||||
} __attribute__((packed)) VDisaDiffPartitionInfo;
|
||||
|
||||
static VDisaDiffPartitionInfo* partitionA_info = NULL;
|
||||
static VDisaDiffPartitionInfo* partitionB_info = NULL;
|
||||
|
||||
static void AlignDisaDiffIvfcRange(DisaDiffIvfcRange* range, u32 log_block_size) {
|
||||
range->offset = (range->offset >> log_block_size) << log_block_size;
|
||||
range->size = align(range->size, 1 << log_block_size);
|
||||
}
|
||||
|
||||
static u32 MergeDisaDiffIvfcRange(DisaDiffIvfcRange new_range, DisaDiffIvfcRange* ranges, u32* n_ranges) {
|
||||
const u32 new_end_offset = new_range.offset + new_range.size;
|
||||
bool add_range = true;
|
||||
|
||||
if (*n_ranges > MAX_IVFC_RANGES)
|
||||
return 1;
|
||||
|
||||
for (u32 i = 0; i < *n_ranges; i++) {
|
||||
u32 end_offset = ranges[i].offset + ranges[i].size;
|
||||
|
||||
if (new_range.offset > ranges[i].offset) {
|
||||
if (end_offset >= new_range.offset) {
|
||||
add_range = false;
|
||||
|
||||
if (end_offset < new_end_offset) {
|
||||
ranges[i].size += new_end_offset - end_offset;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if (ranges[i].offset > new_range.offset) {
|
||||
if (new_end_offset >= ranges[i].offset) {
|
||||
add_range = false;
|
||||
|
||||
if (new_end_offset < end_offset) {
|
||||
ranges[i].offset = new_range.offset;
|
||||
ranges[i].size = new_range.size + end_offset - new_end_offset;
|
||||
} else {
|
||||
ranges[i] = new_range;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
} else {
|
||||
add_range = false;
|
||||
ranges[i].size = max(new_range.size, ranges[i].size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (add_range) {
|
||||
if (*n_ranges == MAX_IVFC_RANGES - 1)
|
||||
return 1;
|
||||
|
||||
ranges[(*n_ranges)++] = new_range;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 FixVDisaDiffIvfcHashChain(bool partitionB) {
|
||||
VDisaDiffPartitionInfo* info = partitionB ? partitionB_info : partitionA_info;
|
||||
if (!info) return 1;
|
||||
|
||||
if (info->n_ivfc_ranges == 0)
|
||||
return 0;
|
||||
|
||||
DisaDiffIvfcRange* ivfc_range_buffer = malloc(sizeof(DisaDiffIvfcRange) * 4 * MAX_IVFC_RANGES);
|
||||
if (!ivfc_range_buffer)
|
||||
return 1;
|
||||
|
||||
u32 n_ivfc_ranges = 0;
|
||||
|
||||
for (u32 i = 0; i < info->n_ivfc_ranges; i++) {
|
||||
if (MergeDisaDiffIvfcRange(info->ivfc_lvl4_ranges[i], &(ivfc_range_buffer[3 * MAX_IVFC_RANGES]), &n_ivfc_ranges) != 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (u32 level = 4; level > 0; level--) {
|
||||
u32 next_n_ivfc_ranges = 0;
|
||||
DisaDiffIvfcRange* ivfc_ranges = &(ivfc_range_buffer[(level - 1) * MAX_IVFC_RANGES]);
|
||||
DisaDiffIvfcRange* next_ivfc_ranges = ((level == 1) ? NULL : ivfc_ranges - MAX_IVFC_RANGES);
|
||||
|
||||
for (u32 j = 0; j < n_ivfc_ranges; j++) {
|
||||
DisaDiffIvfcRange next_range;
|
||||
|
||||
if (FixDisaDiffIvfcLevel(&(info->rw_info), level, ivfc_ranges[j].offset, ivfc_ranges[j].size, &(next_range.offset), &(next_range.size)) != 0)
|
||||
return 1;
|
||||
|
||||
if (next_ivfc_ranges) {
|
||||
AlignDisaDiffIvfcRange(&next_range, (&(info->rw_info.log_ivfc_lvl1))[level - 2]);
|
||||
if (MergeDisaDiffIvfcRange(next_range, next_ivfc_ranges, &next_n_ivfc_ranges) != 0)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
n_ivfc_ranges = next_n_ivfc_ranges;
|
||||
}
|
||||
|
||||
free(ivfc_range_buffer);
|
||||
|
||||
if (FixDisaDiffIvfcLevel(&(info->rw_info), 0, 0, 0, NULL, NULL) != 0)
|
||||
return 1;
|
||||
|
||||
info->n_ivfc_ranges = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DeinitVDisaDiffDrive(void) {
|
||||
if (partitionA_info) {
|
||||
FixVDisaDiffIvfcHashChain(false);
|
||||
if (partitionA_info->rw_info.dpfs_lvl2_cache)
|
||||
free(partitionA_info->rw_info.dpfs_lvl2_cache);
|
||||
free(partitionA_info);
|
||||
partitionA_info = NULL;
|
||||
}
|
||||
|
||||
if (partitionB_info) {
|
||||
FixVDisaDiffIvfcHashChain(true);
|
||||
if (partitionB_info->rw_info.dpfs_lvl2_cache)
|
||||
free(partitionB_info->rw_info.dpfs_lvl2_cache);
|
||||
free(partitionB_info);
|
||||
partitionB_info = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
u64 InitVDisaDiffDrive(void) {
|
||||
DisaDiffRWInfo info;
|
||||
u64 type;
|
||||
|
||||
DeinitVDisaDiffDrive();
|
||||
|
||||
if (!(type = (GetMountState() & (SYS_DISA | SYS_DIFF))))
|
||||
return 0;
|
||||
|
||||
if ((GetDisaDiffRWInfo(NULL, &info, false) != 0) ||
|
||||
(!((bool)info.ivfc_use_extlvl4) && (!(info.dpfs_lvl2_cache = (u8*) malloc(info.size_dpfs_lvl2)) ||
|
||||
(BuildDisaDiffDpfsLvl2Cache(NULL, &info, info.dpfs_lvl2_cache, info.size_dpfs_lvl2) != 0))))
|
||||
return 0;
|
||||
|
||||
if (!(partitionA_info = malloc(sizeof(VDisaDiffPartitionInfo)))) {
|
||||
free(info.dpfs_lvl2_cache);
|
||||
partitionA_info = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(partitionA_info, 0, sizeof(VDisaDiffPartitionInfo));
|
||||
partitionA_info->rw_info = info;
|
||||
|
||||
if ((type & SYS_DISA) && (GetDisaDiffRWInfo(NULL, &info, true) == 0)) {
|
||||
if (!(info.dpfs_lvl2_cache = (u8*) malloc(info.size_dpfs_lvl2)) ||
|
||||
(BuildDisaDiffDpfsLvl2Cache(NULL, &info, info.dpfs_lvl2_cache, info.size_dpfs_lvl2) != 0)) {
|
||||
if (partitionA_info->rw_info.dpfs_lvl2_cache) free(partitionA_info->rw_info.dpfs_lvl2_cache);
|
||||
free(partitionA_info);
|
||||
partitionA_info = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(partitionB_info = malloc(sizeof(VDisaDiffPartitionInfo)))) {
|
||||
if (info.dpfs_lvl2_cache) free(info.dpfs_lvl2_cache);
|
||||
if (partitionA_info->rw_info.dpfs_lvl2_cache) free(partitionA_info->rw_info.dpfs_lvl2_cache);
|
||||
free(partitionA_info);
|
||||
partitionA_info = NULL;
|
||||
partitionB_info = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(partitionB_info, 0, sizeof(VDisaDiffPartitionInfo));
|
||||
partitionB_info->rw_info = info;
|
||||
}
|
||||
|
||||
InitVTickDbDrive();
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
u64 CheckVDisaDiffDrive(void) {
|
||||
u64 type = GetMountState() & (SYS_DISA | SYS_DIFF);
|
||||
|
||||
return (type && partitionA_info) ? type : 0;
|
||||
}
|
||||
|
||||
// Can be very lazy here because there are only two files that can appear
|
||||
bool ReadVDisaDiffDir(VirtualFile* vfile, VirtualDir* vdir) {
|
||||
if (++(vdir->index) > 1)
|
||||
return false;
|
||||
|
||||
VDisaDiffPartitionInfo* info = vdir->index ? partitionB_info : partitionA_info;
|
||||
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
memset(vfile, 0, sizeof(VirtualFile));
|
||||
snprintf(vfile->name, 32, "partition%c.bin", vdir->index ? 'B' : 'A');
|
||||
vfile->size = info->rw_info.size_ivfc_lvl4;
|
||||
if (vdir->index) vfile->flags |= VFLAG_PARTITION_B;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ReadVDisaDiffFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) {
|
||||
VDisaDiffPartitionInfo* info = (vfile->flags & VFLAG_PARTITION_B) ? partitionB_info : partitionA_info;
|
||||
if (!info) return 1;
|
||||
u32 ret = ReadDisaDiffIvfcLvl4(NULL, &(info->rw_info), offset, count, buffer);
|
||||
//ShowPrompt(false, "Ret: %X\nCount: %X", ret, count);
|
||||
return (ret == count) ? 0 : 1;
|
||||
}
|
||||
|
||||
int WriteVDisaDiffFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count) {
|
||||
VDisaDiffPartitionInfo* info = (vfile->flags & VFLAG_PARTITION_B) ? partitionB_info : partitionA_info;
|
||||
if (!info) return 1;
|
||||
|
||||
if (WriteDisaDiffIvfcLvl4(NULL, &(info->rw_info), offset, count, buffer) != count) return 1;
|
||||
|
||||
DisaDiffIvfcRange range;
|
||||
range.offset = offset;
|
||||
range.size = count;
|
||||
AlignDisaDiffIvfcRange(&range, info->rw_info.log_ivfc_lvl4);
|
||||
if ((MergeDisaDiffIvfcRange(range, info->ivfc_lvl4_ranges, &(info->n_ivfc_ranges)) != 0) &&
|
||||
((FixVDisaDiffIvfcHashChain(vfile->flags & VFLAG_PARTITION_B) != 0) ||
|
||||
(MergeDisaDiffIvfcRange(range, info->ivfc_lvl4_ranges, &(info->n_ivfc_ranges)) != 0)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
11
arm9/source/virtual/vdisadiff.h
Normal file
11
arm9/source/virtual/vdisadiff.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "virtual.h"
|
||||
|
||||
void DeinitVDisaDiffDrive(void); // This is when the ivfc hash fixing actually happens - **MUST** be called before just powering off
|
||||
u64 InitVDisaDiffDrive(void);
|
||||
u64 CheckVDisaDiffDrive(void);
|
||||
|
||||
bool ReadVDisaDiffDir(VirtualFile* vfile, VirtualDir* vdir);
|
||||
int ReadVDisaDiffFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count);
|
||||
int WriteVDisaDiffFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count);
|
Loading…
x
Reference in New Issue
Block a user