/* * This file is part of libbdplus * Copyright (C) 2008-2010 Accident * Copyright (C) 2013 VideoLAN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #if defined(__MINGW32__) /* ftello64() and fseeko64() prototypes from stdio.h */ # undef __STRICT_ANSI__ #endif #include "segment.h" #include "event.h" #include "util/logging.h" #include "util/macro.h" #include "util/strutl.h" #include #include #include #if defined(__MINGW32__) # define fseeko fseeko64 #endif /* * */ /* CONV_TABLE_STRUCTURE +-----------+-----------+-----------+---------+ |31-------24|23-------16|15--------8|7-------0| |- Number of tables -| +-----------+-----------+-----------+---------+ |- table ID -| |- number of segments-| +-----------+-----------+-----------+---------+ |- Segment offset 1 -| |- Segment offset 2 -| |- Segment offset ..N -| +-----------+-----------+-----------+---------+ |- Segment 1 number of entries | +-----------+-----------+-----------+---------+ |- Segment 1 Entry 1 index value -| |- Segment 1 Entry 1 table entry 0-3 -| |- Segment 1 Entry 1 table entry 4-7 -| |- Segment 1 Entry 1 table entry 8-11 -| |- Segment 1 Entry 1 table entry 12-15 -| +-----------+-----------+-----------+---------+ |- Segment 1 Entry 2 index value -| |- Segment 1 Entry 2 table entry 0-3 -| |- Segment 1 Entry 2 table entry 4-7 -| |- Segment 1 Entry 2 table entry 8-11 -| |- Segment 1 Entry 2 table entry 12-15 -| +-----------+-----------+-----------+---------+ ... Segment 1 Entry N .... +-----------+-----------+-----------+---------+ |- Segment 2 number of entries | +-----------+-----------+-----------+---------+ |- Segment 2 Entry 1 index value -| */ // is 4+16 bytes in memory. Bigger here. struct entry_s { uint32_t index; uint8_t flags; uint16_t patch0_address_adjust; // 12 bits uint16_t patch1_address_adjust; // 12 bits uint8_t patch0_buffer_offset; uint8_t patch1_buffer_offset; uint8_t patch0[5]; uint8_t patch1[5]; uint8_t active; }; typedef struct entry_s entry_t; struct segment_s { uint32_t encrypted; uint32_t offset; uint32_t numEntries; entry_t *Entries; // Technically, we don't need to hold on to these two uint8_t mask[8]; uint8_t key[16]; }; typedef struct segment_s segment_t; struct subtable_s { uint32_t tableID; uint32_t numSegments; segment_t *Segments; // Extra variables. uint32_t merge; // Only used during merging }; typedef struct subtable_s subtable_t; struct conv_table_s { uint16_t numTables; subtable_t *Tables; uint32_t current_table; // When iterating uint32_t current_segment; // - "" - }; /* * */ struct bdplus_st_s { conv_table_t *table; // The following is used for streaming, to be more // efficient and record offset. uint32_t stream_table; uint32_t stream_segment; uint32_t stream_entry; uint64_t stream_offset; uint64_t next_patch_offset; /* optimize patching */ }; /* * */ uint32_t segment_numTables(conv_table_t *ct) { return ct ? ct->numTables : 0; } uint32_t segment_numEntries(conv_table_t *ct) { uint32_t entries = 0; if (ct && ct->current_table < ct->numTables) { subtable_t *subtable = &ct->Tables[ ct->current_table ]; unsigned ii; for (ii = 0; ii < subtable->numSegments; ii++) { entries += subtable->Segments[ii].numEntries; } } return entries; } int32_t segment_setTable(conv_table_t **conv_tab, uint8_t *Table, uint32_t len) { uint32_t numTables, table, currseg, currentry; uint32_t tmp; conv_table_t *ct; subtable_t *subtable; segment_t *segment; entry_t *entry; uint32_t ptr = 0; uint32_t offset = 0; uint32_t encrypted_segments = 0; if (!Table || !len) return -1; BD_DEBUG(DBG_BDPLUS,"[segment] Starting decode of conv_tab.bin: %p (%d)\n", Table, len); ct = *conv_tab; // If we do not already have a conv_tab, allocate it. if (!ct) { ct = (conv_table_t *) malloc(sizeof(*ct)); if (!ct) return -2; memset(ct, 0, sizeof(*ct)); *conv_tab = ct; } // Update the number of tables we received numTables = FETCHU2(&Table[ptr]); if (ct->numTables && (ct->numTables != numTables)) { BD_DEBUG(DBG_BDPLUS,"[segment] Warning, numTables changed between conv_tab parts!\n"); } ct->numTables = numTables; ptr += 2; // Let nextSegment() initialise these, So that we can ignore // any prior calls to trap_Finished(). ct->current_table = 0xffffffff; ct->current_segment = 0xffffffff; // Allocate area to hold all subtable structs, if not already allocated if (!ct->Tables) { ct->Tables = (subtable_t *) malloc(sizeof(subtable_t) * ct->numTables); if (!ct->Tables) return segment_freeTable(conv_tab); memset(ct->Tables, 0, sizeof(subtable_t) * numTables); } BD_DEBUG(DBG_BDPLUS,"[segment] num tables %d\n", numTables); for (table = 0; table < numTables; table++) { uint32_t tableID; uint32_t numSegments; // Assign pointer so we don't need to keep dereferencing subtable = &ct->Tables[ table ]; tableID = FETCH4(&Table[ptr]); ptr += 4; if (subtable->tableID && (subtable->tableID != tableID)) { BD_DEBUG(DBG_BDPLUS,"[segment] Warning: tableID changed %08X != %08X\n", subtable->tableID, tableID); } subtable->tableID = tableID; // Here, we might increase the number of segments. //subtable->numSegments = FETCHU2(&Table[ptr]); numSegments = FETCHU2(&Table[ptr]); ptr += 2; // Don't allocate if no (new) segments if (!numSegments) continue; if (subtable->numSegments && (subtable->numSegments != numSegments)) { BD_DEBUG(DBG_BDPLUS,"[segment] Warning: numSegments changed %08X != %08X\n", subtable->numSegments, numSegments); } subtable->numSegments = numSegments; BD_DEBUG(DBG_BDPLUS,"[segment] Table %d ID %08X, %u segments\n", table, subtable->tableID, subtable->numSegments); // Allocate area if required if (!subtable->Segments) { subtable->Segments = (segment_t *) malloc(sizeof(segment_t) * numSegments); if (!subtable->Segments) continue; memset(subtable->Segments, 0, sizeof(segment_t) * numSegments); } // Loop on all segments for (currseg = 0; currseg < numSegments; currseg++) { segment = &subtable->Segments[ currseg ]; offset = FETCH4(&Table[ptr + (currseg * 4) ]); segment->offset = offset; // not really used segment->numEntries = FETCH4(&Table[offset]); offset += 4; // Don't allocate if no entries if (!segment->numEntries) continue; BD_DEBUG(DBG_BDPLUS," Segment %d offset %08X -> %d entries\n", currseg, offset-4, segment->numEntries); segment->Entries = (entry_t *) malloc(sizeof(entry_t) * segment->numEntries); if (!segment->Entries) continue; // If we have non-zero entries, assume they are encrypted segment->encrypted = 1; encrypted_segments++; memset(segment->Entries, 0, sizeof(entry_t) * segment->numEntries); // First read in the index table for (currentry = 0; currentry < segment->numEntries; currentry++) { entry = &segment->Entries[ currentry ]; entry->index = FETCH4(&Table[offset]); offset += 4; } // Now read in the data. for (currentry = 0; currentry < segment->numEntries; currentry++) { entry = &segment->Entries[ currentry ]; entry->flags = Table[ offset ]; offset += 1; tmp = FETCH4(&Table[offset]); // only fetch 3bytes, 24 bits. offset += 3; tmp &= 0xFFFFFF00; entry->patch0_address_adjust = (tmp & 0xFFF00000) >> 20; entry->patch1_address_adjust = (tmp & 0x000FFF00) >> 8; entry->patch0_buffer_offset = Table[offset++]; entry->patch1_buffer_offset = Table[offset++]; memcpy(entry->patch0, &Table[ offset ], sizeof(entry->patch0)); offset += 5; memcpy(entry->patch1, &Table[ offset ], sizeof(entry->patch1)); offset += 5; } // for currentry } // for currseg // offset should point at the very end of the last entry, which // should be the next table BD_DEBUG(DBG_BDPLUS,"[segment] Table done. Setting ptr to %08X\n", offset); ptr = offset; } // for table BD_DEBUG(DBG_BDPLUS,"[segments] Done parsing. %d segments need decrypting.\n", encrypted_segments); return ct->numTables; } int32_t segment_freeTable(conv_table_t **Table) { uint32_t table, currseg; conv_table_t *ct; subtable_t *subtable; segment_t *segment; BD_DEBUG(DBG_BDPLUS,"[segment] freeing conv_tab.bin\n"); ct = *Table; for (table = 0; table < ct->numTables; table++) { // Assign pointer so we don't need to keep dereferencing subtable = &ct->Tables[ table ]; for (currseg = 0; currseg < subtable->numSegments; currseg++) { segment = &subtable->Segments[ currseg ]; X_FREE(segment->Entries); segment->numEntries = 0; } // Segments X_FREE(subtable->Segments); subtable->numSegments = 0; } // tables X_FREE(ct->Tables); ct->numTables = 0; *Table = NULL; return 0; } // // This takes a tableID, and assigned current_table to the correct // table with said ID. // int32_t segment_setSegment(conv_table_t *conv_tab, uint32_t tableID, uint32_t segment) { uint32_t table; if (!conv_tab) return 1; for (table = 0; table < conv_tab->numTables; table++) { if (conv_tab->Tables[ table ].tableID == tableID) { conv_tab->current_table = table; break; } } if (table >= conv_tab->numTables) { BD_DEBUG(DBG_BDPLUS,"[segment] failed to locate tableID %u.\n", tableID); table = 0; //return 1; // Function should probably signal failures. } BD_DEBUG(DBG_BDPLUS,"[segment] Set to table %u (tableID %u) and segment %u\n", table, conv_tab->Tables[ table ].tableID, segment); //(*conv_tab)->current_table = tableID; conv_tab->current_table = table; conv_tab->current_segment = segment; return 0; } int32_t segment_nextSegment(conv_table_t *conv_tab, uint32_t *ret_table, uint32_t *ret_segment) { uint32_t table, segment; if (conv_tab->current_table == 0xFFFFFFFF) conv_tab->current_table = 0; if (conv_tab->current_segment == 0xFFFFFFFF) conv_tab->current_segment = 0; // Find next encrypted segment, looping all titles, and segments. for (table = conv_tab->current_table; table < conv_tab->numTables; table++) { for (segment = conv_tab->current_segment; segment < conv_tab->Tables[ table ].numSegments; segment++) { if (conv_tab->Tables[ table ].Segments[ segment ].encrypted) { conv_tab->current_table = table; conv_tab->current_segment = segment; BD_DEBUG(DBG_BDPLUS,"[segment] next set to table %d segment %d (tableID %u)\n", table, segment, conv_tab->Tables[ table ].tableID); // All exposed variables count from "1". // We should probably return tableID, not table index. //*ret_table = table; *ret_table = conv_tab->Tables[ table ].tableID; *ret_segment = segment; return 1; } // if encrypted } // for segments conv_tab->current_segment = 0; } // for tables conv_tab->current_table = 0; return 0; } // // Merge 2 conv_tabs into 1. Currently, this assumes that conv_tab will always // hold complete tableID tables. So if we already have a tableID, it is skipped. // This may need to be updated in future, to handle more segments, and entries // added to existing tableIDs. However, there have been no example of such // tables so far. // uint32_t segment_mergeTables(conv_table_t *set1, conv_table_t *set2) { uint32_t numMergeTables, ctable, i; BD_DEBUG(DBG_BDPLUS,"[segment] Merging tables.. \n"); // Count the number of "new" tableIDs in set2. numMergeTables = 0; for (ctable = 0; ctable < set2->numTables; ctable++) { // See if it exists already, if so, skip it. for (i = 0; i < set1->numTables; i++) { if (set2->Tables[ ctable ].tableID == set1->Tables[ i ].tableID) { if (set1->Tables[i].numSegments != set2->Tables[ctable].numSegments) { BD_DEBUG(DBG_BDPLUS,"[segment] Warning, skipping tableID but differenting numSegments\n"); } // if numSegments break; } // tableID == tableID } // for set1 if (i >= set1->numTables) { // Exhausted set1? It is new. numMergeTables++; set2->Tables[ ctable ].merge = 1; } // tableID } // for set2 BD_DEBUG(DBG_BDPLUS,"[segment] Received %u new tableIDs to merge.\n", numMergeTables); if (!numMergeTables) return 0; // Grow the list to hold the new tables. void *tmp = set1->Tables; set1->Tables = (subtable_t *) realloc(set1->Tables, (set1->numTables + numMergeTables) * sizeof(subtable_t)); if (!set1->Tables) { X_FREE(tmp); set1->numTables = 0; BD_DEBUG(DBG_BDPLUS,"[segment] Out of memory.\n"); return 0; } // Clear the new nodes memset(&set1->Tables[ set1->numTables ], 0, numMergeTables * sizeof(subtable_t)); // Merge the tables. For now, we destroy set2, but we could do this // by cloning the table. Perhaps in future versions... for (ctable = 0, i = set1->numTables; ctable < set2->numTables; ctable++, i++) { if (!set2->Tables[ ctable ].merge) continue; BD_DEBUG(DBG_BDPLUS,"[segment] merging tableID %08X, numSegments %u\n", set2->Tables[ ctable ].tableID, set2->Tables[ ctable ].numSegments); memcpy( &set1->Tables[ i ], &set2->Tables[ ctable ], sizeof(set1->Tables[ i ])); // Since we naughtily stole the node, and ptrs, zero the original // so it doesn't go and free anything. memset( &set2->Tables[ ctable ], 0, sizeof(set2->Tables[ ctable ])); } set1->numTables += numMergeTables; BD_DEBUG(DBG_BDPLUS,"[segment] Merge complete. New total tables %u.\n", set1->numTables); return numMergeTables; } // // VM has received a segment key, and mask for a subtable&segment pair. // The key is 16 bytes of XOR data to use, and mask is 8 bytes, where byte // 7 has bits 0-7, and byte 0 has 56-63 bits. // int32_t segment_decrypt(conv_table_t *conv_tab, uint8_t *key, uint8_t *mask) { static const uint8_t empty[32] = {0}; uint32_t i; segment_t *segment; uint32_t currentry, tmp; uint32_t removed = 0; entry_t *entry; uint8_t bits = 0; if (!conv_tab) return 0; if (conv_tab->current_table == 0xFFFFFFFF) return 0; if (conv_tab->current_segment == 0xFFFFFFFF) return 0; if (!memcmp(key, empty, 16)) { BD_DEBUG(DBG_BDPLUS | DBG_CRIT, "[segment] WARNING: receiverd empty segment key\n"); } char str[128]; BD_DEBUG(DBG_BDPLUS | DBG_CRIT,"[segment] Key %2u, %3u: %s\n", conv_tab->current_table, conv_tab->current_segment, str_print_hex(str, key, 16)); BD_DEBUG(DBG_BDPLUS," mask: %s\n", str_print_hex(str, mask, 8)); BD_DEBUG(DBG_BDPLUS,"Q: %s\n", str_print_hex(str, mask, 39)); if ( conv_tab->current_table >= conv_tab->numTables) { BD_DEBUG(DBG_BDPLUS | DBG_CRIT, "[segment] decrypt, current_table (%d) >= numTables! help?!\n", conv_tab->numTables); return 0; } segment = &conv_tab->Tables[ conv_tab->current_table ].Segments[ conv_tab->current_segment ]; // If already decrypted, don't XOR again if (!segment->encrypted) { if (!memcmp(segment->key, key, sizeof(segment->key))) { /* key not changed */ return 0; } if (memcmp(segment->key, empty, 16)) { /* old key was not empty */ BD_DEBUG(DBG_BDPLUS | DBG_CRIT, "[segment] WARNING: Segment already decrypted with different key\n"); return 0; } BD_DEBUG(DBG_BDPLUS | DBG_CRIT, "[segment] Old key was empty, decrypting again with new key\n"); } memcpy(segment->key, key, sizeof(segment->key)); memcpy(segment->mask, mask, sizeof(segment->mask)); // mark it not encrypted so that nextSegment do not spin forever segment->encrypted = 0; for (currentry = 0; currentry < segment->numEntries; currentry++) { entry = &segment->Entries[ currentry ]; // XOR that sucker. entry->flags ^= key[0]; tmp = FETCH4(&key[1]); // only fetch 3bytes, 24 bits. tmp &= 0xFFFFFF00; entry->patch0_address_adjust ^= (tmp & 0xFFF00000) >> 20; entry->patch1_address_adjust ^= (tmp & 0x000FFF00) >> 8; entry->patch0_buffer_offset ^= key[4]; entry->patch1_buffer_offset ^= key[5]; for (i=0; i < sizeof(entry->patch0); i++) { entry->patch0[i] ^= key[ 6 + i ]; entry->patch1[i] ^= key[ 11 + i ]; } } // After decrypting the whole segment, re-parse it to remove any // repair descriptors that are "fakes". for (currentry = 0; currentry < segment->numEntries; currentry++) { entry = &segment->Entries[ currentry ]; // Check the flag field, if it is type 2 (%10xxxxxx) then we // need to use the "mask" field to determine if it should be // applied of not. switch((entry->flags>>6) & 0x3) { case 0: BD_DEBUG(DBG_BDPLUS | DBG_CRIT,"[segment] entry type 0. Don't know what to do\n"); break; case 1: // Type 1, always active. entry->active = 1; break; case 2: // Type 2, index mask[] to check if active bits = entry->flags & 0x3f; // 6 bits, 0-63 // If set true, it is active, so process next.. // Fix me "7", sizeof(mask)? if (((mask[ 7-(bits >> 3) ] & (1<<(bits & 0x07))))) { entry->active = 1; continue; } // Bit was not set, so it is in-active, and should be removed... BD_DEBUG(DBG_BDPLUS,"[segment] removing entry %3u (flags %02X: bits %u => byte %u, set %02X to false)\n", currentry, entry->flags & 0xC0, bits,7-(bits >> 3), 1<<(bits&0x07)); entry->active = 0; #if 0 removed++; // Decrease the number of entries, and copy over the remaining // entry nodes after this. for (i = (int32_t)currentry; i < (int32_t)(segment->numEntries-1); i++) { memcpy(&segment->Entries[ i ], &segment->Entries[ i+1 ], sizeof(segment->Entries[ i ])); } segment->numEntries--; // Also decreate for counter, so the new "this" gets processed. currentry--; #endif bits = 0; // Just clearing for debug print. break; case 3: BD_DEBUG(DBG_BDPLUS | DBG_CRIT,"[segment] entry type 3. Don't know what to do\n"); entry->active = 0; break; default: BD_DEBUG(DBG_BDPLUS,"[segment] I can't get here.\n"); break; } // switch flags } // for entries if (removed) BD_DEBUG(DBG_BDPLUS,"[segment] cleaned out %u entries.\n", removed); return 1; } static int segment_sortby_tableid(const void *a1, const void *a2) { const subtable_t *e1 = (const subtable_t *) a1; const subtable_t *e2 = (const subtable_t *) a2; if (e1->tableID < e2->tableID) return -1; if (e1->tableID > e2->tableID) return 1; return 0; } int32_t segment_save(conv_table_t *ct, FILE *fd) { uint16_t u16; uint32_t u32; uint32_t table, currseg, currentry; subtable_t *subtable; segment_t *segment; entry_t *entry; uint8_t tmp[4]; // Hold 3 byte value in right endianess uint32_t offset; size_t rval; if (!ct) return -1; BD_DEBUG(DBG_BDPLUS,"[segment] saving convTable\n"); // Sort based on tableID ? qsort(ct->Tables, // Base ct->numTables, // nmemb sizeof(ct->Tables[0]), // size segment_sortby_tableid);// compar // Write number of tables STORE2((uint8_t *)&u16, ct->numTables); rval = fwrite(&u16, sizeof(u16), 1, fd); if(rval != 1) BD_DEBUG(DBG_BDPLUS,"[segment] Unable to write number of tables\n"); // We use "offset" to keep track of where we are, and were we WILL write // entries, for the index-offset-array we write at the start of each // segment. offset = sizeof(ct->numTables); for (table = 0; table < ct->numTables; table++) { subtable = &ct->Tables[ table ]; BD_DEBUG(DBG_BDPLUS,"[segment] Saving table %u tableID %08X, numSegments %u\n", table, subtable->tableID, subtable->numSegments); STORE4((uint8_t *)&u32, subtable->tableID); rval = fwrite(&u32, sizeof(u32), 1, fd); offset += 4; STORE2((uint8_t *)&u16, subtable->numSegments); rval = fwrite(&u16, sizeof(u16), 1, fd); offset += 2; offset += subtable->numSegments * sizeof(uint32_t); // Write out segment index list for (currseg = 0; currseg < subtable->numSegments; currseg++) { segment = &subtable->Segments[ currseg ]; //BD_DEBUG(DBG_BDPLUS,"[segment] segment offset %08X computed %08X\n", // segment->offset, offset); //STORE4((uint8_t *)&u32, segment->offset); STORE4((uint8_t *)&u32, offset); rval = fwrite(&u32, sizeof(u32), 1, fd); // Increase offset based on size of entries. offset += sizeof(segment->numEntries); offset += segment->numEntries * sizeof(entry->index); // Size of entry, in case we have added information // in the structure, or packing differences with compilers. offset += segment->numEntries * 16; } // write out segments for (currseg = 0; currseg < subtable->numSegments; currseg++) { segment = &subtable->Segments[ currseg ]; // seek to make sure we are at the right offset? //BD_DEBUG(DBG_BDPLUS,"[segment] save offset(%08X)\n", segment->offset); // This code is broken after merging tables. // if (segment->offset) // fseek(fd, segment->offset, SEEK_SET); STORE4((uint8_t *)&u32, segment->numEntries); rval = fwrite(&u32, sizeof(u32), 1, fd); // Write out entry index list for (currentry = 0; currentry < segment->numEntries; currentry++) { entry = &segment->Entries[ currentry ]; STORE4((uint8_t *)&u32, entry->index); rval = fwrite(&u32, sizeof(u32), 1, fd); } // Write out entries for (currentry = 0; currentry < segment->numEntries; currentry++) { entry = &segment->Entries[ currentry ]; rval = fwrite(&entry->flags, 1, 1, fd); u32 = entry->patch0_address_adjust << 20; u32 |= (entry->patch1_address_adjust << 8); STORE4(tmp, u32); rval = fwrite(tmp, 3, 1, fd); rval = fwrite(&entry->patch0_buffer_offset, 1, 1, fd); rval = fwrite(&entry->patch1_buffer_offset, 1, 1, fd); rval = fwrite(&entry->patch0, sizeof(entry->patch0), 1, fd); rval = fwrite(&entry->patch1, sizeof(entry->patch1), 1, fd); } // entries } // segments } // tables return -1; } /* The first indexing value in the table is: 0x9900A. The first entry is: 4A 1A A0 53 64 B1 AA 2D 40 8E 4A 4D EF 4B 6B E5 (0x9900A+0x1AA)*0xC0 + 0x64 = 0x72D4764 // offset in the big file where the first 5 bytes are put to (0x9900A+0x1AA+0x053)*0xC0 + 0xB1 = 0x72D85F1 // offset in the big file where the second 5 bytes are put to ----- The second indexing value in the table is: 0x99C85. The second entry is: 8F 20 C0 11 AA 98 B6 F7 76 EC CB 7E 61 A3 C6 C1 (0x99C85+0x20C)*0xC0 + 0xAA = 0x736ED6A // offset in the big file where the third 5 bytes are put to (0x99C85+0x20C+0x011)*0xC0 + 0x98 = 0x736FA18 // offset in the big file where the fourth 5 bytes are put to */ int32_t segment_patchfile(conv_table_t *ct, uint32_t table, FILE *fd) { subtable_t *subtable; segment_t *segment; entry_t *entry; uint32_t currentry, currseg; uint64_t offset; int32_t firsttime = 10; BD_DEBUG(DBG_BDPLUS,"segment: direct patch title %d started.\n", table); subtable = &ct->Tables[ table ]; for (currseg = 0; currseg < subtable->numSegments; currseg++) { segment = &subtable->Segments[ currseg ]; for (currentry = 0; currentry < segment->numEntries; currentry++) { entry = &segment->Entries[ currentry ]; // Skip any entries that are in-active. if (!entry->active) continue; #if 1 if (firsttime) { BD_DEBUG(DBG_BDPLUS,"[segment] index %04X\n", entry->index); BD_DEBUG(DBG_BDPLUS,"[segment] flags %02X\n", entry->flags); BD_DEBUG(DBG_BDPLUS,"[segment] adjust0 %04X\n", entry->patch0_address_adjust); BD_DEBUG(DBG_BDPLUS,"[segment] adjust1 %04X\n", entry->patch1_address_adjust); BD_DEBUG(DBG_BDPLUS,"[segment] offset0 %02X\n", entry->patch0_buffer_offset); BD_DEBUG(DBG_BDPLUS,"[segment] offset1 %02X\n", entry->patch1_buffer_offset); BD_DEBUG(DBG_BDPLUS,"[segment] patch0 %02X%02X%02X%02X%02X\n", entry->patch0[0],entry->patch0[1],entry->patch0[2], entry->patch0[3],entry->patch0[4]); BD_DEBUG(DBG_BDPLUS,"[segment] patch1 %02X%02X%02X%02X%02X\n", entry->patch1[0],entry->patch1[1],entry->patch1[2], entry->patch1[3],entry->patch1[4]); } #endif // PATCH 0 offset = (( (uint64_t)entry->index + (uint64_t)entry->patch0_address_adjust) * (uint64_t)0xC0 + (uint64_t)entry->patch0_buffer_offset); if (firsttime) { BD_DEBUG(DBG_BDPLUS,"[segment] would seek to %016"PRIx64" to write patch0\n", offset); } if (fseeko(fd, offset, SEEK_SET)) { printf("Seek to offset %"PRIx64" failed. Stopping at table %d, segment %d, entry %d.\n", offset, table, currseg, currentry); return -1; } if (fwrite(entry->patch0, sizeof(entry->patch0), 1, fd) != 1) { printf("Write at offset %"PRIx64" failed. Stopping at table %d, segment %d, entry %d.\n", offset, table, currseg, currentry); return -1; } // PATCH 1 offset = (( (uint64_t)entry->index + (uint64_t)entry->patch0_address_adjust + (uint64_t)entry->patch1_address_adjust) * (uint64_t)0xC0 + (uint64_t)entry->patch1_buffer_offset); if (firsttime) { BD_DEBUG(DBG_BDPLUS,"[segment] would seek to %016"PRIx64" to write patch1\n", offset); } if (fseeko(fd, offset, SEEK_SET)) { printf("Seek to offset %"PRIx64" failed. Stopping at table %d, segment %d, entry %d.\n", offset, table, currseg, currentry); return -1; } if (fwrite(entry->patch1, sizeof(entry->patch1), 1, fd) != 1) { printf("Write at offset %"PRIx64" failed. Stopping at table %d, segment %d, entry %d.\n", offset, table, currseg, currentry); return -1; } if (firsttime)firsttime--; } // for entries } // for segments return 0; } bdplus_st_t *segment_set_m2ts(conv_table_t *ct, uint32_t m2ts) { int table = -1; BD_DEBUG(DBG_BDPLUS, "set_m2ts(%05u.m2ts)\n", m2ts); if (!ct || !ct->numTables) { BD_DEBUG(DBG_CRIT, "set_m2ts(%05u.m2ts): no tables !\n", m2ts); return NULL; } unsigned ii; for (ii = 0; ii < ct->numTables; ii++) { if (ct->Tables[ii].tableID == m2ts) { table = ii; break; } } if (table < 0) { BD_DEBUG(DBG_BDPLUS, "no conversion table %05u.m2ts\n", m2ts); return NULL; } BD_DEBUG(DBG_BDPLUS, "using table index %d for %05u.m2ts\n", table, m2ts); /* empty table -> no patching needed */ int segments = 0; for (ii = 0; ii < ct->Tables[table].numSegments; ii++) { segments += ct->Tables[table].Segments[ii].numEntries; } if (segments < 1) { BD_DEBUG(DBG_BDPLUS, "conversion table is empty\n"); return NULL; } /* table not decrypted ? */ if (ct->Tables[table].Segments[0].encrypted) { BD_DEBUG(DBG_BDPLUS | DBG_CRIT, "conversion table for %05d.m2ts is still encrypted\n", m2ts); return NULL; } // Changing table, or seeking elsewhere, means zero the segment and // entry so we have to find them again. bdplus_st_t *st = calloc(1, sizeof(*st)); if (!st) { BD_DEBUG(DBG_CRIT, "out of memory\n"); return NULL; } st->stream_table = table; st->table = ct; BD_DEBUG(DBG_BDPLUS,"[segment] settable(%05u.m2ts): %p\n", m2ts, st); return st; } int32_t segment_patchseek(bdplus_st_t *ct, uint64_t offset) { // Changing table, or seeking elsewhere, means zero the segment and // entry so we have to find them again. ct->stream_segment = 0; ct->stream_entry = 0; ct->stream_offset = offset; ct->next_patch_offset = 0; BD_DEBUG(DBG_BDPLUS,"[segment] seek: %016"PRIx64"\n", offset); return 0; } // // Given a buffer and its size in bytes. Using the "offset" the buffer // starts at, work out if any patch Entries lie inside that offset, and // modify buffer as required. // int32_t segment_patch(bdplus_st_t *ct, int len, uint8_t *buffer) { uint64_t end_offset, start_offset, offset0, offset1, diff; subtable_t *subtable; segment_t *segment; entry_t *entry; uint32_t currentry, currseg; int32_t patches=0; BD_DEBUG(DBG_BDPLUS, "[segment] read(len %d): %016"PRIx64"\n", len, ct->stream_offset); // While stream_segment[stream_entry] is less than the end of the buffer // possible apply it // and iterate to the next one. start_offset = ct->stream_offset; end_offset = ct->stream_offset + (uint64_t) len; ct->stream_offset += (uint64_t) len; if (ct->next_patch_offset > end_offset) { return 0; } subtable = &ct->table->Tables[ ct->stream_table ]; for (currseg = ct->stream_segment; currseg < subtable->numSegments; currseg++, ct->stream_segment++ ) { segment = &subtable->Segments[ currseg ]; for (currentry = ct->stream_entry; currentry < segment->numEntries; currentry++, ct->stream_entry++ ) { entry = &segment->Entries[ currentry ]; // Skip any entries that are in-active. if (!entry->active) continue; offset0 = (((uint64_t)entry->index + (uint64_t)entry->patch0_address_adjust) * (uint64_t)0xC0 + (uint64_t)entry->patch0_buffer_offset); // If this Entry is beyond this buffer, stop here, we need // more data. if (offset0 > end_offset) { ct->next_patch_offset = offset0; return patches; } offset1 = (( (uint64_t)entry->index + (uint64_t)entry->patch0_address_adjust + (uint64_t)entry->patch1_address_adjust) * (uint64_t)0xC0 + (uint64_t)entry->patch1_buffer_offset); // While this Entry (patch1) is (completely) before this // buffer, skip to the next if (offset1+(uint64_t)sizeof(entry->patch1) <= start_offset) continue; // Ok, it is possible this Entry patch0, or patch1, lands // Inside this buffer, so lets process it. // Check patch0, really we do patch0 -4 >= offset, at least // one byte is inside. But since patch0 could be "0", we can // not do -4. // So we go +4 on offset, and do the same test. // AND if patch is < end_offset. // Consider patch0 if (offset0 < start_offset) { // patch0 goes over the start of buffer. diff = (start_offset - offset0); if (diff < (uint64_t) sizeof(entry->patch0)) { memcpy(buffer, &entry->patch0[ diff ], sizeof(entry->patch0) - (size_t)diff); patches++; } // 0-4 bytes } else { // greater-or-equal diff = (end_offset - offset0); if (diff < (uint64_t) sizeof(entry->patch0)) { // Stradling the end memcpy(&buffer[ len - diff ], entry->patch0, (size_t)diff); patches++; } else { // Entirely inside memcpy(&buffer[ len - diff ], entry->patch0, sizeof(entry->patch0)); patches++; } } // If this Entry is beyond this buffer, stop here, we need // more data. if (offset1 > end_offset) return patches; // Consider patch1 if (offset1 < start_offset) { // patch1 goes over the start of buffer. diff = (start_offset - offset1); if (diff < (uint64_t) sizeof(entry->patch1)) { memcpy(buffer, &entry->patch1[ diff ], sizeof(entry->patch1) - (size_t)diff); patches++; } // 0-4 bytes } else { // greater-or-equal diff = (end_offset - offset1); if (diff < (uint64_t) sizeof(entry->patch1)) { // Stradling the end memcpy(&buffer[ len - diff ], entry->patch1, (size_t)diff); patches++; } else { // Entirely inside memcpy(&buffer[ len - diff ], entry->patch1, sizeof(entry->patch1)); patches++; } } } // currentry ct->stream_entry = 0; } // curseg // If we set stream_segment to 0 here, we will forever scan the list. // So we leave it high //ct->stream_segment = 0; // We've run out of entries.. return patches; }