VirtualBox

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

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

Storage/CUE: Fixed VINF_SUCCESS when the file is too big, should return VERR_VD_INVALID_SIZE. CUE sometimes ended up with things it doesn't grok (no idea why this wasn't happen more frequently).

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