VirtualBox

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

Last change on this file since 77910 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use