VirtualBox

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

Last change on this file since 67761 was 66494, checked in by vboxsync, 8 years ago

Storage: Get rid of duplicated code in the backends for unsupported features

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