#include "vdisadiff.h" #include "disadiff.h" #include "common.h" #include "image.h" #include "vbdri.h" // So we can mount a file as vdisadiff and vbdri simeltaneously #define VFLAG_PARTITION_B (1 << 31) #define MAX_IVFC_RANGES 1024 typedef struct { u32 offset; u32 size; } PACKED_STRUCT DisaDiffIvfcRange; typedef struct { u32 n_ivfc_ranges; DisaDiffIvfcRange ivfc_lvl4_ranges[MAX_IVFC_RANGES]; DisaDiffRWInfo rw_info; } PACKED_STRUCT VDisaDiffPartitionInfo; static VDisaDiffPartitionInfo* partitionA_info = NULL; static VDisaDiffPartitionInfo* partitionB_info = NULL; static void AlignDisaDiffIvfcRange(DisaDiffIvfcRange* range, u32 log_block_size) { u32 end_offset = align(range->offset + range->size, 1 << log_block_size); range->offset = (range->offset >> log_block_size) << log_block_size; range->size = end_offset - range->offset; } 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) * 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, &n_ivfc_ranges) != 0) return 1; } for (u32 level = 4; level > 0; level--) { u32 next_n_ivfc_ranges = 0; DisaDiffIvfcRange* ivfc_ranges = (level % 2) ? info->ivfc_lvl4_ranges : ivfc_range_buffer; DisaDiffIvfcRange* next_ivfc_ranges = (level % 2) ? ivfc_range_buffer : info->ivfc_lvl4_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) || (!(info.dpfs_lvl2_cache = (u8*) malloc(info.size_dpfs_lvl2)) || (BuildDisaDiffDpfsLvl2Cache(NULL, &info, info.dpfs_lvl2_cache, info.size_dpfs_lvl2) != 0))) { free(info.dpfs_lvl2_cache); 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; } InitVBDRIDrive(); return type; } u64 CheckVDisaDiffDrive(void) { u64 type = GetMountState(); return ((type & (SYS_DISA | SYS_DIFF)) && 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; }