VirtualBox

source: vbox/trunk/src/VBox/Storage/CUE.cpp@ 82829

Last change on this file since 82829 was 79965, checked in by vboxsync, 5 years ago

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.5 KB
Line 
1/* $Id: CUE.cpp 79965 2019-07-24 20:32:32Z vboxsync $ */
2/** @file
3 * CUE - CUE/BIN Disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2017-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VD_CUE
23#include <VBox/vd-plugin.h>
24#include <VBox/err.h>
25
26#include <VBox/log.h>
27#include <VBox/scsiinline.h>
28#include <iprt/assert.h>
29#include <iprt/asm.h>
30#include <iprt/alloc.h>
31#include <iprt/cdefs.h>
32#include <iprt/ctype.h>
33#include <iprt/path.h>
34#include <iprt/string.h>
35
36#include "VDBackends.h"
37#include "VDBackendsInline.h"
38
39
40/*********************************************************************************************************************************
41* Constants And Macros, Structures and Typedefs *
42*********************************************************************************************************************************/
43
44/**
45 * CUE descriptor file token type.
46 */
47typedef enum CUETOKENTYPE
48{
49 /** Invalid token type. */
50 CUETOKENTYPE_INVALID = 0,
51 /** Reserved keyword. */
52 CUETOKENTYPE_KEYWORD,
53 /** String token. */
54 CUETOKENTYPE_STRING,
55 /** Unsigned integer. */
56 CUETOKENTYPE_INTEGER_UNSIGNED,
57 /** MSF (mm:ss:ff) location token. */
58 CUETOKENTYPE_MSF,
59 /** Error token (unexpected character found). */
60 CUETOKENTYPE_ERROR,
61 /** End of stream token. */
62 CUETOKENTYPE_EOS
63} CUETOKENTYPE;
64
65/**
66 * CUE reservered keyword type.
67 */
68typedef enum CUEKEYWORD
69{
70 /** Invalid keyword. */
71 CUEKEYWORD_INVALID = 0,
72 /** FILE. */
73 CUEKEYWORD_FILE,
74 /** BINARY */
75 CUEKEYWORD_BINARY,
76 /** MOTOROLA */
77 CUEKEYWORD_MOTOROLA,
78 /** WAVE */
79 CUEKEYWORD_WAVE,
80 /** MP3 */
81 CUEKEYWORD_MP3,
82 /** AIFF */
83 CUEKEYWORD_AIFF,
84 /** CATALOG */
85 CUEKEYWORD_CATALOG,
86 CUEKEYWORD_CDTEXTFILE,
87 CUEKEYWORD_FLAGS,
88 CUEKEYWORD_INDEX,
89 CUEKEYWORD_ISRC,
90 CUEKEYWORD_PERFORMER,
91 CUEKEYWORD_POSTGAP,
92 CUEKEYWORD_PREGAP,
93 CUEKEYWORD_SONGWRITER,
94 CUEKEYWORD_TITLE,
95 CUEKEYWORD_TRACK,
96 CUEKEYWORD_MODE1_2048,
97 CUEKEYWORD_MODE1_2352,
98 CUEKEYWORD_AUDIO,
99 CUEKEYWORD_REM
100} CUEKEYWORD;
101
102/**
103 * CUE sheet token.
104 */
105typedef struct CUETOKEN
106{
107 /** The token type. */
108 CUETOKENTYPE enmType;
109 /** Token type dependent data. */
110 union
111 {
112 /** Keyword token. */
113 struct
114 {
115 /** The keyword enumerator. */
116 CUEKEYWORD enmKeyword;
117 } Keyword;
118 /** String token (without quotation marks). */
119 struct
120 {
121 /** Pointer to the start of the string. */
122 const char *psz;
123 /** Number of characters for the string excluding the null terminator. */
124 size_t cch;
125 } String;
126 /** Integer token. */
127 struct
128 {
129 /** Numerical constant. */
130 uint64_t u64;
131 } Int;
132 /** MSF location token. */
133 struct
134 {
135 /** Minute part. */
136 uint8_t u8Minute;
137 /** Second part. */
138 uint8_t u8Second;
139 /** Frame part. */
140 uint8_t u8Frame;
141 } Msf;
142 } Type;
143} CUETOKEN;
144/** Pointer to a CUE sheet token. */
145typedef CUETOKEN *PCUETOKEN;
146/** Pointer to a const CUE sheet token. */
147typedef const CUETOKEN *PCCUETOKEN;
148
149/**
150 * CUE tokenizer state.
151 */
152typedef struct CUETOKENIZER
153{
154 /** Char buffer to read from. */
155 const char *pszInput;
156 /** Token 1. */
157 CUETOKEN Token1;
158 /** Token 2. */
159 CUETOKEN Token2;
160 /** Pointer to the current active token. */
161 PCUETOKEN pTokenCurr;
162 /** The next token in the input stream (used for peeking). */
163 PCUETOKEN pTokenNext;
164} CUETOKENIZER;
165/** Pointer to a CUE tokenizer state. */
166typedef CUETOKENIZER *PCUETOKENIZER;
167
168/**
169 * CUE keyword entry.
170 */
171typedef struct CUEKEYWORDDESC
172{
173 /** Keyword string. */
174 const char *pszKeyword;
175 /** Size of the string in characters without zero terminator. */
176 size_t cchKeyword;
177 /** Keyword type. */
178 CUEKEYWORD enmKeyword;
179} CUEKEYWORDDESC;
180/** Pointer to a CUE keyword entry. */
181typedef CUEKEYWORDDESC *PCUEKEYWORDDESC;
182/** Pointer to a const CUE keyword entry. */
183typedef const CUEKEYWORDDESC *PCCUEKEYWORDDESC;
184
185/**
186 * CUE image data structure.
187 */
188typedef struct CUEIMAGE
189{
190 /** Image name. */
191 const char *pszFilename;
192 /** Storage handle. */
193 PVDIOSTORAGE pStorage;
194 /** The backing file containing the actual data. */
195 char *pszDataFilename;
196 /** Storage handle for the backing file. */
197 PVDIOSTORAGE pStorageData;
198
199 /** Pointer to the per-disk VD interface list. */
200 PVDINTERFACE pVDIfsDisk;
201 /** Pointer to the per-image VD interface list. */
202 PVDINTERFACE pVDIfsImage;
203 /** Error interface. */
204 PVDINTERFACEERROR pIfError;
205 /** I/O interface. */
206 PVDINTERFACEIOINT pIfIo;
207
208 /** Open flags passed by VD layer. */
209 unsigned uOpenFlags;
210 /** Image flags defined during creation or determined during open. */
211 unsigned uImageFlags;
212 /** Maximum number of tracks the region list can hold. */
213 uint32_t cTracksMax;
214 /** Pointer to our internal region list. */
215 PVDREGIONLIST pRegionList;
216 /** Flag whether the backing file is little (BINARY) or big (MOTOROLA) endian. */
217 bool fLittleEndian;
218} CUEIMAGE, *PCUEIMAGE;
219
220
221/*********************************************************************************************************************************
222* Static Variables *
223*********************************************************************************************************************************/
224
225/** NULL-terminated array of supported file extensions. */
226static const VDFILEEXTENSION s_aCueFileExtensions[] =
227{
228 {"cue", VDTYPE_OPTICAL_DISC},
229 {NULL, VDTYPE_INVALID}
230};
231
232/**
233 * Known keywords.
234 */
235static const CUEKEYWORDDESC g_aCueKeywords[] =
236{
237 {RT_STR_TUPLE("FILE"), CUEKEYWORD_FILE},
238 {RT_STR_TUPLE("BINARY"), CUEKEYWORD_BINARY},
239 {RT_STR_TUPLE("WAVE"), CUEKEYWORD_WAVE},
240 {RT_STR_TUPLE("MP3"), CUEKEYWORD_MP3},
241 {RT_STR_TUPLE("AIFF"), CUEKEYWORD_AIFF},
242 {RT_STR_TUPLE("CATALOG"), CUEKEYWORD_CATALOG},
243 {RT_STR_TUPLE("CDTEXTFILE"), CUEKEYWORD_CDTEXTFILE},
244 {RT_STR_TUPLE("FLAGS"), CUEKEYWORD_FLAGS},
245 {RT_STR_TUPLE("INDEX"), CUEKEYWORD_INDEX},
246 {RT_STR_TUPLE("ISRC"), CUEKEYWORD_ISRC},
247 {RT_STR_TUPLE("PERFORMER"), CUEKEYWORD_PERFORMER},
248 {RT_STR_TUPLE("POSTGAP"), CUEKEYWORD_POSTGAP},
249 {RT_STR_TUPLE("PREGAP"), CUEKEYWORD_PREGAP},
250 {RT_STR_TUPLE("SONGWRITER"), CUEKEYWORD_SONGWRITER},
251 {RT_STR_TUPLE("TITLE"), CUEKEYWORD_TITLE},
252 {RT_STR_TUPLE("TRACK"), CUEKEYWORD_TRACK},
253 {RT_STR_TUPLE("MODE1/2048"), CUEKEYWORD_MODE1_2048},
254 {RT_STR_TUPLE("MODE1/2352"), CUEKEYWORD_MODE1_2352},
255 {RT_STR_TUPLE("AUDIO"), CUEKEYWORD_AUDIO},
256 {RT_STR_TUPLE("REM"), CUEKEYWORD_REM}
257};
258
259
260/*********************************************************************************************************************************
261* Internal Functions *
262*********************************************************************************************************************************/
263
264
265/**
266 * Converts a MSF formatted address value read from the given buffer
267 * to an LBA number. MSF 00:00:00 equals LBA 0.
268 *
269 * @returns The LBA number.
270 * @param pbBuf The buffer to read the MSF formatted address
271 * from.
272 */
273DECLINLINE(uint32_t) cueMSF2LBA(const uint8_t *pbBuf)
274{
275 return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2];
276}
277
278/**
279 * Ensures that the region list can hold up to the given number of tracks.
280 *
281 * @returns VBox status code.
282 * @param pThis The CUE image state.
283 * @param cTracksMax Maximum number of tracks.
284 */
285static int cueEnsureRegionListSize(PCUEIMAGE pThis, uint32_t cTracksMax)
286{
287 int rc = VINF_SUCCESS;
288
289 if (pThis->cTracksMax < cTracksMax)
290 {
291 PVDREGIONLIST pRegionListNew = (PVDREGIONLIST)RTMemRealloc(pThis->pRegionList,
292 RT_UOFFSETOF_DYN(VDREGIONLIST, aRegions[cTracksMax]));
293 if (pRegionListNew)
294 {
295 /* Init all the new allocated tracks. */
296 for (uint32_t i = pThis->cTracksMax; i < cTracksMax; i++)
297 pRegionListNew->aRegions[i].offRegion = UINT64_MAX;
298 pThis->pRegionList = pRegionListNew;
299 pThis->cTracksMax = cTracksMax;
300 }
301 else
302 rc = VERR_NO_MEMORY;
303 }
304
305 return rc;
306}
307
308/**
309 * Returns whether the tokenizer reached the end of the stream.
310 *
311 * @returns true if the tokenizer reached the end of stream marker
312 * false otherwise.
313 * @param pTokenizer The tokenizer state.
314 */
315DECLINLINE(bool) cueTokenizerIsEos(PCUETOKENIZER pTokenizer)
316{
317 return *pTokenizer->pszInput == '\0';
318}
319
320/**
321 * Skip one character in the input stream.
322 *
323 * @returns nothing.
324 * @param pTokenizer The tokenizer state.
325 */
326DECLINLINE(void) cueTokenizerSkipCh(PCUETOKENIZER pTokenizer)
327{
328 /* Never ever go past EOS. */
329 if (!cueTokenizerIsEos(pTokenizer))
330 pTokenizer->pszInput++;
331}
332
333/**
334 * Returns the next char in the input buffer without advancing it.
335 *
336 * @returns Next character in the input buffer.
337 * @param pTokenizer The tokenizer state.
338 */
339DECLINLINE(char) cueTokenizerPeekCh(PCUETOKENIZER pTokenizer)
340{
341 return cueTokenizerIsEos(pTokenizer)
342 ? '\0'
343 : *(pTokenizer->pszInput + 1);
344}
345
346/**
347 * Returns the next character in the input buffer advancing the internal
348 * position.
349 *
350 * @returns Next character in the stream.
351 * @param pTokenizer The tokenizer state.
352 */
353DECLINLINE(char) cueTokenizerGetCh(PCUETOKENIZER pTokenizer)
354{
355 char ch;
356
357 if (cueTokenizerIsEos(pTokenizer))
358 ch = '\0';
359 else
360 ch = *pTokenizer->pszInput;
361
362 return ch;
363}
364
365/**
366 * Sets a new line for the tokenizer.
367 *
368 * @returns nothing.
369 * @param pTokenizer The tokenizer state.
370 * @param cSkip How many characters to skip.
371 */
372DECLINLINE(void) cueTokenizerNewLine(PCUETOKENIZER pTokenizer, unsigned cSkip)
373{
374 pTokenizer->pszInput += cSkip;
375}
376
377/**
378 * Checks whether the current position in the input stream is a new line
379 * and skips it.
380 *
381 * @returns Flag whether there was a new line at the current position
382 * in the input buffer.
383 * @param pTokenizer The tokenizer state.
384 */
385DECLINLINE(bool) cueTokenizerIsSkipNewLine(PCUETOKENIZER pTokenizer)
386{
387 bool fNewline = true;
388
389 if ( cueTokenizerGetCh(pTokenizer) == '\r'
390 && cueTokenizerPeekCh(pTokenizer) == '\n')
391 cueTokenizerNewLine(pTokenizer, 2);
392 else if (cueTokenizerGetCh(pTokenizer) == '\n')
393 cueTokenizerNewLine(pTokenizer, 1);
394 else
395 fNewline = false;
396
397 return fNewline;
398}
399
400/**
401 * Skip all whitespace starting from the current input buffer position.
402 * Skips all present comments too.
403 *
404 * @returns nothing.
405 * @param pTokenizer The tokenizer state.
406 */
407DECLINLINE(void) cueTokenizerSkipWhitespace(PCUETOKENIZER pTokenizer)
408{
409 while (!cueTokenizerIsEos(pTokenizer))
410 {
411 while ( cueTokenizerGetCh(pTokenizer) == ' '
412 || cueTokenizerGetCh(pTokenizer) == '\t')
413 cueTokenizerSkipCh(pTokenizer);
414
415 if ( !cueTokenizerIsEos(pTokenizer)
416 && !cueTokenizerIsSkipNewLine(pTokenizer))
417 break; /* Skipped everything, next is some real content. */
418 }
419}
420
421/**
422 * Skips a multi line comment.
423 *
424 * @returns nothing.
425 * @param pTokenizer The tokenizer state.
426 */
427DECLINLINE(void) cueTokenizerSkipComment(PCUETOKENIZER pTokenizer)
428{
429 while ( !cueTokenizerIsEos(pTokenizer)
430 && !cueTokenizerIsSkipNewLine(pTokenizer))
431 cueTokenizerSkipCh(pTokenizer);
432 cueTokenizerSkipWhitespace(pTokenizer);
433}
434
435/**
436 * Get an identifier token from the tokenizer.
437 *
438 * @returns nothing.
439 * @param pTokenizer The tokenizer state.
440 * @param pToken The uninitialized token.
441 */
442static void cueTokenizerGetKeyword(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
443{
444 char ch;
445 unsigned cchKeyword = 0;
446 bool fIsKeyword = false;
447 bool fIsComment;
448 const char *pszKeyword;
449
450 Assert(RT_C_IS_ALPHA(*pTokenizer->pszInput));
451
452 do
453 {
454 fIsComment = false;
455 pszKeyword = pTokenizer->pszInput;
456
457 do
458 {
459 cchKeyword++;
460 cueTokenizerSkipCh(pTokenizer);
461 ch = cueTokenizerGetCh(pTokenizer);
462 }
463 while (RT_C_IS_ALNUM(ch) || ch == '_' || ch == '/' || ch == '.');
464
465 /* Check whether we got a keyword or a string constant. */
466 for (unsigned i = 0; i < RT_ELEMENTS(g_aCueKeywords); i++)
467 {
468 if (!RTStrNCmp(g_aCueKeywords[i].pszKeyword, pszKeyword, RT_MIN(cchKeyword, g_aCueKeywords[i].cchKeyword)))
469 {
470 if (g_aCueKeywords[i].enmKeyword == CUEKEYWORD_REM)
471 {
472 /* The REM keyword is handled here as it indicates a comment which we just skip. */
473 cueTokenizerSkipComment(pTokenizer);
474 fIsComment = true;
475 }
476 else
477 {
478 fIsKeyword = true;
479 pToken->enmType = CUETOKENTYPE_KEYWORD;
480 pToken->Type.Keyword.enmKeyword = g_aCueKeywords[i].enmKeyword;
481 }
482 break;
483 }
484 }
485 } while (fIsComment);
486
487 /* Make it a string. */
488 if (ch == '\0')
489 pToken->enmType = CUETOKENTYPE_EOS;
490 else if (!fIsKeyword)
491 {
492 pToken->enmType = CUETOKENTYPE_STRING;
493 pToken->Type.String.psz = pszKeyword;
494 pToken->Type.String.cch = cchKeyword;
495 }
496}
497
498/**
499 * Get an integer value or MSF location indicator from the tokenizer.
500 *
501 * @returns nothing.
502 * @param pTokenizer The tokenizer state.
503 * @param pToken The uninitialized token.
504 */
505static void cueTokenizerGetIntegerOrMsf(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
506{
507 char szInt[20 + 1]; /* Maximum which fits into an unsigned 64bit integer + zero terminator. */
508 unsigned cchInt = 0;
509 bool fMsf = false;
510 char ch = cueTokenizerGetCh(pTokenizer);
511
512 Assert(RT_C_IS_DIGIT(ch));
513 RT_ZERO(szInt);
514
515 /* Go through the characters and check for the : mark which denotes MSF location indicator. */
516 do
517 {
518 szInt[cchInt++] = ch;
519 cueTokenizerSkipCh(pTokenizer);
520 ch = cueTokenizerGetCh(pTokenizer);
521 if (ch == ':')
522 fMsf = true;
523 }
524 while ( (RT_C_IS_DIGIT(ch) || ch == ':')
525 && cchInt < sizeof(szInt));
526
527 if (cchInt < sizeof(szInt) - 1)
528 {
529 if (fMsf)
530 {
531 /* Check that the format matches our expectations (mm:ss:ff). */
532 if ( cchInt == 8 && szInt[2] == ':' && szInt[5] == ':')
533 {
534 /* Parse the single fields. */
535 szInt[2] = '\0';
536 szInt[5] = '\0';
537
538 int rc = RTStrToUInt8Full(&szInt[0], 10, &pToken->Type.Msf.u8Minute);
539 if (RT_SUCCESS(rc))
540 rc = RTStrToUInt8Full(&szInt[3], 10, &pToken->Type.Msf.u8Second);
541 if (RT_SUCCESS(rc))
542 rc = RTStrToUInt8Full(&szInt[6], 10, &pToken->Type.Msf.u8Frame);
543 if (RT_SUCCESS(rc))
544 pToken->enmType = CUETOKENTYPE_MSF;
545 else
546 pToken->enmType = CUETOKENTYPE_ERROR;
547 }
548 else
549 pToken->enmType = CUETOKENTYPE_ERROR;
550 }
551 else
552 {
553 pToken->enmType = CUETOKENTYPE_INTEGER_UNSIGNED;
554 int rc = RTStrToUInt64Full(&szInt[0], 10, &pToken->Type.Int.u64);
555 if (RT_FAILURE(rc))
556 pToken->enmType = CUETOKENTYPE_ERROR;
557 }
558 }
559 else
560 pToken->enmType = CUETOKENTYPE_ERROR;
561}
562
563/**
564 * Parses a string constant.
565 *
566 * @returns nothing.
567 * @param pTokenizer The tokenizer state.
568 * @param pToken The uninitialized token.
569 *
570 * @remarks: No escape sequences allowed at this time.
571 */
572static void cueTokenizerGetStringConst(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
573{
574 unsigned cchStr = 0;
575
576 Assert(cueTokenizerGetCh(pTokenizer) == '\"');
577 cueTokenizerSkipCh(pTokenizer); /* Skip " */
578
579 pToken->enmType = CUETOKENTYPE_STRING;
580 pToken->Type.String.psz = pTokenizer->pszInput;
581
582 while ( !cueTokenizerIsEos(pTokenizer)
583 && cueTokenizerGetCh(pTokenizer) != '\"')
584 {
585 cchStr++;
586 cueTokenizerSkipCh(pTokenizer);
587 }
588
589 /* End of stream without a closing quote is an error. */
590 if (RT_UNLIKELY(cueTokenizerIsEos(pTokenizer)))
591 pToken->enmType = CUETOKENTYPE_ERROR;
592 else
593 {
594 cueTokenizerSkipCh(pTokenizer); /* Skip closing " */
595 pToken->Type.String.cch = cchStr;
596 }
597}
598
599/**
600 * Get the end of stream token.
601 *
602 * @returns nothing.
603 * @param pTokenizer The tokenizer state.
604 * @param pToken The uninitialized token.
605 */
606static void cueTokenizerGetEos(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
607{
608 Assert(cueTokenizerGetCh(pTokenizer) == '\0'); RT_NOREF(pTokenizer);
609
610 pToken->enmType = CUETOKENTYPE_EOS;
611}
612
613/**
614 * Read the next token from the tokenizer stream.
615 *
616 * @returns nothing.
617 * @param pTokenizer The tokenizer to read from.
618 * @param pToken Uninitialized token to fill the token data into.
619 */
620static void cueTokenizerReadNextToken(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
621{
622 /* Skip all eventually existing whitespace, newlines and comments first. */
623 cueTokenizerSkipWhitespace(pTokenizer);
624
625 char ch = cueTokenizerGetCh(pTokenizer);
626 if (RT_C_IS_ALPHA(ch))
627 cueTokenizerGetKeyword(pTokenizer, pToken);
628 else if (RT_C_IS_DIGIT(ch))
629 cueTokenizerGetIntegerOrMsf(pTokenizer, pToken);
630 else if (ch == '\"')
631 cueTokenizerGetStringConst(pTokenizer, pToken);
632 else if (ch == '\0')
633 cueTokenizerGetEos(pTokenizer, pToken);
634 else
635 pToken->enmType = CUETOKENTYPE_ERROR;
636}
637
638/**
639 * Create a new tokenizer.
640 *
641 * @returns Pointer to the new tokenizer state on success.
642 * NULL if out of memory.
643 * @param pszInput The input to create the tokenizer for.
644 */
645static PCUETOKENIZER cueTokenizerCreate(const char *pszInput)
646{
647 PCUETOKENIZER pTokenizer = (PCUETOKENIZER)RTMemAllocZ(sizeof(CUETOKENIZER));
648 if (pTokenizer)
649 {
650 pTokenizer->pszInput = pszInput;
651 pTokenizer->pTokenCurr = &pTokenizer->Token1;
652 pTokenizer->pTokenNext = &pTokenizer->Token2;
653 /* Fill the tokenizer with two first tokens. */
654 cueTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenCurr);
655 cueTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
656 }
657
658 return pTokenizer;
659}
660
661/**
662 * Get the current token in the input stream.
663 *
664 * @returns Pointer to the next token in the stream.
665 * @param pTokenizer The tokenizer to destroy.
666 */
667DECLINLINE(PCCUETOKEN) cueTokenizerGetToken(PCUETOKENIZER pTokenizer)
668{
669 return pTokenizer->pTokenCurr;
670}
671
672/**
673 * Get the class of the current token.
674 *
675 * @returns Class of the current token.
676 * @param pTokenizer The tokenizer state.
677 */
678DECLINLINE(CUETOKENTYPE) cueTokenizerGetTokenType(PCUETOKENIZER pTokenizer)
679{
680 return pTokenizer->pTokenCurr->enmType;
681}
682
683/**
684 * Consume the current token advancing to the next in the stream.
685 *
686 * @returns nothing.
687 * @param pTokenizer The tokenizer state.
688 */
689static void cueTokenizerConsume(PCUETOKENIZER pTokenizer)
690{
691 PCUETOKEN pTokenTmp = pTokenizer->pTokenCurr;
692
693 /* Switch next token to current token and read in the next token. */
694 pTokenizer->pTokenCurr = pTokenizer->pTokenNext;
695 pTokenizer->pTokenNext = pTokenTmp;
696 cueTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
697}
698
699/**
700 * Check whether the next token in the input stream is a keyword and matches the given
701 * keyword.
702 *
703 * @returns true if the token matched.
704 * false otherwise.
705 * @param pTokenizer The tokenizer state.
706 * @param enmKeyword The keyword to check against.
707 */
708static bool cueTokenizerIsKeywordEqual(PCUETOKENIZER pTokenizer, CUEKEYWORD enmKeyword)
709{
710 PCCUETOKEN pToken = cueTokenizerGetToken(pTokenizer);
711
712 if ( pToken->enmType == CUETOKENTYPE_KEYWORD
713 && pToken->Type.Keyword.enmKeyword == enmKeyword)
714 return true;
715
716 return false;
717}
718
719/**
720 * Check whether the next token in the input stream is a keyword and matches the given
721 * keyword and skips it.
722 *
723 * @returns true if the token matched and was skipped.
724 * false otherwise.
725 * @param pTokenizer The tokenizer state.
726 * @param enmKeyword The keyword to check against.
727 */
728static bool cueTokenizerSkipIfIsKeywordEqual(PCUETOKENIZER pTokenizer, CUEKEYWORD enmKeyword)
729{
730 bool fEqual = cueTokenizerIsKeywordEqual(pTokenizer, enmKeyword);
731 if (fEqual)
732 cueTokenizerConsume(pTokenizer);
733
734 return fEqual;
735}
736
737/**
738 * Duplicates the string of the current token and consumes it.
739 *
740 * @returns VBox status code.
741 * @param pTokenizer The tokenizer state.
742 * @param ppszStr Where to store the pointer to the duplicated string on success.
743 * Free with RTStrFree().
744 */
745static int cueTokenizerConsumeStringDup(PCUETOKENIZER pTokenizer, char **ppszStr)
746{
747 int rc = VINF_SUCCESS;
748 Assert(cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_STRING);
749
750 *ppszStr = RTStrDupN(pTokenizer->pTokenCurr->Type.String.psz,
751 pTokenizer->pTokenCurr->Type.String.cch);
752 if (!*ppszStr)
753 rc = VERR_NO_STR_MEMORY;
754
755 cueTokenizerConsume(pTokenizer);
756 return rc;
757}
758
759/**
760 * Consumes an integer token returning the value.
761 *
762 * @returns Integer value in the token.
763 * @param pTokenizer The tokenizer state.
764 */
765static uint64_t cueTokenizerConsumeInteger(PCUETOKENIZER pTokenizer)
766{
767 uint64_t u64 = 0;
768 Assert(cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_INTEGER_UNSIGNED);
769
770 u64 = pTokenizer->pTokenCurr->Type.Int.u64;
771 cueTokenizerConsume(pTokenizer);
772 return u64;
773}
774
775/**
776 * Parses and skips the remaining string part of a directive.
777 *
778 * @returns VBox status code.
779 * @param pThis The CUE image state.
780 * @param pTokenizer The tokenizer state.
781 * @param pszDirective The directive we skip the string part for.
782 */
783static int cueParseAndSkipStringRemainder(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer,
784 const char *pszDirective)
785{
786 int rc = VINF_SUCCESS;
787
788 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_STRING)
789 cueTokenizerConsume(pTokenizer);
790 else
791 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
792 N_("CUE: Error parsing '%s', expected string for %s directive"), pThis->pszFilename,
793 pszDirective);
794
795 return rc;
796}
797
798/**
799 * Parses and skips the remaining MSF part of a directive.
800 *
801 * @returns VBox status code.
802 * @param pThis The CUE image state.
803 * @param pTokenizer The tokenizer state.
804 * @param pszDirective The directive we skip the string part for.
805 */
806static int cueParseAndSkipMsfRemainder(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer,
807 const char *pszDirective)
808{
809 int rc = VINF_SUCCESS;
810
811 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_MSF)
812 cueTokenizerConsume(pTokenizer);
813 else
814 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
815 N_("CUE: Error parsing '%s', expected MSF location for %s directive"), pThis->pszFilename,
816 pszDirective);
817
818 return rc;
819}
820
821/**
822 * Parses the remainder of a INDEX directive.
823 *
824 * @returns VBox status code.
825 * @param pThis The CUE image state.
826 * @param pTokenizer The tokenizer state.
827 * @param pu8Index Where to store the parsed index number on success.
828 * @param pu64Lba Where to store the parsed positional information on success.
829 */
830static int cueParseIndex(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer,
831 uint8_t *pu8Index, uint64_t *pu64Lba)
832{
833 int rc = VINF_SUCCESS;
834
835 /*
836 * The index consists of the index number and positional information in MSF format.
837 */
838 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_INTEGER_UNSIGNED)
839 {
840 uint64_t u64Index = cueTokenizerConsumeInteger(pTokenizer);
841 if (u64Index <= 99)
842 {
843 /* Parse the position. */
844 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_MSF)
845 {
846 PCCUETOKEN pToken = cueTokenizerGetToken(pTokenizer);
847 uint8_t abMsf[3];
848 abMsf[0] = pToken->Type.Msf.u8Minute;
849 abMsf[1] = pToken->Type.Msf.u8Second;
850 abMsf[2] = pToken->Type.Msf.u8Frame;
851
852 *pu8Index = (uint8_t)u64Index;
853 *pu64Lba = cueMSF2LBA(&abMsf[0]);
854 cueTokenizerConsume(pTokenizer);
855 }
856 else
857 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
858 N_("CUE: Error parsing '%s', expected MSF location"), pThis->pszFilename);
859 }
860 else
861 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
862 N_("CUE: Error parsing '%s', index number must be between 01 and 99"), pThis->pszFilename);
863 }
864 else
865 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
866 N_("CUE: Error parsing '%s', expected index number after INDEX directive"), pThis->pszFilename);
867
868 return rc;
869}
870
871/**
872 * Parses the things coming below a TRACK directive.
873 *
874 * @returns VBox status code.
875 * @param pThis The CUE image state.
876 * @param pTokenizer The tokenizer state.
877 * @param pu64LbaStart Where to store the starting LBA for this track on success.
878 */
879static int cueParseTrackNesting(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer, uint64_t *pu64LbaStart)
880{
881 int rc = VINF_SUCCESS;
882 bool fSeenInitialIndex = false;
883
884 do
885 {
886 if ( cueTokenizerIsKeywordEqual(pTokenizer, CUEKEYWORD_TRACK)
887 || cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_EOS)
888 break;
889
890 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_KEYWORD)
891 {
892 if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_TITLE))
893 rc = cueParseAndSkipStringRemainder(pThis, pTokenizer, "TITLE");
894 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_PERFORMER))
895 rc = cueParseAndSkipStringRemainder(pThis, pTokenizer, "PERFORMER");
896 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_PREGAP))
897 rc = cueParseAndSkipMsfRemainder(pThis, pTokenizer, "PREGAP");
898 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_POSTGAP))
899 rc = cueParseAndSkipMsfRemainder(pThis, pTokenizer, "POSTGAP");
900 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_INDEX))
901 {
902 uint8_t u8Index = 0;
903 uint64_t u64Lba = 0;
904 rc = cueParseIndex(pThis, pTokenizer, &u8Index, &u64Lba);
905 if ( RT_SUCCESS(rc)
906 && u8Index == 1)
907 {
908 if (!fSeenInitialIndex)
909 {
910 fSeenInitialIndex = true;
911 *pu64LbaStart = u64Lba;
912 }
913 else
914 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
915 N_("CUE: Error parsing '%s', multiple INDEX 01 directives"), pThis->pszFilename);
916 }
917 }
918 else
919 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
920 N_("CUE: Error parsing '%s', unexpected directive for TRACK found"), pThis->pszFilename);
921 }
922 else
923 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
924 N_("CUE: Error parsing '%s', expected a CUE sheet keyword"), pThis->pszFilename);
925 }
926 while (RT_SUCCESS(rc));
927
928 if ( RT_SUCCESS(rc)
929 && !fSeenInitialIndex)
930 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
931 N_("CUE: Error parsing '%s', no initial INDEX directive for this track"), pThis->pszFilename);
932
933 return rc;
934}
935
936/**
937 * Parses the remainder of a TRACK directive.
938 *
939 * @returns VBox status code.
940 * @param pThis The CUE image state.
941 * @param pTokenizer The tokenizer state.
942 */
943static int cueParseTrack(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer)
944{
945 int rc = VINF_SUCCESS;
946
947 /*
948 * A track consists of the track number and data type followed by a list of indexes
949 * and other metadata like title and performer we don't care about.
950 */
951 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_INTEGER_UNSIGNED)
952 {
953 uint64_t u64Track = cueTokenizerConsumeInteger(pTokenizer);
954 if (u64Track <= 99)
955 {
956 /* Parse the data mode. */
957 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_KEYWORD)
958 {
959 CUEKEYWORD enmDataMode = pTokenizer->pTokenCurr->Type.Keyword.enmKeyword;
960 if ( cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_AUDIO)
961 || cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_MODE1_2048)
962 || cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_MODE1_2352))
963 {
964 /*
965 * Parse everything coming below the track (index points, etc.), we only need to find
966 * the starting point.
967 */
968 uint64_t uLbaStart = 0;
969 rc = cueParseTrackNesting(pThis, pTokenizer, &uLbaStart);
970 if (RT_SUCCESS(rc))
971 {
972 /* Create a new region for this track. */
973 RT_NOREF1(enmDataMode);
974 rc = cueEnsureRegionListSize(pThis, u64Track);
975 if (RT_SUCCESS(rc))
976 {
977 PVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[u64Track - 1];
978 pRegion->offRegion = uLbaStart;
979 if (enmDataMode == CUEKEYWORD_MODE1_2352)
980 {
981 pRegion->cbBlock = 2352;
982 pRegion->enmDataForm = VDREGIONDATAFORM_MODE1_2352;
983 }
984 else if (enmDataMode == CUEKEYWORD_AUDIO)
985 {
986 pRegion->cbBlock = 2352;
987 pRegion->enmDataForm = VDREGIONDATAFORM_CDDA;
988 }
989 else
990 {
991 pRegion->cbBlock = 2048;
992 pRegion->enmDataForm = VDREGIONDATAFORM_MODE1_2048;
993 }
994 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
995 pRegion->cbData = pRegion->cbBlock;
996 pRegion->cbMetadata = 0;
997 }
998 else
999 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1000 N_("CUE: Failed to allocate memory for the track list for '%s'"),
1001 pThis->pszFilename);
1002 }
1003 }
1004 else
1005 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1006 N_("CUE: Error parsing '%s', the data mode is not supported"), pThis->pszFilename);
1007 }
1008 else
1009 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1010 N_("CUE: Error parsing '%s', expected data mode"), pThis->pszFilename);
1011 }
1012 else
1013 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1014 N_("CUE: Error parsing '%s', track number must be between 01 and 99"), pThis->pszFilename);
1015 }
1016 else
1017 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1018 N_("CUE: Error parsing '%s', expected track number after TRACK directive"), pThis->pszFilename);
1019
1020 return rc;
1021}
1022
1023/**
1024 * Parses a list of tracks which must come after a FILE directive.
1025 *
1026 * @returns VBox status code.
1027 * @param pThis The CUE image state.
1028 * @param pTokenizer The tokenizer state.
1029 */
1030static int cueParseTrackList(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer)
1031{
1032 int rc = VINF_SUCCESS;
1033
1034 while ( RT_SUCCESS(rc)
1035 && cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_TRACK))
1036 rc = cueParseTrack(pThis, pTokenizer);
1037
1038 return rc;
1039}
1040
1041/**
1042 * Parses the remainder of a FILE directive.
1043 *
1044 * @returns VBox status code.
1045 * @param pThis The CUE image state.
1046 * @param pTokenizer The tokenizer state.
1047 */
1048static int cueParseFile(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer)
1049{
1050 int rc = VINF_SUCCESS;
1051
1052 /* First must come a string constant followed by a keyword giving the file type. */
1053 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_STRING)
1054 {
1055 rc = cueTokenizerConsumeStringDup(pTokenizer, &pThis->pszDataFilename);
1056 if (RT_SUCCESS(rc))
1057 {
1058 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_KEYWORD)
1059 {
1060 if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_BINARY))
1061 {
1062 pThis->fLittleEndian = true;
1063 rc = cueParseTrackList(pThis, pTokenizer);
1064 }
1065 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_MOTOROLA))
1066 {
1067 pThis->fLittleEndian = false;
1068 rc = cueParseTrackList(pThis, pTokenizer);
1069 }
1070 else
1071 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1072 N_("CUE: Error parsing '%s', the file type is not supported (only BINARY)"), pThis->pszFilename);
1073 }
1074 else
1075 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1076 N_("CUE: Error parsing '%s', expected file type"), pThis->pszFilename);
1077 }
1078 else
1079 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1080 N_("CUE: Error parsing '%s', failed to allocate memory for filename"), pThis->pszFilename);
1081 }
1082 else
1083 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1084 N_("CUE: Error parsing '%s', expected filename after FILE directive"), pThis->pszFilename);
1085
1086 return rc;
1087}
1088
1089/**
1090 * Parses the keyword in the given tokenizer.
1091 *
1092 * @returns VBox status code.
1093 * @param pThis The CUE image state.
1094 * @param pTokenizer The tokenizer state.
1095 */
1096static int cueParseKeyword(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer)
1097{
1098 int rc = VINF_SUCCESS;
1099
1100 if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_FILE))
1101 rc = cueParseFile(pThis, pTokenizer);
1102 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_TITLE))
1103 rc = cueParseAndSkipStringRemainder(pThis, pTokenizer, "TITLE");
1104 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_PERFORMER))
1105 rc = cueParseAndSkipStringRemainder(pThis, pTokenizer, "PERFORMER");
1106 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_SONGWRITER))
1107 rc = cueParseAndSkipStringRemainder(pThis, pTokenizer, "SONGWRITER");
1108 else /* Skip all other keywords we don't need/support. */
1109 cueTokenizerConsume(pTokenizer);
1110
1111 return rc;
1112}
1113
1114
1115/**
1116 * Parses the CUE sheet from the given tokenizer.
1117 *
1118 * @returns VBox status code.
1119 * @param pThis The CUE image state.
1120 * @param pTokenizer The tokenizer state.
1121 */
1122static int cueParseFromTokenizer(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer)
1123{
1124 int rc = VINF_SUCCESS;
1125
1126 LogFlowFunc(("pThis=%p\n", pThis));
1127
1128 /* We don't support multiple FILE directives for now. */
1129 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_KEYWORD)
1130 rc = cueParseKeyword(pThis, pTokenizer);
1131 else
1132 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1133 N_("CUE: Error parsing '%s', expected a keyword"), pThis->pszFilename);
1134
1135 if ( RT_SUCCESS(rc)
1136 && cueTokenizerGetTokenType(pTokenizer) != CUETOKENTYPE_EOS)
1137 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1138 N_("CUE: Error parsing '%s', expected end of stream"), pThis->pszFilename);
1139
1140 LogFlowFunc(("returns rc=%Rrc\n", rc));
1141 return rc;
1142}
1143
1144/**
1145 * Finalizes the track list of the image.
1146 *
1147 * @returns VBox status code.
1148 * @param pThis The CUE image state.
1149 * @param cbImage Size of the image data in bytes.
1150 */
1151static int cueTrackListFinalize(PCUEIMAGE pThis, uint64_t cbImage)
1152{
1153 int rc = VINF_SUCCESS;
1154
1155 if ( pThis->cTracksMax == 0
1156 || pThis->pRegionList->aRegions[0].offRegion == UINT64_MAX)
1157 return vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1158 N_("CUE: Error parsing '%s', detected empty track list"), pThis->pszFilename);
1159
1160 /*
1161 * Fixup the track list to contain the proper sizes now that we parsed all tracks,
1162 * check also that there are no gaps in the list.
1163 */
1164 uint32_t cTracks = 1;
1165 uint64_t offDisk = 0;
1166 for (uint32_t i = 1; i < pThis->cTracksMax; i++)
1167 {
1168 PVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[i];
1169 PVDREGIONDESC pRegionPrev = &pThis->pRegionList->aRegions[i - 1];
1170 if (pRegion->offRegion != UINT64_MAX)
1171 {
1172 cTracks++;
1173 uint64_t cBlocks = pRegion->offRegion - (pRegionPrev->offRegion / pRegionPrev->cbBlock);
1174 pRegionPrev->cRegionBlocksOrBytes = pRegionPrev->cbBlock * cBlocks;
1175 offDisk += pRegionPrev->cRegionBlocksOrBytes;
1176
1177 if (cbImage < pRegionPrev->cRegionBlocksOrBytes)
1178 return vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1179 N_("CUE: Error parsing '%s', image file is too small for track list"),
1180 pThis->pszFilename);
1181
1182 cbImage -= pRegionPrev->cRegionBlocksOrBytes;
1183 pRegion->offRegion = offDisk;
1184 }
1185 else
1186 break;
1187 }
1188
1189 /* Fixup last track. */
1190 PVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[cTracks - 1];
1191 pRegion->cRegionBlocksOrBytes = cbImage;
1192
1193 pThis->pRegionList->cRegions = cTracks;
1194 pThis->pRegionList->fFlags = 0;
1195
1196 /* Check that there are no gaps in the track list. */
1197 for (uint32_t i = cTracks; cTracks < pThis->cTracksMax; i++)
1198 {
1199 pRegion = &pThis->pRegionList->aRegions[i];
1200 if (pRegion->offRegion != UINT64_MAX)
1201 {
1202 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1203 N_("CUE: Error parsing '%s', detected gaps in the track list"), pThis->pszFilename);
1204 break;
1205 }
1206 }
1207
1208 return rc;
1209}
1210
1211/**
1212 * Internal. Free all allocated space for representing an image except pThis,
1213 * and optionally delete the image from disk.
1214 */
1215static int cueFreeImage(PCUEIMAGE pThis, bool fDelete)
1216{
1217 int rc = VINF_SUCCESS;
1218
1219 /* Freeing a never allocated image (e.g. because the open failed) is
1220 * not signalled as an error. After all nothing bad happens. */
1221 if (pThis)
1222 {
1223 if (pThis->pStorage)
1224 {
1225 rc = vdIfIoIntFileClose(pThis->pIfIo, pThis->pStorage);
1226 pThis->pStorage = NULL;
1227 }
1228
1229 if (pThis->pStorageData)
1230 {
1231 rc = vdIfIoIntFileClose(pThis->pIfIo, pThis->pStorageData);
1232 pThis->pStorageData = NULL;
1233 }
1234
1235 if (pThis->pRegionList)
1236 {
1237 RTMemFree(pThis->pRegionList);
1238 pThis->pRegionList = NULL;
1239 }
1240
1241 if (pThis->pszDataFilename)
1242 {
1243 RTStrFree(pThis->pszDataFilename);
1244 pThis->pszDataFilename = NULL;
1245 }
1246
1247 if (fDelete && pThis->pszFilename)
1248 vdIfIoIntFileDelete(pThis->pIfIo, pThis->pszFilename);
1249 }
1250
1251 LogFlowFunc(("returns %Rrc\n", rc));
1252 return rc;
1253}
1254
1255/**
1256 * Internal: Open an image, constructing all necessary data structures.
1257 */
1258static int cueOpenImage(PCUEIMAGE pThis, unsigned uOpenFlags)
1259{
1260 pThis->uOpenFlags = uOpenFlags;
1261
1262 pThis->pIfError = VDIfErrorGet(pThis->pVDIfsDisk);
1263 pThis->pIfIo = VDIfIoIntGet(pThis->pVDIfsImage);
1264 AssertPtrReturn(pThis->pIfIo, VERR_INVALID_PARAMETER);
1265
1266 /* Open the image. */
1267 int rc = vdIfIoIntFileOpen(pThis->pIfIo, pThis->pszFilename,
1268 VDOpenFlagsToFileOpenFlags(uOpenFlags,
1269 false /* fCreate */),
1270 &pThis->pStorage);
1271 if (RT_SUCCESS(rc))
1272 {
1273 uint64_t cbFile;
1274 /* The descriptor file shouldn't be huge, so limit ourselfs to 16KB for now. */
1275 rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorage, &cbFile);
1276 if ( RT_SUCCESS(rc)
1277 && cbFile <= _16K - 1)
1278 {
1279 char szInput[_16K];
1280 RT_ZERO(szInput);
1281
1282 rc = vdIfIoIntFileReadSync(pThis->pIfIo, pThis->pStorage, 0,
1283 &szInput, cbFile);
1284 if (RT_SUCCESS(rc))
1285 {
1286 RTStrPurgeEncoding(&szInput[0]);
1287 PCUETOKENIZER pTokenizer = cueTokenizerCreate(&szInput[0]);
1288 if (pTokenizer)
1289 {
1290 rc = cueParseFromTokenizer(pThis, pTokenizer);
1291 RTMemFree(pTokenizer);
1292 if (RT_SUCCESS(rc))
1293 {
1294 /* Open the backing file. */
1295 char szBackingFile[RTPATH_MAX];
1296 rc = RTStrCopy(&szBackingFile[0], sizeof(szBackingFile), pThis->pszFilename);
1297 if (RT_SUCCESS(rc))
1298 {
1299 RTPathStripFilename(&szBackingFile[0]);
1300 rc = RTPathAppend(&szBackingFile[0], sizeof(szBackingFile), pThis->pszDataFilename);
1301 if (RT_SUCCESS(rc))
1302 {
1303 rc = vdIfIoIntFileOpen(pThis->pIfIo, szBackingFile,
1304 VDOpenFlagsToFileOpenFlags(uOpenFlags,
1305 false /* fCreate */),
1306 &pThis->pStorageData);
1307 if (RT_SUCCESS(rc))
1308 {
1309 rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorageData, &cbFile);
1310 if (RT_SUCCESS(rc))
1311 rc = cueTrackListFinalize(pThis, cbFile);
1312 else
1313 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1314 N_("CUE: Unable to query size of backing file '%s'"),
1315 szBackingFile);
1316 }
1317 else
1318 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1319 N_("CUE: Unable to open backing file '%s'"),
1320 szBackingFile);
1321 }
1322 else
1323 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1324 N_("CUE: Error constructing backing filename from '%s'"),
1325 pThis->pszFilename);
1326 }
1327 else
1328 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1329 N_("CUE: Error constructing backing filename from '%s'"),
1330 pThis->pszFilename);
1331 }
1332 }
1333 }
1334 else
1335 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS, N_("CUE: Error reading '%s'"), pThis->pszFilename);
1336 }
1337 else if (RT_SUCCESS(rc))
1338 rc = vdIfError(pThis->pIfError, VERR_VD_INVALID_SIZE,
1339 RT_SRC_POS, N_("CUE: The descriptor file '%s' is too huge (%llu vs %llu)"),
1340 pThis->pszFilename, cbFile, _16K - 1);
1341 }
1342 /* else: Do NOT signal an appropriate error here, as the VD layer has the
1343 * choice of retrying the open if it failed. */
1344
1345 if (RT_FAILURE(rc))
1346 cueFreeImage(pThis, false);
1347 return rc;
1348}
1349
1350/**
1351 * Converts the data form enumeration to a string.
1352 *
1353 * @returns String name of the given data form.
1354 * @param enmDataForm The data form.
1355 */
1356static const char *cueRegionDataFormStringify(VDREGIONDATAFORM enmDataForm)
1357{
1358 switch (enmDataForm)
1359 {
1360 #define DATAFORM2STR(tag) case VDREGIONDATAFORM_##tag: return #tag
1361
1362 DATAFORM2STR(INVALID);
1363 DATAFORM2STR(RAW);
1364 DATAFORM2STR(CDDA);
1365 DATAFORM2STR(CDDA_PAUSE);
1366 DATAFORM2STR(MODE1_2048);
1367 DATAFORM2STR(MODE1_2352);
1368 DATAFORM2STR(MODE1_0);
1369 DATAFORM2STR(XA_2336);
1370 DATAFORM2STR(XA_2352);
1371 DATAFORM2STR(XA_0);
1372 DATAFORM2STR(MODE2_2336);
1373 DATAFORM2STR(MODE2_2352);
1374 DATAFORM2STR(MODE2_0);
1375
1376 #undef DATAFORM2STR
1377
1378 default:
1379 {
1380 AssertMsgFailed(("Unknown data form %d! forgot to add it to the switch?\n", enmDataForm));
1381 return "UNKNOWN!";
1382 }
1383 }
1384}
1385
1386/**
1387 * Converts the data form enumeration to a string.
1388 *
1389 * @returns String name of the given data form.
1390 * @param enmMetadataForm The metadata form.
1391 */
1392static const char *cueRegionMetadataFormStringify(VDREGIONMETADATAFORM enmMetadataForm)
1393{
1394 switch (enmMetadataForm)
1395 {
1396 #define METADATAFORM2STR(tag) case VDREGIONMETADATAFORM_##tag: return #tag
1397
1398 METADATAFORM2STR(INVALID);
1399 METADATAFORM2STR(RAW);
1400 METADATAFORM2STR(NONE);
1401
1402 #undef METADATAFORM2STR
1403
1404 default:
1405 {
1406 AssertMsgFailed(("Unknown metadata form %d! forgot to add it to the switch?\n", enmMetadataForm));
1407 return "UNKNOWN!";
1408 }
1409 }
1410}
1411
1412/**
1413 * Returns the region containing the given offset.
1414 *
1415 * @returns Pointer to the region or NULL if not found.
1416 * @param pThis The CUE image state.
1417 * @param uOffset The offset to look for.
1418 */
1419static PCVDREGIONDESC cueRegionQueryByOffset(PCUEIMAGE pThis, uint64_t uOffset)
1420{
1421 for (uint32_t i = 0; i < pThis->pRegionList->cRegions; i++)
1422 {
1423 PCVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[i];
1424 if ( pRegion->offRegion <= uOffset
1425 && pRegion->offRegion + pRegion->cRegionBlocksOrBytes > uOffset)
1426 return pRegion;
1427 }
1428
1429 return NULL;
1430}
1431
1432/** @copydoc VDIMAGEBACKEND::pfnProbe */
1433static DECLCALLBACK(int) cueProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1434 PVDINTERFACE pVDIfsImage, VDTYPE enmDesiredType, VDTYPE *penmType)
1435{
1436 RT_NOREF(pVDIfsDisk, enmDesiredType);
1437 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
1438 int rc = VINF_SUCCESS;
1439
1440 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
1441
1442 PCUEIMAGE pThis = (PCUEIMAGE)RTMemAllocZ(sizeof(CUEIMAGE));
1443 if (RT_LIKELY(pThis))
1444 {
1445 pThis->pszFilename = pszFilename;
1446 pThis->pStorage = NULL;
1447 pThis->pVDIfsDisk = pVDIfsDisk;
1448 pThis->pVDIfsImage = pVDIfsImage;
1449
1450 rc = cueOpenImage(pThis, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
1451 cueFreeImage(pThis, false);
1452 RTMemFree(pThis);
1453
1454 if (RT_SUCCESS(rc))
1455 *penmType = VDTYPE_OPTICAL_DISC;
1456 else
1457 rc = VERR_VD_GEN_INVALID_HEADER;
1458 }
1459 else
1460 rc = VERR_NO_MEMORY;
1461
1462 LogFlowFunc(("returns %Rrc\n", rc));
1463 return rc;
1464}
1465
1466/** @copydoc VDIMAGEBACKEND::pfnOpen */
1467static DECLCALLBACK(int) cueOpen(const char *pszFilename, unsigned uOpenFlags,
1468 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1469 VDTYPE enmType, void **ppBackendData)
1470{
1471 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n",
1472 pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
1473 int rc;
1474 PCUEIMAGE pThis;
1475
1476 /* Check open flags. All valid flags are supported. */
1477 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
1478 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
1479 AssertReturn(enmType == VDTYPE_OPTICAL_DISC, VERR_NOT_SUPPORTED);
1480
1481 pThis = (PCUEIMAGE)RTMemAllocZ(sizeof(CUEIMAGE));
1482 if (RT_LIKELY(pThis))
1483 {
1484 pThis->pszFilename = pszFilename;
1485 pThis->pStorage = NULL;
1486 pThis->pVDIfsDisk = pVDIfsDisk;
1487 pThis->pVDIfsImage = pVDIfsImage;
1488
1489 rc = cueOpenImage(pThis, uOpenFlags);
1490 if (RT_SUCCESS(rc))
1491 *ppBackendData = pThis;
1492 else
1493 RTMemFree(pThis);
1494 }
1495 else
1496 rc = VERR_NO_MEMORY;
1497
1498 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1499 return rc;
1500}
1501
1502/** @copydoc VDIMAGEBACKEND::pfnClose */
1503static DECLCALLBACK(int) cueClose(void *pBackendData, bool fDelete)
1504{
1505 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1506 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1507 int rc = cueFreeImage(pThis, fDelete);
1508 RTMemFree(pThis);
1509
1510 LogFlowFunc(("returns %Rrc\n", rc));
1511 return rc;
1512}
1513
1514/** @copydoc VDIMAGEBACKEND::pfnRead */
1515static DECLCALLBACK(int) cueRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1516 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1517{
1518 LogFlowFunc(("pBackendData=%#p uOffset=%llu cbToRead=%zu pIoCtx=%#p pcbActuallyRead=%#p\n",
1519 pBackendData, uOffset, cbToRead, pIoCtx, pcbActuallyRead));
1520 int rc = VINF_SUCCESS;
1521 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1522
1523 /* Get the region */
1524 PCVDREGIONDESC pRegion = cueRegionQueryByOffset(pThis, uOffset);
1525 if (pRegion)
1526 {
1527 /* Clip read size to remain in the region (not necessary I think). */
1528 uint64_t offRead = uOffset - pRegion->offRegion;
1529
1530 cbToRead = RT_MIN(cbToRead, pRegion->cRegionBlocksOrBytes - offRead);
1531 Assert(!(cbToRead % pRegion->cbBlock));
1532
1533 /* Need to convert audio data samples to big endian. */
1534 if ( pRegion->enmDataForm == VDREGIONDATAFORM_CDDA
1535 && pThis->fLittleEndian)
1536 {
1537 *pcbActuallyRead = cbToRead;
1538
1539 while (cbToRead)
1540 {
1541 RTSGSEG Segment;
1542 unsigned cSegments = 1;
1543 size_t cbSeg = 0;
1544
1545 cbSeg = vdIfIoIntIoCtxSegArrayCreate(pThis->pIfIo, pIoCtx, &Segment,
1546 &cSegments, cbToRead);
1547
1548 rc = vdIfIoIntFileReadSync(pThis->pIfIo, pThis->pStorageData, uOffset, Segment.pvSeg, cbSeg);
1549 if (RT_FAILURE(rc))
1550 break;
1551
1552 uint16_t *pu16Buf = (uint16_t *)Segment.pvSeg;
1553 for (uint32_t i = 0; i < cbSeg / sizeof(uint16_t); i++)
1554 {
1555 *pu16Buf = RT_BSWAP_U16(*pu16Buf);
1556 pu16Buf++;
1557 }
1558
1559 cbToRead -= RT_MIN(cbToRead, cbSeg);
1560 uOffset += cbSeg;
1561 }
1562 }
1563 else
1564 {
1565 rc = vdIfIoIntFileReadUser(pThis->pIfIo, pThis->pStorageData, uOffset,
1566 pIoCtx, cbToRead);
1567 if (RT_SUCCESS(rc))
1568 *pcbActuallyRead = cbToRead;
1569 }
1570 }
1571 else
1572 rc = VERR_INVALID_PARAMETER;
1573
1574 return rc;
1575}
1576
1577/** @copydoc VDIMAGEBACKEND::pfnWrite */
1578static DECLCALLBACK(int) cueWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
1579 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
1580 size_t *pcbPostRead, unsigned fWrite)
1581{
1582 RT_NOREF7(uOffset, cbToWrite, pIoCtx, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite);
1583 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
1584 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
1585 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1586 int rc;
1587
1588 AssertPtr(pThis);
1589
1590 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1591 rc = VERR_VD_IMAGE_READ_ONLY;
1592 else
1593 rc = VERR_NOT_SUPPORTED;
1594
1595 LogFlowFunc(("returns %Rrc\n", rc));
1596 return rc;
1597}
1598
1599/** @copydoc VDIMAGEBACKEND::pfnFlush */
1600static DECLCALLBACK(int) cueFlush(void *pBackendData, PVDIOCTX pIoCtx)
1601{
1602 RT_NOREF2(pBackendData, pIoCtx);
1603
1604 return VINF_SUCCESS;
1605}
1606
1607/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
1608static DECLCALLBACK(unsigned) cueGetVersion(void *pBackendData)
1609{
1610 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1611 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1612
1613 AssertPtrReturn(pThis, 0);
1614
1615 return 1;
1616}
1617
1618/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
1619static DECLCALLBACK(uint64_t) cueGetFileSize(void *pBackendData)
1620{
1621 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1622 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1623
1624 AssertPtrReturn(pThis, 0);
1625
1626 uint64_t cbFile = 0;
1627 if (pThis->pStorage)
1628 {
1629 int rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorageData, &cbFile);
1630 if (RT_FAILURE(rc))
1631 cbFile = 0; /* Make sure it is 0 */
1632 }
1633
1634 LogFlowFunc(("returns %lld\n", cbFile));
1635 return cbFile;
1636}
1637
1638/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
1639static DECLCALLBACK(int) cueGetPCHSGeometry(void *pBackendData,
1640 PVDGEOMETRY pPCHSGeometry)
1641{
1642 RT_NOREF1(pPCHSGeometry);
1643 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1644 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1645 int rc = VINF_SUCCESS;
1646
1647 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1648
1649 rc = VERR_NOT_SUPPORTED;
1650
1651 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1652 return rc;
1653}
1654
1655/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
1656static DECLCALLBACK(int) cueSetPCHSGeometry(void *pBackendData,
1657 PCVDGEOMETRY pPCHSGeometry)
1658{
1659 RT_NOREF1(pPCHSGeometry);
1660 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
1661 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1662 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1663 int rc = VINF_SUCCESS;
1664
1665 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1666
1667 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1668 rc = VERR_VD_IMAGE_READ_ONLY;
1669 else
1670 rc = VERR_NOT_SUPPORTED;
1671
1672 LogFlowFunc(("returns %Rrc\n", rc));
1673 return rc;
1674}
1675
1676/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
1677static DECLCALLBACK(int) cueGetLCHSGeometry(void *pBackendData,
1678 PVDGEOMETRY pLCHSGeometry)
1679{
1680 RT_NOREF1(pLCHSGeometry);
1681 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1682 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1683 int rc = VINF_SUCCESS;
1684
1685 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1686
1687 rc = VERR_NOT_SUPPORTED;
1688
1689 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1690 return rc;
1691}
1692
1693/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
1694static DECLCALLBACK(int) cueSetLCHSGeometry(void *pBackendData,
1695 PCVDGEOMETRY pLCHSGeometry)
1696{
1697 RT_NOREF1(pLCHSGeometry);
1698 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
1699 pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1700 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1701 int rc = VINF_SUCCESS;
1702
1703 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1704
1705 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1706 rc = VERR_VD_IMAGE_READ_ONLY;
1707 else
1708 rc = VERR_NOT_SUPPORTED;
1709
1710 LogFlowFunc(("returns %Rrc\n", rc));
1711 return rc;
1712}
1713
1714/** @copydoc VDIMAGEBACKEND::pfnQueryRegions */
1715static DECLCALLBACK(int) cueQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
1716{
1717 LogFlowFunc(("pBackendData=%#p ppRegionList=%#p\n", pBackendData, ppRegionList));
1718 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1719
1720 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1721
1722 *ppRegionList = pThis->pRegionList;
1723 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
1724 return VINF_SUCCESS;
1725}
1726
1727/** @copydoc VDIMAGEBACKEND::pfnRegionListRelease */
1728static DECLCALLBACK(void) cueRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
1729{
1730 RT_NOREF1(pRegionList);
1731 LogFlowFunc(("pBackendData=%#p pRegionList=%#p\n", pBackendData, pRegionList));
1732 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1733 AssertPtr(pThis); RT_NOREF(pThis);
1734
1735 /* Nothing to do here. */
1736}
1737
1738/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
1739static DECLCALLBACK(unsigned) cueGetImageFlags(void *pBackendData)
1740{
1741 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1742 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1743
1744 AssertPtrReturn(pThis, 0);
1745
1746 LogFlowFunc(("returns %#x\n", pThis->uImageFlags));
1747 return pThis->uImageFlags;
1748}
1749
1750/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
1751static DECLCALLBACK(unsigned) cueGetOpenFlags(void *pBackendData)
1752{
1753 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1754 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1755
1756 AssertPtrReturn(pThis, 0);
1757
1758 LogFlowFunc(("returns %#x\n", pThis->uOpenFlags));
1759 return pThis->uOpenFlags;
1760}
1761
1762/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
1763static DECLCALLBACK(int) cueSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
1764{
1765 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
1766 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1767 int rc = VINF_SUCCESS;
1768
1769 /* Image must be opened and the new flags must be valid. */
1770 if (!pThis || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
1771 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
1772 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
1773 rc = VERR_INVALID_PARAMETER;
1774 else
1775 {
1776 /* Implement this operation via reopening the image. */
1777 rc = cueFreeImage(pThis, false);
1778 if (RT_SUCCESS(rc))
1779 rc = cueOpenImage(pThis, uOpenFlags);
1780 }
1781
1782 LogFlowFunc(("returns %Rrc\n", rc));
1783 return rc;
1784}
1785
1786/** @copydoc VDIMAGEBACKEND::pfnGetComment */
1787VD_BACKEND_CALLBACK_GET_COMMENT_DEF_NOT_SUPPORTED(cueGetComment);
1788
1789/** @copydoc VDIMAGEBACKEND::pfnSetComment */
1790VD_BACKEND_CALLBACK_SET_COMMENT_DEF_NOT_SUPPORTED(cueSetComment, PCUEIMAGE);
1791
1792/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
1793VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(cueGetUuid);
1794
1795/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
1796VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(cueSetUuid, PCUEIMAGE);
1797
1798/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
1799VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(cueGetModificationUuid);
1800
1801/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
1802VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(cueSetModificationUuid, PCUEIMAGE);
1803
1804/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
1805VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(cueGetParentUuid);
1806
1807/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
1808VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(cueSetParentUuid, PCUEIMAGE);
1809
1810/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
1811VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(cueGetParentModificationUuid);
1812
1813/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
1814VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(cueSetParentModificationUuid, PCUEIMAGE);
1815
1816/** @copydoc VDIMAGEBACKEND::pfnDump */
1817static DECLCALLBACK(void) cueDump(void *pBackendData)
1818{
1819 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1820
1821 AssertPtrReturnVoid(pThis);
1822 vdIfErrorMessage(pThis->pIfError, "Dumping CUE image \"%s\" mode=%s uOpenFlags=%X File=%#p\n",
1823 pThis->pszFilename,
1824 (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
1825 pThis->uOpenFlags,
1826 pThis->pStorage);
1827 vdIfErrorMessage(pThis->pIfError, "Backing File \"%s\" File=%#p\n",
1828 pThis->pszDataFilename, pThis->pStorageData);
1829 vdIfErrorMessage(pThis->pIfError, "Number of tracks: %u\n", pThis->pRegionList->cRegions);
1830 for (uint32_t i = 0; i < pThis->pRegionList->cRegions; i++)
1831 {
1832 PCVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[i];
1833
1834 vdIfErrorMessage(pThis->pIfError, "------------------------ Track %u ------------------------\n", i);
1835 vdIfErrorMessage(pThis->pIfError, "Start=%llu Size=%llu BlockSize=%llu DataSize=%llu MetadataSize=%llu\n",
1836 pRegion->offRegion, pRegion->cRegionBlocksOrBytes, pRegion->cbBlock, pRegion->cbData,
1837 pRegion->cbMetadata);
1838 vdIfErrorMessage(pThis->pIfError, "DataForm=%s MetadataForm=%s\n",
1839 cueRegionDataFormStringify(pRegion->enmDataForm),
1840 cueRegionMetadataFormStringify(pRegion->enmMetadataForm));
1841 }
1842}
1843
1844
1845
1846const VDIMAGEBACKEND g_CueBackend =
1847{
1848 /* u32Version */
1849 VD_IMGBACKEND_VERSION,
1850 /* pszBackendName */
1851 "CUE",
1852 /* uBackendCaps */
1853 VD_CAP_FILE | VD_CAP_VFS,
1854 /* paFileExtensions */
1855 s_aCueFileExtensions,
1856 /* paConfigInfo */
1857 NULL,
1858 /* pfnProbe */
1859 cueProbe,
1860 /* pfnOpen */
1861 cueOpen,
1862 /* pfnCreate */
1863 NULL,
1864 /* pfnRename */
1865 NULL,
1866 /* pfnClose */
1867 cueClose,
1868 /* pfnRead */
1869 cueRead,
1870 /* pfnWrite */
1871 cueWrite,
1872 /* pfnFlush */
1873 cueFlush,
1874 /* pfnDiscard */
1875 NULL,
1876 /* pfnGetVersion */
1877 cueGetVersion,
1878 /* pfnGetFileSize */
1879 cueGetFileSize,
1880 /* pfnGetPCHSGeometry */
1881 cueGetPCHSGeometry,
1882 /* pfnSetPCHSGeometry */
1883 cueSetPCHSGeometry,
1884 /* pfnGetLCHSGeometry */
1885 cueGetLCHSGeometry,
1886 /* pfnSetLCHSGeometry */
1887 cueSetLCHSGeometry,
1888 /* pfnQueryRegions */
1889 cueQueryRegions,
1890 /* pfnRegionListRelease */
1891 cueRegionListRelease,
1892 /* pfnGetImageFlags */
1893 cueGetImageFlags,
1894 /* pfnGetOpenFlags */
1895 cueGetOpenFlags,
1896 /* pfnSetOpenFlags */
1897 cueSetOpenFlags,
1898 /* pfnGetComment */
1899 cueGetComment,
1900 /* pfnSetComment */
1901 cueSetComment,
1902 /* pfnGetUuid */
1903 cueGetUuid,
1904 /* pfnSetUuid */
1905 cueSetUuid,
1906 /* pfnGetModificationUuid */
1907 cueGetModificationUuid,
1908 /* pfnSetModificationUuid */
1909 cueSetModificationUuid,
1910 /* pfnGetParentUuid */
1911 cueGetParentUuid,
1912 /* pfnSetParentUuid */
1913 cueSetParentUuid,
1914 /* pfnGetParentModificationUuid */
1915 cueGetParentModificationUuid,
1916 /* pfnSetParentModificationUuid */
1917 cueSetParentModificationUuid,
1918 /* pfnDump */
1919 cueDump,
1920 /* pfnGetTimestamp */
1921 NULL,
1922 /* pfnGetParentTimestamp */
1923 NULL,
1924 /* pfnSetParentTimestamp */
1925 NULL,
1926 /* pfnGetParentFilename */
1927 NULL,
1928 /* pfnSetParentFilename */
1929 NULL,
1930 /* pfnComposeLocation */
1931 genericFileComposeLocation,
1932 /* pfnComposeName */
1933 genericFileComposeName,
1934 /* pfnCompact */
1935 NULL,
1936 /* pfnResize */
1937 NULL,
1938 /* pfnRepair */
1939 NULL,
1940 /* pfnTraverseMetadata */
1941 NULL,
1942 /* u32VersionEnd */
1943 VD_IMGBACKEND_VERSION
1944};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette