VirtualBox

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

Last change on this file since 72293 was 72293, checked in by vboxsync, 7 years ago

CUE: Fixed REM keyword parsing.

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