Minor changes.
[xy_vsfilter.git] / src / decss / udf.cpp
bloba91a6cde2da898b35e6516d43454057a25093a46
1 /*************************************************************************
2 vStrip by [maven] (maven@maven.de)
3 udf.c: routines for udf-parsing (because windows just doesn't cut it),
4 refs: udf102.pdf, udf200.pdf, ecma 167
5 (tabsize 2)
6 *************************************************************************/
8 #include "stdafx.h"
9 #include "udf.h"
11 static bool aspi_GetSectorInfo(const HANDLE hDrive, DWORD* sec_size, DWORD* max_sec)
13 LARGE_INTEGER size;
14 size.LowPart = GetFileSize(hDrive, (DWORD*)&size.HighPart);
16 *sec_size = 2048;
17 *max_sec = size.QuadPart / *sec_size;
19 return true;
22 static bool aspi_ReadSectors(const HANDLE hDrive, int lba, int nSectors, DWORD sec_size, BYTE* sector)
24 DWORD nbr = 0;
25 return lba*sec_size == SetFilePointer(hDrive, lba*sec_size, NULL, FILE_BEGIN)
26 && ReadFile(hDrive, sector, nSectors*sec_size, &nbr, NULL);
29 static bool udf_GetLBA(const tp_udf_FileEntry fe, const DWORD sec_size, DWORD *start, DWORD *end)
31 if (fe->LengthofAllocationDescriptors == 0)
32 return false;
33 switch (fe->ICBTag.Flags & udf_icbf_Mask)
35 case udf_icbf_ShortAd:
37 tp_udf_short_ad ad = (tp_udf_short_ad)(fe->ExtendedAttributes + fe->LengthofExtendedAttributes);
39 *start = ad->Location;
40 *end = *start + ((ad->Length & udf_LengthMask) - 1) / sec_size;
41 return true;
43 break;
44 case udf_icbf_LongAd:
46 tp_udf_long_ad ad = (tp_udf_long_ad)(fe->ExtendedAttributes + fe->LengthofExtendedAttributes);
48 *start = ad->Location.Location; // ignore partition number
49 *end = *start + ((ad->Length & udf_LengthMask) - 1) / sec_size;
50 return true;
52 break;
53 case udf_icbf_ExtAd:
55 tp_udf_ext_ad ad = (tp_udf_ext_ad)(fe->ExtendedAttributes + fe->LengthofExtendedAttributes);
57 *start = ad->Location.Location; // ignore partition number
58 *end = *start + ((ad->Length & udf_LengthMask) - 1) / sec_size;
59 return true;
61 break;
63 return false;
66 tp_udf_file udf_get_root(const HANDLE hDrive, const WORD partition_number)
68 BYTE sector[fio_SECTOR_SIZE];
69 tp_udf_tag tag = (tp_udf_tag)sector;
70 DWORD sec_size, max_sec, i, j;
71 DWORD MVDS_lba, MVDS_lba_end, MVDS_back_lba, MVDS_back_lba_end;
72 DWORD FileDescriptorSequence_lba, FileDescriptorSequence_lba_end;
73 DWORD partition_lba, parent_icb;
74 tp_udf_AnchorVolumeDescriptorPointer avd;
75 bool res, part_valid, vol_valid;
77 if (!aspi_GetSectorInfo(hDrive, &sec_size, &max_sec))
78 return NULL;
80 if (sec_size != fio_SECTOR_SIZE || max_sec < 256)
81 return NULL;
83 // read AnchorVolumeDescriptorPointer at 256 (or MaxSec) (Tag == 2)
84 res = aspi_ReadSectors(hDrive, 256, 1, sec_size, sector);
85 if (!res || tag->TagIdentifier != udf_TAG_AnchorVolumeDescriptor)
87 res = aspi_ReadSectors(hDrive, max_sec, 1, sec_size, sector);
88 if (!res || tag->TagIdentifier != udf_TAG_AnchorVolumeDescriptor)
89 return NULL;
92 // check Static Structures
94 // get MainVolumeDescriptorSequence Location & Length
95 avd = (tp_udf_AnchorVolumeDescriptorPointer)sector;
96 MVDS_lba = avd->MainVolumeDescriptorSequenceExtent.Location;
97 MVDS_lba_end = MVDS_lba + (avd->MainVolumeDescriptorSequenceExtent.Length - 1) / sec_size;
98 MVDS_back_lba = avd->ReserveVolumeDescriptorSequenceExtent.Location;
99 MVDS_back_lba_end = MVDS_back_lba + (avd->ReserveVolumeDescriptorSequenceExtent.Length - 1) / sec_size;
101 // read MVDS_Location..MVDS_Location + (MVDS_Length - 1) / SectorSize sectors
103 part_valid = vol_valid = false;
104 i = 1;
106 { // try twice (if we need to) for ReserveAnchor
107 j = MVDS_lba;
110 res = aspi_ReadSectors(hDrive, j++, 1, sec_size, sector);
111 if (res)
113 if (tag->TagIdentifier == udf_TAG_PartitionDescriptor && !part_valid)
114 { // get stuff out of partition
115 tp_udf_PartitionDescriptor par = (tp_udf_PartitionDescriptor )sector;
117 part_valid = par->PartitionNumber == partition_number;
118 if (part_valid)
119 { // extract par->PartitionStartingLocation, par->PartitionLength
120 partition_lba = par->PartitionStartingLocation;
123 else if (tag->TagIdentifier == udf_TAG_LogicalVolumeDescriptor && !vol_valid)
124 { // get stuff out of volume
125 tp_udf_LogicalVolumeDescriptor vol = (tp_udf_LogicalVolumeDescriptor)sector;
127 // check_volume sector size
128 vol_valid = (vol->LogicalBlockSize == sec_size) && (partition_number == vol->FileSetDescriptorSequence.Location.PartitionNumber);
129 if (vol_valid)
130 { // extract vol->FileSetDescriptorSequence
131 FileDescriptorSequence_lba = vol->FileSetDescriptorSequence.Location.Location;
132 FileDescriptorSequence_lba_end = FileDescriptorSequence_lba + ((vol->FileSetDescriptorSequence.Length & udf_LengthMask) - 1) / sec_size;
136 else
137 tag->TagIdentifier = 0;
138 } while (j <= MVDS_lba_end && tag->TagIdentifier != udf_TAG_TerminatingDescriptor && ((!part_valid) || (!vol_valid)));
140 if ((!part_valid) || (!vol_valid))
141 { // try backup
142 MVDS_lba = MVDS_back_lba;
143 MVDS_lba_end = MVDS_back_lba_end;
145 } while (i-- && ((!part_valid) || (!vol_valid)));
147 if (part_valid && vol_valid)
148 { // read FileSetDescriptor, get RootDir Location & Length, RootDir Length != 0
149 res = aspi_ReadSectors(hDrive, FileDescriptorSequence_lba + partition_lba, 1, sec_size, sector);
150 if (res && tag->TagIdentifier == udf_TAG_FileSetDescriptor)
152 tp_udf_FileSetDescriptor fsd = (tp_udf_FileSetDescriptor)sector;
154 if (partition_number == fsd->RootDirectoryICB.Location.PartitionNumber)
156 parent_icb = fsd->RootDirectoryICB.Location.Location;
157 res = aspi_ReadSectors(hDrive, partition_lba + parent_icb, 1, sec_size, sector);
158 if (res && tag->TagIdentifier == udf_TAG_FileEntry)
160 tp_udf_FileEntry fe = (tp_udf_FileEntry)sector;
162 if (fe->ICBTag.FileType == udf_FT_Directory)
164 tp_udf_file root = (tp_udf_file)malloc(sizeof *root);
166 root->partition_lba = partition_lba;
167 udf_GetLBA(fe, sec_size, &root->dir_lba, &root->dir_end_lba);
168 root->dir_left = (DWORD)fe->InformationLength; // don't want directories of more than 4gb
169 root->sector = NULL;
170 root->fid = NULL;
171 root->sec_size = sec_size;
172 strcpy(root->name, "/");
173 root->is_dir = true;
174 root->is_parent = false;
175 return root;
182 return NULL;
185 static void udf_GetName(const BYTE *data, const DWORD len, char *target)
187 DWORD p = 1, i = 0;
189 if (len == 0 || !(data[0] & 0x18))
190 target[0] = '\0';
192 if (data[0] & 0x10)
193 { // ignore MSB of unicode16
194 p++;
196 while (p < len)
197 target[i++] = data[p += 2];
199 else
201 while (p < len)
202 target[i++] = data[p++];
205 target[i]='\0';
208 tp_udf_file udf_get_sub(const HANDLE hDrive, tp_udf_file f)
210 if (f->is_dir && !f->is_parent && f->fid)
212 BYTE sector[fio_SECTOR_SIZE];
213 tp_udf_tag tag = (tp_udf_tag)sector;
214 bool res;
216 res = aspi_ReadSectors(hDrive, f->partition_lba + f->fid->ICB.Location.Location, 1, f->sec_size, sector);
217 if (res && tag->TagIdentifier == udf_TAG_FileEntry)
219 tp_udf_FileEntry fe = (tp_udf_FileEntry)sector;
221 if (fe->ICBTag.FileType == udf_FT_Directory)
223 tp_udf_file newf = (tp_udf_file)malloc(sizeof *newf);
225 strcpy(newf->name, f->name); // maybe just ""?
226 newf->sec_size = f->sec_size;
227 newf->partition_lba = f->partition_lba;
228 udf_GetLBA(fe, f->sec_size, &newf->dir_lba, &newf->dir_end_lba);
229 newf->dir_left = (DWORD)fe->InformationLength; // don't want directories of more than 4gb
230 newf->sector = NULL;
231 newf->fid = NULL;
232 newf->is_dir = true;
233 newf->is_parent = false;
234 return newf;
238 return NULL;
241 tp_udf_file udf_get_next(const HANDLE hDrive, tp_udf_file f)
243 bool res = true;
245 if (f->dir_left <= 0)
247 f->fid = NULL;
248 return NULL;
251 if (f->fid)
252 { // advance to next FileIdentifierDescriptor
253 DWORD ofs = 4 * ((sizeof *(f->fid) + f->fid->LengthofImplementationUse + f->fid->LengthofFileIdentifier + 3) / 4);
255 f->fid = (tp_udf_FileIdentifierDescriptor)((BYTE *)f->fid + ofs);
258 if (f->fid == NULL)
260 DWORD size = f->sec_size * (f->dir_end_lba - f->dir_lba + 1);
262 if (!f->sector)
263 f->sector = (BYTE*)malloc(size);
264 res = aspi_ReadSectors(hDrive, f->partition_lba + f->dir_lba, (WORD)(f->dir_end_lba - f->dir_lba + 1), f->sec_size, f->sector);
265 if (res)
266 f->fid = (tp_udf_FileIdentifierDescriptor)f->sector;
267 else
268 f->fid = NULL;
271 if (f->fid && f->fid->DescriptorTag.TagIdentifier == udf_TAG_FileIdentifierDescriptor)
273 DWORD ofs = 4 * ((sizeof *f->fid + f->fid->LengthofImplementationUse + f->fid->LengthofFileIdentifier + 3) / 4);
275 f->dir_left -= ofs;
276 f->is_dir = (f->fid->FileCharacteristics & udf_FID_Directory) != 0;
277 f->is_parent = (f->fid->FileCharacteristics & udf_FID_Parent) != 0;
278 udf_GetName(f->fid->ImplementationUse + f->fid->LengthofImplementationUse, f->fid->LengthofFileIdentifier, f->name);
279 return f;
281 return NULL;
284 void udf_free(tp_udf_file f)
286 if (f)
288 if (f->sector)
289 free(f->sector);
290 free(f);
294 #define udf_PATH_DELIMITERS "/\\"
296 static tp_udf_file udf_ff_traverse(const HANDLE hDrive, tp_udf_file f, char *token)
298 while (udf_get_next(hDrive, f))
300 if (stricmp(token, f->name) == 0)
302 char *next_tok = strtok(NULL, udf_PATH_DELIMITERS);
304 if (!next_tok)
305 return f; // found
306 else if (f->is_dir)
308 tp_udf_file f2 = udf_get_sub(hDrive, f);
310 if (f2)
312 tp_udf_file f3 = udf_ff_traverse(hDrive, f2, next_tok);
314 if (!f3)
315 udf_free(f2);
316 return f3;
321 return NULL;
324 tp_udf_file udf_find_file(const HANDLE hDrive, const WORD partition, const char *name)
326 tp_udf_file f = udf_get_root(hDrive, partition), f2 = NULL;
328 if (f)
330 char tokenline[udf_MAX_PATHLEN];
331 char *token;
333 strcpy(tokenline, name);
334 token = strtok(tokenline, udf_PATH_DELIMITERS);
335 if (token)
336 f2 = udf_ff_traverse(hDrive, f, token);
337 udf_free(f);
339 return f2;
342 bool udf_get_lba(const HANDLE hDrive, const tp_udf_file f, DWORD *start_lba, DWORD *end_lba)
344 if (f->fid)
346 BYTE sector[2048];
347 tp_udf_FileEntry fe = (tp_udf_FileEntry)sector;
348 bool res;
350 res = aspi_ReadSectors(hDrive, f->partition_lba + f->fid->ICB.Location.Location, 1, f->sec_size, sector);
351 if (res && fe->DescriptorTag.TagIdentifier == udf_TAG_FileEntry)
352 return udf_GetLBA(fe, f->sec_size, start_lba, end_lba);
354 return false;