VirtualBox

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

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

Storage/CUE: build fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 66.6 KB
Line 
1/* $Id: CUE.cpp 66142 2017-03-16 17:38:29Z 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.215389.xyz. 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/alloc.h>
30#include <iprt/cdefs.h>
31#include <iprt/ctype.h>
32#include <iprt/path.h>
33#include <iprt/string.h>
34
35#include "VDBackends.h"
36
37
38/*********************************************************************************************************************************
39* Constants And Macros, Structures and Typedefs *
40*********************************************************************************************************************************/
41
42/**
43 * CUE descriptor file token type.
44 */
45typedef enum CUETOKENTYPE
46{
47 /** Invalid token type. */
48 CUETOKENTYPE_INVALID = 0,
49 /** Reserved keyword. */
50 CUETOKENTYPE_KEYWORD,
51 /** String token. */
52 CUETOKENTYPE_STRING,
53 /** Unsigned integer. */
54 CUETOKENTYPE_INTEGER_UNSIGNED,
55 /** MSF (mm:ss:ff) location token. */
56 CUETOKENTYPE_MSF,
57 /** Error token (unexpected character found). */
58 CUETOKENTYPE_ERROR,
59 /** End of stream token. */
60 CUETOKENTYPE_EOS
61} CUETOKENTYPE;
62
63/**
64 * CUE reservered keyword type.
65 */
66typedef enum CUEKEYWORD
67{
68 /** Invalid keyword. */
69 CUEKEYWORD_INVALID = 0,
70 /** FILE. */
71 CUEKEYWORD_FILE,
72 /** BINARY */
73 CUEKEYWORD_BINARY,
74 /** WAVE */
75 CUEKEYWORD_WAVE,
76 /** MP3 */
77 CUEKEYWORD_MP3,
78 /** AIFF */
79 CUEKEYWORD_AIFF,
80 /** CATALOG */
81 CUEKEYWORD_CATALOG,
82 CUEKEYWORD_CDTEXTFILE,
83 CUEKEYWORD_FLAGS,
84 CUEKEYWORD_INDEX,
85 CUEKEYWORD_ISRC,
86 CUEKEYWORD_PERFORMER,
87 CUEKEYWORD_POSTGAP,
88 CUEKEYWORD_PREGAP,
89 CUEKEYWORD_SONGWRITER,
90 CUEKEYWORD_TITLE,
91 CUEKEYWORD_TRACK,
92 CUEKEYWORD_MODE1_2048,
93 CUEKEYWORD_MODE1_2352,
94 CUEKEYWORD_AUDIO,
95 CUEKEYWORD_REM
96} CUEKEYWORD;
97
98/**
99 * CUE sheet token.
100 */
101typedef struct CUETOKEN
102{
103 /** The token type. */
104 CUETOKENTYPE enmType;
105 /** Token type dependent data. */
106 union
107 {
108 /** Keyword token. */
109 struct
110 {
111 /** The keyword enumerator. */
112 CUEKEYWORD enmKeyword;
113 } Keyword;
114 /** String token (without quotation marks). */
115 struct
116 {
117 /** Pointer to the start of the string. */
118 const char *psz;
119 /** Number of characters for the string excluding the null terminator. */
120 size_t cch;
121 } String;
122 /** Integer token. */
123 struct
124 {
125 /** Numerical constant. */
126 uint64_t u64;
127 } Int;
128 /** MSF location token. */
129 struct
130 {
131 /** Minute part. */
132 uint8_t u8Minute;
133 /** Second part. */
134 uint8_t u8Second;
135 /** Frame part. */
136 uint8_t u8Frame;
137 } Msf;
138 } Type;
139} CUETOKEN;
140/** Pointer to a CUE sheet token. */
141typedef CUETOKEN *PCUETOKEN;
142/** Pointer to a const CUE sheet token. */
143typedef const CUETOKEN *PCCUETOKEN;
144
145/**
146 * CUE tokenizer state.
147 */
148typedef struct CUETOKENIZER
149{
150 /** Char buffer to read from. */
151 const char *pszInput;
152 /** Token 1. */
153 CUETOKEN Token1;
154 /** Token 2. */
155 CUETOKEN Token2;
156 /** Pointer to the current active token. */
157 PCUETOKEN pTokenCurr;
158 /** The next token in the input stream (used for peeking). */
159 PCUETOKEN pTokenNext;
160} CUETOKENIZER;
161/** Pointer to a CUE tokenizer state. */
162typedef CUETOKENIZER *PCUETOKENIZER;
163
164/**
165 * CUE keyword entry.
166 */
167typedef struct CUEKEYWORDDESC
168{
169 /** Keyword string. */
170 const char *pszKeyword;
171 /** Size of the string in characters without zero terminator. */
172 size_t cchKeyword;
173 /** Keyword type. */
174 CUEKEYWORD enmKeyword;
175} CUEKEYWORDDESC;
176/** Pointer to a CUE keyword entry. */
177typedef CUEKEYWORDDESC *PCUEKEYWORDDESC;
178/** Pointer to a const CUE keyword entry. */
179typedef const CUEKEYWORDDESC *PCCUEKEYWORDDESC;
180
181/**
182 * CUE image data structure.
183 */
184typedef struct CUEIMAGE
185{
186 /** Image name. */
187 const char *pszFilename;
188 /** Storage handle. */
189 PVDIOSTORAGE pStorage;
190 /** The backing file containing the actual data. */
191 char *pszDataFilename;
192 /** Storage handle for the backing file. */
193 PVDIOSTORAGE pStorageData;
194
195 /** Pointer to the per-disk VD interface list. */
196 PVDINTERFACE pVDIfsDisk;
197 /** Pointer to the per-image VD interface list. */
198 PVDINTERFACE pVDIfsImage;
199 /** Error interface. */
200 PVDINTERFACEERROR pIfError;
201 /** I/O interface. */
202 PVDINTERFACEIOINT pIfIo;
203
204 /** Open flags passed by VD layer. */
205 unsigned uOpenFlags;
206 /** Image flags defined during creation or determined during open. */
207 unsigned uImageFlags;
208 /** Maximum number of tracks the region list can hold. */
209 uint32_t cTracksMax;
210 /** Pointer to our internal region list. */
211 PVDREGIONLIST pRegionList;
212} CUEIMAGE, *PCUEIMAGE;
213
214
215/*********************************************************************************************************************************
216* Static Variables *
217*********************************************************************************************************************************/
218
219/** NULL-terminated array of supported file extensions. */
220static const VDFILEEXTENSION s_aCueFileExtensions[] =
221{
222 {"cue", VDTYPE_DVD},
223 {NULL, VDTYPE_INVALID}
224};
225
226/**
227 * Known keywords.
228 */
229static const CUEKEYWORDDESC g_aCueKeywords[] =
230{
231 {RT_STR_TUPLE("FILE"), CUEKEYWORD_FILE},
232 {RT_STR_TUPLE("BINARY"), CUEKEYWORD_BINARY},
233 {RT_STR_TUPLE("WAVE"), CUEKEYWORD_WAVE},
234 {RT_STR_TUPLE("MP3"), CUEKEYWORD_MP3},
235 {RT_STR_TUPLE("AIFF"), CUEKEYWORD_AIFF},
236 {RT_STR_TUPLE("CATALOG"), CUEKEYWORD_CATALOG},
237 {RT_STR_TUPLE("CDTEXTFILE"), CUEKEYWORD_CDTEXTFILE},
238 {RT_STR_TUPLE("FLAGS"), CUEKEYWORD_FLAGS},
239 {RT_STR_TUPLE("INDEX"), CUEKEYWORD_INDEX},
240 {RT_STR_TUPLE("ISRC"), CUEKEYWORD_ISRC},
241 {RT_STR_TUPLE("PERFORMER"), CUEKEYWORD_PERFORMER},
242 {RT_STR_TUPLE("POSTGAP"), CUEKEYWORD_POSTGAP},
243 {RT_STR_TUPLE("PREGAP"), CUEKEYWORD_PREGAP},
244 {RT_STR_TUPLE("SONGWRITER"), CUEKEYWORD_SONGWRITER},
245 {RT_STR_TUPLE("TITLE"), CUEKEYWORD_TITLE},
246 {RT_STR_TUPLE("TRACK"), CUEKEYWORD_TRACK},
247 {RT_STR_TUPLE("MODE1/2048"), CUEKEYWORD_MODE1_2048},
248 {RT_STR_TUPLE("MODE1/2352"), CUEKEYWORD_MODE1_2352},
249 {RT_STR_TUPLE("AUDIO"), CUEKEYWORD_AUDIO},
250 {RT_STR_TUPLE("REM"), CUEKEYWORD_REM}
251};
252
253
254/*********************************************************************************************************************************
255* Internal Functions *
256*********************************************************************************************************************************/
257
258/**
259 * Ensures that the region list can hold up to the given number of tracks.
260 *
261 * @returns VBox status code.
262 * @param pThis The CUE image state.
263 * @param cTracksMax Maximum number of tracks.
264 */
265static int cueEnsureRegionListSize(PCUEIMAGE pThis, uint32_t cTracksMax)
266{
267 int rc = VINF_SUCCESS;
268
269 if (pThis->cTracksMax < cTracksMax)
270 {
271 PVDREGIONLIST pRegionListNew = (PVDREGIONLIST)RTMemRealloc(pThis->pRegionList,
272 RT_UOFFSETOF(VDREGIONLIST, aRegions[cTracksMax]));
273 if (pRegionListNew)
274 {
275 /* Init all the new allocated tracks. */
276 for (uint32_t i = pThis->cTracksMax; i < cTracksMax; i++)
277 pRegionListNew->aRegions[i].offRegion = UINT64_MAX;
278 pThis->pRegionList = pRegionListNew;
279 pThis->cTracksMax = cTracksMax;
280 }
281 else
282 rc = VERR_NO_MEMORY;
283 }
284
285 return rc;
286}
287
288/**
289 * Returns whether the tokenizer reached the end of the stream.
290 *
291 * @returns true if the tokenizer reached the end of stream marker
292 * false otherwise.
293 * @param pTokenizer The tokenizer state.
294 */
295DECLINLINE(bool) cueTokenizerIsEos(PCUETOKENIZER pTokenizer)
296{
297 return *pTokenizer->pszInput == '\0';
298}
299
300/**
301 * Skip one character in the input stream.
302 *
303 * @returns nothing.
304 * @param pTokenizer The tokenizer state.
305 */
306DECLINLINE(void) cueTokenizerSkipCh(PCUETOKENIZER pTokenizer)
307{
308 pTokenizer->pszInput++;
309}
310
311/**
312 * Returns the next char in the input buffer without advancing it.
313 *
314 * @returns Next character in the input buffer.
315 * @param pTokenizer The tokenizer state.
316 */
317DECLINLINE(char) cueTokenizerPeekCh(PCUETOKENIZER pTokenizer)
318{
319 return cueTokenizerIsEos(pTokenizer)
320 ? '\0'
321 : *(pTokenizer->pszInput + 1);
322}
323
324/**
325 * Returns the next character in the input buffer advancing the internal
326 * position.
327 *
328 * @returns Next character in the stream.
329 * @param pTokenizer The tokenizer state.
330 */
331DECLINLINE(char) cueTokenizerGetCh(PCUETOKENIZER pTokenizer)
332{
333 char ch;
334
335 if (cueTokenizerIsEos(pTokenizer))
336 ch = '\0';
337 else
338 ch = *pTokenizer->pszInput;
339
340 return ch;
341}
342
343/**
344 * Sets a new line for the tokenizer.
345 *
346 * @returns nothing.
347 * @param pTokenizer The tokenizer state.
348 */
349DECLINLINE(void) cueTokenizerNewLine(PCUETOKENIZER pTokenizer, unsigned cSkip)
350{
351 pTokenizer->pszInput += cSkip;
352}
353
354/**
355 * Checks whether the current position in the input stream is a new line
356 * and skips it.
357 *
358 * @returns Flag whether there was a new line at the current position
359 * in the input buffer.
360 * @param pTokenizer The tokenizer state.
361 */
362DECLINLINE(bool) cueTokenizerIsSkipNewLine(PCUETOKENIZER pTokenizer)
363{
364 bool fNewline = true;
365
366 if ( cueTokenizerGetCh(pTokenizer) == '\r'
367 && cueTokenizerPeekCh(pTokenizer) == '\n')
368 cueTokenizerNewLine(pTokenizer, 2);
369 else if (cueTokenizerGetCh(pTokenizer) == '\n')
370 cueTokenizerNewLine(pTokenizer, 1);
371 else
372 fNewline = false;
373
374 return fNewline;
375}
376
377/**
378 * Skips a multi line comment.
379 *
380 * @returns nothing.
381 * @param pTokenizer The tokenizer state.
382 */
383DECLINLINE(void) cueTokenizerSkipComment(PCUETOKENIZER pTokenizer)
384{
385 while ( !cueTokenizerIsEos(pTokenizer)
386 && !cueTokenizerIsSkipNewLine(pTokenizer))
387 cueTokenizerSkipCh(pTokenizer);
388}
389
390/**
391 * Skip all whitespace starting from the current input buffer position.
392 * Skips all present comments too.
393 *
394 * @returns nothing.
395 * @param pTokenizer The tokenizer state.
396 */
397DECLINLINE(void) cueTokenizerSkipWhitespace(PCUETOKENIZER pTokenizer)
398{
399 while (!cueTokenizerIsEos(pTokenizer))
400 {
401 while ( cueTokenizerGetCh(pTokenizer) == ' '
402 || cueTokenizerGetCh(pTokenizer) == '\t')
403 cueTokenizerSkipCh(pTokenizer);
404
405 if ( !cueTokenizerIsEos(pTokenizer)
406 && !cueTokenizerIsSkipNewLine(pTokenizer))
407 break; /* Skipped everything, next is some real content. */
408 }
409}
410
411/**
412 * Get an identifier token from the tokenizer.
413 *
414 * @returns nothing.
415 * @param pTokenizer The tokenizer state.
416 * @param pToken The uninitialized token.
417 */
418static void cueTokenizerGetKeyword(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
419{
420 char ch;
421 unsigned cchKeyword = 0;
422 bool fIsKeyword = false;
423 bool fIsComment = false;
424 const char *pszKeyword = pTokenizer->pszInput;
425
426 Assert(RT_C_IS_ALPHA(*pszKeyword));
427
428 do
429 {
430 fIsComment = false;
431
432 do
433 {
434 cchKeyword++;
435 cueTokenizerSkipCh(pTokenizer);
436 ch = cueTokenizerGetCh(pTokenizer);
437 }
438 while (RT_C_IS_ALNUM(ch) || ch == '_' || ch == '/' || ch == '.');
439
440 /* Check whether we got a keyword or a string constant. */
441 for (unsigned i = 0; i < RT_ELEMENTS(g_aCueKeywords); i++)
442 {
443 if (!RTStrNCmp(g_aCueKeywords[i].pszKeyword, pszKeyword, RT_MIN(cchKeyword, g_aCueKeywords[i].cchKeyword)))
444 {
445 if (g_aCueKeywords[i].enmKeyword == CUEKEYWORD_REM)
446 {
447 /* The REM keyword is handled here as it indicates a comment which we just skip. */
448 cueTokenizerSkipComment(pTokenizer);
449 fIsComment = true;
450 }
451 else
452 {
453 fIsKeyword = true;
454 pToken->enmType = CUETOKENTYPE_KEYWORD;
455 pToken->Type.Keyword.enmKeyword = g_aCueKeywords[i].enmKeyword;
456 }
457 break;
458 }
459 }
460 } while (fIsComment);
461
462 /* Make it a string. */
463 if (ch == '\0')
464 pToken->enmType = CUETOKENTYPE_EOS;
465 else if (!fIsKeyword)
466 {
467 pToken->enmType = CUETOKENTYPE_STRING;
468 pToken->Type.String.psz = pszKeyword;
469 pToken->Type.String.cch = cchKeyword;
470 }
471}
472
473/**
474 * Get an integer value or MSF location indicator from the tokenizer.
475 *
476 * @returns nothing.
477 * @param pTokenizer The tokenizer state.
478 * @param pToken The uninitialized token.
479 */
480static void cueTokenizerGetIntegerOrMsf(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
481{
482 char szInt[20 + 1]; /* Maximum which fits into an unsigned 64bit integer + zero terminator. */
483 unsigned cchInt = 0;
484 bool fMsf = false;
485 char ch = cueTokenizerGetCh(pTokenizer);
486
487 Assert(RT_C_IS_DIGIT(ch));
488 RT_ZERO(szInt);
489
490 /* Go through the characters and check for the : mark which denotes MSF location indicator. */
491 do
492 {
493 szInt[cchInt++] = ch;
494 cueTokenizerSkipCh(pTokenizer);
495 ch = cueTokenizerGetCh(pTokenizer);
496 if (ch == ':')
497 fMsf = true;
498 }
499 while ( (RT_C_IS_DIGIT(ch) || ch == ':')
500 && cchInt < sizeof(szInt));
501
502 if (cchInt < sizeof(szInt) - 1)
503 {
504 if (fMsf)
505 {
506 /* Check that the format matches our expectations (mm:ss:ff). */
507 if ( cchInt == 8 && szInt[2] == ':' && szInt[5] == ':')
508 {
509 /* Parse the single fields. */
510 szInt[2] = '\0';
511 szInt[5] = '\0';
512
513 int rc = RTStrToUInt8Full(&szInt[0], 10, &pToken->Type.Msf.u8Minute);
514 if (RT_SUCCESS(rc))
515 rc = RTStrToUInt8Full(&szInt[3], 10, &pToken->Type.Msf.u8Second);
516 if (RT_SUCCESS(rc))
517 rc = RTStrToUInt8Full(&szInt[6], 10, &pToken->Type.Msf.u8Frame);
518 if (RT_SUCCESS(rc))
519 pToken->enmType = CUETOKENTYPE_MSF;
520 else
521 pToken->enmType = CUETOKENTYPE_ERROR;
522 }
523 else
524 pToken->enmType = CUETOKENTYPE_ERROR;
525 }
526 else
527 {
528 pToken->enmType = CUETOKENTYPE_INTEGER_UNSIGNED;
529 int rc = RTStrToUInt64Full(&szInt[0], 10, &pToken->Type.Int.u64);
530 if (RT_FAILURE(rc))
531 pToken->enmType = CUETOKENTYPE_ERROR;
532 }
533 }
534 else
535 pToken->enmType = CUETOKENTYPE_ERROR;
536}
537
538/**
539 * Parses a string constant.
540 *
541 * @returns nothing.
542 * @param pTokenizer The tokenizer state.
543 * @param pToken The uninitialized token.
544 *
545 * @remarks: No escape sequences allowed at this time.
546 */
547static void cueTokenizerGetStringConst(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
548{
549 unsigned cchStr = 0;
550
551 Assert(cueTokenizerGetCh(pTokenizer) == '\"');
552 cueTokenizerSkipCh(pTokenizer); /* Skip " */
553
554 pToken->enmType = CUETOKENTYPE_STRING;
555 pToken->Type.String.psz = pTokenizer->pszInput;
556
557 while (cueTokenizerGetCh(pTokenizer) != '\"')
558 {
559 cchStr++;
560 cueTokenizerSkipCh(pTokenizer);
561 }
562
563 cueTokenizerSkipCh(pTokenizer); /* Skip closing " */
564
565 pToken->Type.String.cch = cchStr;
566}
567
568/**
569 * Get the end of stream token.
570 *
571 * @returns nothing.
572 * @param pTokenizer The tokenizer state.
573 * @param pToken The uninitialized token.
574 */
575static void cueTokenizerGetEos(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
576{
577 Assert(cueTokenizerGetCh(pTokenizer) == '\0'); RT_NOREF(pTokenizer);
578
579 pToken->enmType = CUETOKENTYPE_EOS;
580}
581
582/**
583 * Read the next token from the tokenizer stream.
584 *
585 * @returns nothing.
586 * @param pTokenizer The tokenizer to read from.
587 * @param pToken Uninitialized token to fill the token data into.
588 */
589static void cueTokenizerReadNextToken(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
590{
591 /* Skip all eventually existing whitespace, newlines and comments first. */
592 cueTokenizerSkipWhitespace(pTokenizer);
593
594 char ch = cueTokenizerGetCh(pTokenizer);
595 if (RT_C_IS_ALPHA(ch))
596 cueTokenizerGetKeyword(pTokenizer, pToken);
597 else if (RT_C_IS_DIGIT(ch))
598 cueTokenizerGetIntegerOrMsf(pTokenizer, pToken);
599 else if (ch == '\"')
600 cueTokenizerGetStringConst(pTokenizer, pToken);
601 else if (ch == '\0')
602 cueTokenizerGetEos(pTokenizer, pToken);
603 else
604 pToken->enmType = CUETOKENTYPE_ERROR;
605}
606
607/**
608 * Create a new tokenizer.
609 *
610 * @returns Pointer to the new tokenizer state on success.
611 * NULL if out of memory.
612 * @param pszInput The input to create the tokenizer for.
613 */
614static PCUETOKENIZER cueTokenizerCreate(const char *pszInput)
615{
616 PCUETOKENIZER pTokenizer = (PCUETOKENIZER)RTMemAllocZ(sizeof(CUETOKENIZER));
617 if (pTokenizer)
618 {
619 pTokenizer->pszInput = pszInput;
620 pTokenizer->pTokenCurr = &pTokenizer->Token1;
621 pTokenizer->pTokenNext = &pTokenizer->Token2;
622 /* Fill the tokenizer with two first tokens. */
623 cueTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenCurr);
624 cueTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
625 }
626
627 return pTokenizer;
628}
629
630/**
631 * Get the current token in the input stream.
632 *
633 * @returns Pointer to the next token in the stream.
634 * @param pTokenizer The tokenizer to destroy.
635 */
636DECLINLINE(PCCUETOKEN) cueTokenizerGetToken(PCUETOKENIZER pTokenizer)
637{
638 return pTokenizer->pTokenCurr;
639}
640
641/**
642 * Get the class of the current token.
643 *
644 * @returns Class of the current token.
645 * @param pTokenizer The tokenizer state.
646 */
647DECLINLINE(CUETOKENTYPE) cueTokenizerGetTokenType(PCUETOKENIZER pTokenizer)
648{
649 return pTokenizer->pTokenCurr->enmType;
650}
651
652/**
653 * Returns the token class of the next token in the stream.
654 *
655 * @returns Token class of the next token.
656 * @param pTokenizer The tokenizer state.
657 */
658DECLINLINE(CUETOKENTYPE) cueTokenizerPeekNextType(PCUETOKENIZER pTokenizer)
659{
660 return pTokenizer->pTokenNext->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 rc = cueParseTrackList(pThis, pTokenizer);
1042 else
1043 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1044 N_("CUE: Error parsing '%s', the file type is not supported (only BINARY)"), pThis->pszFilename);
1045 }
1046 else
1047 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1048 N_("CUE: Error parsing '%s', expected file type"), pThis->pszFilename);
1049 }
1050 else
1051 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1052 N_("CUE: Error parsing '%s', failed to allocate memory for filename"), pThis->pszFilename);
1053 }
1054 else
1055 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1056 N_("CUE: Error parsing '%s', expected filename after FILE directive"), pThis->pszFilename);
1057
1058 return rc;
1059}
1060
1061/**
1062 * Parses the keyword in the given tokenizer.
1063 *
1064 * @returns VBox status code.
1065 * @param pThis The CUE image state.
1066 * @param pTokenizer The tokenizer state.
1067 */
1068static int cueParseKeyword(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer)
1069{
1070 int rc = VINF_SUCCESS;
1071
1072 if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_FILE))
1073 rc = cueParseFile(pThis, pTokenizer);
1074 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_TITLE))
1075 rc = cueParseAndSkipStringRemainder(pThis, pTokenizer, "TITLE");
1076 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_PERFORMER))
1077 rc = cueParseAndSkipStringRemainder(pThis, pTokenizer, "PERFORMER");
1078 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_SONGWRITER))
1079 rc = cueParseAndSkipStringRemainder(pThis, pTokenizer, "SONGWRITER");
1080 else /* Skip all other keywords we don't need/support. */
1081 cueTokenizerConsume(pTokenizer);
1082
1083 return rc;
1084}
1085
1086
1087/**
1088 * Parses the CUE sheet from the given tokenizer.
1089 *
1090 * @returns VBox status code.
1091 * @param pThis The CUE image state.
1092 * @param pTokenizer The tokenizer state.
1093 */
1094static int cueParseFromTokenizer(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer)
1095{
1096 int rc = VINF_SUCCESS;
1097
1098 LogFlowFunc(("pThis=%p\n", pThis));
1099
1100 /* We don't support multiple FILE directives for now. */
1101 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_KEYWORD)
1102 rc = cueParseKeyword(pThis, pTokenizer);
1103 else
1104 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1105 N_("CUE: Error parsing '%s', expected a keyword"), pThis->pszFilename);
1106
1107 if ( RT_SUCCESS(rc)
1108 && cueTokenizerGetTokenType(pTokenizer) != CUETOKENTYPE_EOS)
1109 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1110 N_("CUE: Error parsing '%s', expected end of stream"), pThis->pszFilename);
1111
1112 LogFlowFunc(("returns rc=%Rrc\n", rc));
1113 return rc;
1114}
1115
1116/**
1117 * Finalizes the track list of the image.
1118 *
1119 * @returns VBox status code.
1120 * @param pThis The CUE image state.
1121 * @param cbImage Size of the image data in bytes.
1122 */
1123static int cueTrackListFinalize(PCUEIMAGE pThis, uint64_t cbImage)
1124{
1125 int rc = VINF_SUCCESS;
1126
1127 if ( pThis->cTracksMax == 0
1128 || pThis->pRegionList->aRegions[0].offRegion == UINT64_MAX)
1129 return vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1130 N_("CUE: Error parsing '%s', detected empty track list"), pThis->pszFilename);
1131
1132 /*
1133 * Fixup the track list to contain the proper sizes now that we parsed all tracks,
1134 * check also that there are no gaps in the list.
1135 */
1136 uint32_t cTracks = 1;
1137 uint64_t offDisk = 0;
1138 for (uint32_t i = 1; i < pThis->cTracksMax; i++)
1139 {
1140 PVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[i];
1141 PVDREGIONDESC pRegionPrev = &pThis->pRegionList->aRegions[i - 1];
1142 if (pRegion->offRegion != UINT64_MAX)
1143 {
1144 cTracks++;
1145 pRegionPrev->cRegionBlocksOrBytes = pRegionPrev->cbBlock * pRegion->offRegion;
1146 offDisk += pRegionPrev->cRegionBlocksOrBytes;
1147
1148 if (cbImage < pRegionPrev->cRegionBlocksOrBytes)
1149 return vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1150 N_("CUE: Error parsing '%s', image file is too small for track list"),
1151 pThis->pszFilename);
1152
1153 cbImage -= pRegionPrev->cRegionBlocksOrBytes;
1154 pRegion->offRegion = offDisk;
1155 }
1156 else
1157 break;
1158 }
1159
1160 /* Fixup last track. */
1161 PVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[cTracks - 1];
1162 pRegion->cRegionBlocksOrBytes = cbImage;
1163
1164 pThis->pRegionList->cRegions = cTracks;
1165 pThis->pRegionList->fFlags = 0;
1166
1167 /* Check that there are no gaps in the track list. */
1168 for (uint32_t i = cTracks; cTracks < pThis->cTracksMax; i++)
1169 {
1170 pRegion = &pThis->pRegionList->aRegions[i];
1171 if (pRegion->offRegion != UINT64_MAX)
1172 {
1173 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1174 N_("CUE: Error parsing '%s', detected gaps in the track list"), pThis->pszFilename);
1175 break;
1176 }
1177 }
1178
1179 return rc;
1180}
1181
1182/**
1183 * Internal. Free all allocated space for representing an image except pThis,
1184 * and optionally delete the image from disk.
1185 */
1186static int cueFreeImage(PCUEIMAGE pThis, bool fDelete)
1187{
1188 int rc = VINF_SUCCESS;
1189
1190 /* Freeing a never allocated image (e.g. because the open failed) is
1191 * not signalled as an error. After all nothing bad happens. */
1192 if (pThis)
1193 {
1194 if (pThis->pStorage)
1195 {
1196 rc = vdIfIoIntFileClose(pThis->pIfIo, pThis->pStorage);
1197 pThis->pStorage = NULL;
1198 }
1199
1200 if (pThis->pStorageData)
1201 {
1202 rc = vdIfIoIntFileClose(pThis->pIfIo, pThis->pStorageData);
1203 pThis->pStorageData = NULL;
1204 }
1205
1206 if (pThis->pRegionList)
1207 {
1208 RTMemFree(pThis->pRegionList);
1209 pThis->pRegionList = NULL;
1210 }
1211
1212 if (pThis->pszDataFilename)
1213 {
1214 RTStrFree(pThis->pszDataFilename);
1215 pThis->pszDataFilename = NULL;
1216 }
1217
1218 if (fDelete && pThis->pszFilename)
1219 vdIfIoIntFileDelete(pThis->pIfIo, pThis->pszFilename);
1220 }
1221
1222 LogFlowFunc(("returns %Rrc\n", rc));
1223 return rc;
1224}
1225
1226/**
1227 * Internal: Open an image, constructing all necessary data structures.
1228 */
1229static int cueOpenImage(PCUEIMAGE pThis, unsigned uOpenFlags)
1230{
1231 pThis->uOpenFlags = uOpenFlags;
1232
1233 pThis->pIfError = VDIfErrorGet(pThis->pVDIfsDisk);
1234 pThis->pIfIo = VDIfIoIntGet(pThis->pVDIfsImage);
1235 AssertPtrReturn(pThis->pIfIo, VERR_INVALID_PARAMETER);
1236
1237 /* Open the image. */
1238 int rc = vdIfIoIntFileOpen(pThis->pIfIo, pThis->pszFilename,
1239 VDOpenFlagsToFileOpenFlags(uOpenFlags,
1240 false /* fCreate */),
1241 &pThis->pStorage);
1242 if (RT_SUCCESS(rc))
1243 {
1244 uint64_t cbFile;
1245 /* The descriptor file shouldn't be huge, so limit ourselfs to 16KB for now. */
1246 rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorage, &cbFile);
1247 if ( RT_SUCCESS(rc)
1248 && cbFile <= _16K - 1)
1249 {
1250 char szInput[_16K];
1251 RT_ZERO(szInput);
1252
1253 rc = vdIfIoIntFileReadSync(pThis->pIfIo, pThis->pStorage, 0,
1254 &szInput, cbFile);
1255 if (RT_SUCCESS(rc))
1256 {
1257 RTStrPurgeEncoding(&szInput[0]);
1258 PCUETOKENIZER pTokenizer = cueTokenizerCreate(&szInput[0]);
1259 if (pTokenizer)
1260 {
1261 rc = cueParseFromTokenizer(pThis, pTokenizer);
1262 RTMemFree(pTokenizer);
1263 if (RT_SUCCESS(rc))
1264 {
1265 /* Open the backing file. */
1266 char szBackingFile[RTPATH_MAX];
1267 rc = RTStrCopy(&szBackingFile[0], sizeof(szBackingFile), pThis->pszFilename);
1268 if (RT_SUCCESS(rc))
1269 {
1270 RTPathStripFilename(&szBackingFile[0]);
1271 rc = RTPathAppend(&szBackingFile[0], sizeof(szBackingFile), pThis->pszDataFilename);
1272 if (RT_SUCCESS(rc))
1273 {
1274 rc = vdIfIoIntFileOpen(pThis->pIfIo, szBackingFile,
1275 VDOpenFlagsToFileOpenFlags(uOpenFlags,
1276 false /* fCreate */),
1277 &pThis->pStorageData);
1278 if (RT_SUCCESS(rc))
1279 {
1280 rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorageData, &cbFile);
1281 if (RT_SUCCESS(rc))
1282 rc = cueTrackListFinalize(pThis, cbFile);
1283 else
1284 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1285 N_("CUE: Unable to query size of backing file '%s'"),
1286 szBackingFile);
1287 }
1288 else
1289 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1290 N_("CUE: Unable to open backing file '%s'"),
1291 szBackingFile);
1292 }
1293 else
1294 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1295 N_("CUE: Error constructing backing filename from '%s'"),
1296 pThis->pszFilename);
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 }
1304 }
1305 else
1306 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS, N_("CUE: Error reading '%s'"), pThis->pszFilename);
1307 }
1308 else if (RT_SUCCESS(rc))
1309 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS, N_("CUE: The descriptor file '%s' is too huge (%llu vs %llu)"),
1310 pThis->pszFilename, cbFile, _16K - 1);
1311 }
1312 /* else: Do NOT signal an appropriate error here, as the VD layer has the
1313 * choice of retrying the open if it failed. */
1314
1315 if (RT_FAILURE(rc))
1316 cueFreeImage(pThis, false);
1317 return rc;
1318}
1319
1320/**
1321 * Converts the data form enumeration to a string.
1322 *
1323 * @returns String name of the given data form.
1324 * @param enmDataForm The data form.
1325 */
1326static const char *cueRegionDataFormStringify(VDREGIONDATAFORM enmDataForm)
1327{
1328 switch (enmDataForm)
1329 {
1330 #define DATAFORM2STR(tag) case VDREGIONDATAFORM_##tag: return #tag
1331
1332 DATAFORM2STR(INVALID);
1333 DATAFORM2STR(RAW);
1334 DATAFORM2STR(CDDA);
1335 DATAFORM2STR(CDDA_PAUSE);
1336 DATAFORM2STR(MODE1_2048);
1337 DATAFORM2STR(MODE1_2352);
1338 DATAFORM2STR(MODE1_0);
1339 DATAFORM2STR(XA_2336);
1340 DATAFORM2STR(XA_2352);
1341 DATAFORM2STR(XA_0);
1342 DATAFORM2STR(MODE2_2336);
1343 DATAFORM2STR(MODE2_2352);
1344 DATAFORM2STR(MODE2_0);
1345
1346 #undef DATAFORM2STR
1347
1348 default:
1349 {
1350 AssertMsgFailed(("Unknown data form %d! forgot to add it to the switch?\n", enmDataForm));
1351 return "UNKNOWN!";
1352 }
1353 }
1354}
1355
1356/**
1357 * Converts the data form enumeration to a string.
1358 *
1359 * @returns String name of the given data form.
1360 * @param enmMetadataForm The metadata form.
1361 */
1362static const char *cueRegionMetadataFormStringify(VDREGIONMETADATAFORM enmMetadataForm)
1363{
1364 switch (enmMetadataForm)
1365 {
1366 #define METADATAFORM2STR(tag) case VDREGIONMETADATAFORM_##tag: return #tag
1367
1368 METADATAFORM2STR(INVALID);
1369 METADATAFORM2STR(RAW);
1370 METADATAFORM2STR(NONE);
1371
1372 #undef METADATAFORM2STR
1373
1374 default:
1375 {
1376 AssertMsgFailed(("Unknown metadata form %d! forgot to add it to the switch?\n", enmMetadataForm));
1377 return "UNKNOWN!";
1378 }
1379 }
1380}
1381
1382/**
1383 * Returns the region containing the given offset.
1384 *
1385 * @returns Pointer to the region or NULL if not found.
1386 * @param pThis The CUE image state.
1387 * @param uOffset The offset to look for.
1388 */
1389static PCVDREGIONDESC cueRegionQueryByOffset(PCUEIMAGE pThis, uint64_t uOffset)
1390{
1391 for (uint32_t i = 0; i < pThis->pRegionList->cRegions; i++)
1392 {
1393 PCVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[i];
1394 if ( pRegion->offRegion <= uOffset
1395 && pRegion->offRegion + pRegion->cRegionBlocksOrBytes > uOffset)
1396 return pRegion;
1397 }
1398
1399 return NULL;
1400}
1401
1402/** @copydoc VDIMAGEBACKEND::pfnProbe */
1403static DECLCALLBACK(int) cueProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1404 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
1405{
1406 RT_NOREF1(pVDIfsDisk);
1407 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
1408 int rc = VINF_SUCCESS;
1409
1410 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
1411
1412 PCUEIMAGE pThis = (PCUEIMAGE)RTMemAllocZ(sizeof(CUEIMAGE));
1413 if (RT_LIKELY(pThis))
1414 {
1415 pThis->pszFilename = pszFilename;
1416 pThis->pStorage = NULL;
1417 pThis->pVDIfsDisk = pVDIfsDisk;
1418 pThis->pVDIfsImage = pVDIfsImage;
1419
1420 rc = cueOpenImage(pThis, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
1421 cueFreeImage(pThis, false);
1422 RTMemFree(pThis);
1423
1424 if (RT_SUCCESS(rc))
1425 *penmType = VDTYPE_DVD;
1426 else
1427 rc = VERR_VD_GEN_INVALID_HEADER;
1428 }
1429 else
1430 rc = VERR_NO_MEMORY;
1431
1432 LogFlowFunc(("returns %Rrc\n", rc));
1433 return rc;
1434}
1435
1436/** @copydoc VDIMAGEBACKEND::pfnOpen */
1437static DECLCALLBACK(int) cueOpen(const char *pszFilename, unsigned uOpenFlags,
1438 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1439 VDTYPE enmType, void **ppBackendData)
1440{
1441 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n",
1442 pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
1443 int rc;
1444 PCUEIMAGE pThis;
1445
1446 /* Check open flags. All valid flags are supported. */
1447 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
1448 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
1449 AssertReturn(enmType == VDTYPE_DVD, VERR_NOT_SUPPORTED);
1450
1451 pThis = (PCUEIMAGE)RTMemAllocZ(sizeof(CUEIMAGE));
1452 if (RT_LIKELY(pThis))
1453 {
1454 pThis->pszFilename = pszFilename;
1455 pThis->pStorage = NULL;
1456 pThis->pVDIfsDisk = pVDIfsDisk;
1457 pThis->pVDIfsImage = pVDIfsImage;
1458
1459 rc = cueOpenImage(pThis, uOpenFlags);
1460 if (RT_SUCCESS(rc))
1461 *ppBackendData = pThis;
1462 else
1463 RTMemFree(pThis);
1464 }
1465 else
1466 rc = VERR_NO_MEMORY;
1467
1468 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1469 return rc;
1470}
1471
1472/** @copydoc VDIMAGEBACKEND::pfnClose */
1473static DECLCALLBACK(int) cueClose(void *pBackendData, bool fDelete)
1474{
1475 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1476 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1477 int rc = cueFreeImage(pThis, fDelete);
1478 RTMemFree(pThis);
1479
1480 LogFlowFunc(("returns %Rrc\n", rc));
1481 return rc;
1482}
1483
1484/** @copydoc VDIMAGEBACKEND::pfnRead */
1485static DECLCALLBACK(int) cueRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1486 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1487{
1488 LogFlowFunc(("pBackendData=%#p uOffset=%llu cbToRead=%zu pIoCtx=%#p pcbActuallyRead=%#p\n",
1489 pBackendData, uOffset, cbToRead, pIoCtx, pcbActuallyRead));
1490 int rc = VINF_SUCCESS;
1491 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1492
1493 /* Get the region */
1494 PCVDREGIONDESC pRegion = cueRegionQueryByOffset(pThis, uOffset);
1495 if (pRegion)
1496 {
1497 /* Clip read size to remain in the region (not necessary I think). */
1498 uint64_t offRead = uOffset - pRegion->offRegion;
1499
1500 cbToRead = RT_MIN(cbToRead, pRegion->cRegionBlocksOrBytes - offRead);
1501 Assert(!(cbToRead % pRegion->cbBlock));
1502
1503 rc = vdIfIoIntFileReadUser(pThis->pIfIo, pThis->pStorageData, uOffset,
1504 pIoCtx, cbToRead);
1505 if (RT_SUCCESS(rc))
1506 *pcbActuallyRead = cbToRead;
1507 }
1508 else
1509 rc = VERR_INVALID_PARAMETER;
1510
1511 return rc;
1512}
1513
1514/** @copydoc VDIMAGEBACKEND::pfnWrite */
1515static DECLCALLBACK(int) cueWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
1516 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
1517 size_t *pcbPostRead, unsigned fWrite)
1518{
1519 RT_NOREF7(uOffset, cbToWrite, pIoCtx, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite);
1520 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
1521 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
1522 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1523 int rc;
1524
1525 AssertPtr(pThis);
1526
1527 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1528 rc = VERR_VD_IMAGE_READ_ONLY;
1529 else
1530 rc = VERR_NOT_SUPPORTED;
1531
1532 LogFlowFunc(("returns %Rrc\n", rc));
1533 return rc;
1534}
1535
1536/** @copydoc VDIMAGEBACKEND::pfnFlush */
1537static DECLCALLBACK(int) cueFlush(void *pBackendData, PVDIOCTX pIoCtx)
1538{
1539 RT_NOREF2(pBackendData, pIoCtx);
1540
1541 return VINF_SUCCESS;
1542}
1543
1544/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
1545static DECLCALLBACK(unsigned) cueGetVersion(void *pBackendData)
1546{
1547 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1548 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1549
1550 AssertPtrReturn(pThis, 0);
1551
1552 return 1;
1553}
1554
1555/** @copydoc VDIMAGEBACKEND::pfnGetSectorSize */
1556static DECLCALLBACK(uint32_t) cueGetSectorSize(void *pBackendData)
1557{
1558 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1559 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1560 uint32_t cb = 0;
1561
1562 AssertPtrReturn(pThis, 0);
1563
1564 LogFlowFunc(("returns %u\n", cb));
1565 return cb;
1566}
1567
1568/** @copydoc VDIMAGEBACKEND::pfnGetSize */
1569static DECLCALLBACK(uint64_t) cueGetSize(void *pBackendData)
1570{
1571 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1572 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1573 uint64_t cb = 0;
1574
1575 AssertPtrReturn(pThis, 0);
1576
1577 LogFlowFunc(("returns %llu\n", cb));
1578 return cb;
1579}
1580
1581/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
1582static DECLCALLBACK(uint64_t) cueGetFileSize(void *pBackendData)
1583{
1584 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1585 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1586
1587 AssertPtrReturn(pThis, 0);
1588
1589 uint64_t cbFile = 0;
1590 if (pThis->pStorage)
1591 {
1592 int rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorage, &cbFile);
1593 if (RT_FAILURE(rc))
1594 cbFile = 0; /* Make sure it is 0 */
1595 }
1596
1597 LogFlowFunc(("returns %lld\n", cbFile));
1598 return cbFile;
1599}
1600
1601/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
1602static DECLCALLBACK(int) cueGetPCHSGeometry(void *pBackendData,
1603 PVDGEOMETRY pPCHSGeometry)
1604{
1605 RT_NOREF1(pPCHSGeometry);
1606 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1607 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1608 int rc = VINF_SUCCESS;
1609
1610 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1611
1612 rc = VERR_NOT_SUPPORTED;
1613
1614 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1615 return rc;
1616}
1617
1618/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
1619static DECLCALLBACK(int) cueSetPCHSGeometry(void *pBackendData,
1620 PCVDGEOMETRY pPCHSGeometry)
1621{
1622 RT_NOREF1(pPCHSGeometry);
1623 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
1624 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1625 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1626 int rc = VINF_SUCCESS;
1627
1628 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1629
1630 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1631 rc = VERR_VD_IMAGE_READ_ONLY;
1632 else
1633 rc = VERR_NOT_SUPPORTED;
1634
1635 LogFlowFunc(("returns %Rrc\n", rc));
1636 return rc;
1637}
1638
1639/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
1640static DECLCALLBACK(int) cueGetLCHSGeometry(void *pBackendData,
1641 PVDGEOMETRY pLCHSGeometry)
1642{
1643 RT_NOREF1(pLCHSGeometry);
1644 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1645 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1646 int rc = VINF_SUCCESS;
1647
1648 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1649
1650 rc = VERR_NOT_SUPPORTED;
1651
1652 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1653 return rc;
1654}
1655
1656/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
1657static DECLCALLBACK(int) cueSetLCHSGeometry(void *pBackendData,
1658 PCVDGEOMETRY pLCHSGeometry)
1659{
1660 RT_NOREF1(pLCHSGeometry);
1661 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
1662 pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1663 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1664 int rc = VINF_SUCCESS;
1665
1666 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1667
1668 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1669 rc = VERR_VD_IMAGE_READ_ONLY;
1670 else
1671 rc = VERR_NOT_SUPPORTED;
1672
1673 LogFlowFunc(("returns %Rrc\n", rc));
1674 return rc;
1675}
1676
1677/** @copydoc VDIMAGEBACKEND::pfnQueryRegions */
1678static DECLCALLBACK(int) cueQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
1679{
1680 LogFlowFunc(("pBackendData=%#p ppRegionList=%#p\n", pBackendData, ppRegionList));
1681 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1682
1683 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1684
1685 *ppRegionList = pThis->pRegionList;
1686 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
1687 return VINF_SUCCESS;
1688}
1689
1690/** @copydoc VDIMAGEBACKEND::pfnRegionListRelease */
1691static DECLCALLBACK(void) cueRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
1692{
1693 RT_NOREF1(pRegionList);
1694 LogFlowFunc(("pBackendData=%#p pRegionList=%#p\n", pBackendData, pRegionList));
1695 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1696 AssertPtr(pThis); RT_NOREF(pThis);
1697
1698 /* Nothing to do here. */
1699}
1700
1701/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
1702static DECLCALLBACK(unsigned) cueGetImageFlags(void *pBackendData)
1703{
1704 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1705 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1706
1707 AssertPtrReturn(pThis, 0);
1708
1709 LogFlowFunc(("returns %#x\n", pThis->uImageFlags));
1710 return pThis->uImageFlags;
1711}
1712
1713/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
1714static DECLCALLBACK(unsigned) cueGetOpenFlags(void *pBackendData)
1715{
1716 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1717 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1718
1719 AssertPtrReturn(pThis, 0);
1720
1721 LogFlowFunc(("returns %#x\n", pThis->uOpenFlags));
1722 return pThis->uOpenFlags;
1723}
1724
1725/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
1726static DECLCALLBACK(int) cueSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
1727{
1728 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
1729 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1730 int rc = VINF_SUCCESS;
1731
1732 /* Image must be opened and the new flags must be valid. */
1733 if (!pThis || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
1734 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
1735 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
1736 rc = VERR_INVALID_PARAMETER;
1737 else
1738 {
1739 /* Implement this operation via reopening the image. */
1740 rc = cueFreeImage(pThis, false);
1741 if (RT_SUCCESS(rc))
1742 rc = cueOpenImage(pThis, uOpenFlags);
1743 }
1744
1745 LogFlowFunc(("returns %Rrc\n", rc));
1746 return rc;
1747}
1748
1749/** @copydoc VDIMAGEBACKEND::pfnGetComment */
1750static DECLCALLBACK(int) cueGetComment(void *pBackendData, char *pszComment,
1751 size_t cbComment)
1752{
1753 RT_NOREF2(pszComment, cbComment);
1754 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
1755 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1756
1757 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1758
1759 LogFlowFunc(("returns %Rrc comment='%s'\n", VERR_NOT_SUPPORTED, pszComment));
1760 return VERR_NOT_SUPPORTED;
1761}
1762
1763/** @copydoc VDIMAGEBACKEND::pfnSetComment */
1764static DECLCALLBACK(int) cueSetComment(void *pBackendData, const char *pszComment)
1765{
1766 RT_NOREF1(pszComment);
1767 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
1768 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1769
1770 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1771
1772 int rc;
1773 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1774 rc = VERR_VD_IMAGE_READ_ONLY;
1775 else
1776 rc = VERR_NOT_SUPPORTED;
1777
1778 LogFlowFunc(("returns %Rrc\n", rc));
1779 return rc;
1780}
1781
1782/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
1783static DECLCALLBACK(int) cueGetUuid(void *pBackendData, PRTUUID pUuid)
1784{
1785 RT_NOREF1(pUuid);
1786 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1787 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1788
1789 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1790
1791 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
1792 return VERR_NOT_SUPPORTED;
1793}
1794
1795/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
1796static DECLCALLBACK(int) cueSetUuid(void *pBackendData, PCRTUUID pUuid)
1797{
1798 RT_NOREF1(pUuid);
1799 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1800 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1801
1802 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1803
1804 int rc;
1805 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1806 rc = VERR_VD_IMAGE_READ_ONLY;
1807 else
1808 rc = VERR_NOT_SUPPORTED;
1809
1810 LogFlowFunc(("returns %Rrc\n", rc));
1811 return rc;
1812}
1813
1814/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
1815static DECLCALLBACK(int) cueGetModificationUuid(void *pBackendData, PRTUUID pUuid)
1816{
1817 RT_NOREF1(pUuid);
1818 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1819 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1820
1821 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1822
1823 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
1824 return VERR_NOT_SUPPORTED;
1825}
1826
1827/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
1828static DECLCALLBACK(int) cueSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
1829{
1830 RT_NOREF1(pUuid);
1831 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1832 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1833
1834 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1835
1836 int rc;
1837 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1838 rc = VERR_VD_IMAGE_READ_ONLY;
1839 else
1840 rc = VERR_NOT_SUPPORTED;
1841
1842 LogFlowFunc(("returns %Rrc\n", rc));
1843 return rc;
1844}
1845
1846/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
1847static DECLCALLBACK(int) cueGetParentUuid(void *pBackendData, PRTUUID pUuid)
1848{
1849 RT_NOREF1(pUuid);
1850 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1851 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1852
1853 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1854
1855 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
1856 return VERR_NOT_SUPPORTED;
1857}
1858
1859/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
1860static DECLCALLBACK(int) cueSetParentUuid(void *pBackendData, PCRTUUID pUuid)
1861{
1862 RT_NOREF1(pUuid);
1863 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1864 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1865
1866 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1867
1868 int rc;
1869 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1870 rc = VERR_VD_IMAGE_READ_ONLY;
1871 else
1872 rc = VERR_NOT_SUPPORTED;
1873
1874 LogFlowFunc(("returns %Rrc\n", rc));
1875 return rc;
1876}
1877
1878/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
1879static DECLCALLBACK(int) cueGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
1880{
1881 RT_NOREF1(pUuid);
1882 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1883 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1884
1885 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1886
1887 int rc;
1888 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1889 rc = VERR_VD_IMAGE_READ_ONLY;
1890 else
1891 rc = VERR_NOT_SUPPORTED;
1892
1893 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
1894 return rc;
1895}
1896
1897/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
1898static DECLCALLBACK(int) cueSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
1899{
1900 RT_NOREF1(pUuid);
1901 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1902 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1903
1904 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1905
1906 int rc;
1907 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1908 rc = VERR_VD_IMAGE_READ_ONLY;
1909 else
1910 rc = VERR_NOT_SUPPORTED;
1911
1912 LogFlowFunc(("returns %Rrc\n", rc));
1913 return rc;
1914}
1915
1916/** @copydoc VDIMAGEBACKEND::pfnDump */
1917static DECLCALLBACK(void) cueDump(void *pBackendData)
1918{
1919 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1920
1921 AssertPtrReturnVoid(pThis);
1922 vdIfErrorMessage(pThis->pIfError, "Dumping CUE image \"%s\" mode=%s uOpenFlags=%X File=%#p\n",
1923 pThis->pszFilename,
1924 (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
1925 pThis->uOpenFlags,
1926 pThis->pStorage);
1927 vdIfErrorMessage(pThis->pIfError, "Backing File \"%s\" File=%#p\n",
1928 pThis->pszDataFilename, pThis->pStorageData);
1929 vdIfErrorMessage(pThis->pIfError, "Number of tracks: %u\n", pThis->pRegionList->cRegions);
1930 for (uint32_t i = 0; i < pThis->pRegionList->cRegions; i++)
1931 {
1932 PCVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[i];
1933
1934 vdIfErrorMessage(pThis->pIfError, "------------------------ Track %u ------------------------\n", i);
1935 vdIfErrorMessage(pThis->pIfError, "Start=%llu Size=%llu BlockSize=%llu DataSize=%llu MetadataSize=%llu\n",
1936 pRegion->offRegion, pRegion->cRegionBlocksOrBytes, pRegion->cbBlock, pRegion->cbData,
1937 pRegion->cbMetadata);
1938 vdIfErrorMessage(pThis->pIfError, "DataForm=%s MetadataForm=%s\n",
1939 cueRegionDataFormStringify(pRegion->enmDataForm),
1940 cueRegionMetadataFormStringify(pRegion->enmMetadataForm));
1941 }
1942}
1943
1944
1945
1946const VDIMAGEBACKEND g_CueBackend =
1947{
1948 /* u32Version */
1949 VD_IMGBACKEND_VERSION,
1950 /* pszBackendName */
1951 "CUE",
1952 /* uBackendCaps */
1953 VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS,
1954 /* paFileExtensions */
1955 s_aCueFileExtensions,
1956 /* paConfigInfo */
1957 NULL,
1958 /* pfnProbe */
1959 cueProbe,
1960 /* pfnOpen */
1961 cueOpen,
1962 /* pfnCreate */
1963 NULL,
1964 /* pfnRename */
1965 NULL,
1966 /* pfnClose */
1967 cueClose,
1968 /* pfnRead */
1969 cueRead,
1970 /* pfnWrite */
1971 cueWrite,
1972 /* pfnFlush */
1973 cueFlush,
1974 /* pfnDiscard */
1975 NULL,
1976 /* pfnGetVersion */
1977 cueGetVersion,
1978 /* pfnGetSectorSize */
1979 cueGetSectorSize,
1980 /* pfnGetSize */
1981 cueGetSize,
1982 /* pfnGetFileSize */
1983 cueGetFileSize,
1984 /* pfnGetPCHSGeometry */
1985 cueGetPCHSGeometry,
1986 /* pfnSetPCHSGeometry */
1987 cueSetPCHSGeometry,
1988 /* pfnGetLCHSGeometry */
1989 cueGetLCHSGeometry,
1990 /* pfnSetLCHSGeometry */
1991 cueSetLCHSGeometry,
1992 /* pfnQueryRegions */
1993 cueQueryRegions,
1994 /* pfnRegionListRelease */
1995 cueRegionListRelease,
1996 /* pfnGetImageFlags */
1997 cueGetImageFlags,
1998 /* pfnGetOpenFlags */
1999 cueGetOpenFlags,
2000 /* pfnSetOpenFlags */
2001 cueSetOpenFlags,
2002 /* pfnGetComment */
2003 cueGetComment,
2004 /* pfnSetComment */
2005 cueSetComment,
2006 /* pfnGetUuid */
2007 cueGetUuid,
2008 /* pfnSetUuid */
2009 cueSetUuid,
2010 /* pfnGetModificationUuid */
2011 cueGetModificationUuid,
2012 /* pfnSetModificationUuid */
2013 cueSetModificationUuid,
2014 /* pfnGetParentUuid */
2015 cueGetParentUuid,
2016 /* pfnSetParentUuid */
2017 cueSetParentUuid,
2018 /* pfnGetParentModificationUuid */
2019 cueGetParentModificationUuid,
2020 /* pfnSetParentModificationUuid */
2021 cueSetParentModificationUuid,
2022 /* pfnDump */
2023 cueDump,
2024 /* pfnGetTimestamp */
2025 NULL,
2026 /* pfnGetParentTimestamp */
2027 NULL,
2028 /* pfnSetParentTimestamp */
2029 NULL,
2030 /* pfnGetParentFilename */
2031 NULL,
2032 /* pfnSetParentFilename */
2033 NULL,
2034 /* pfnComposeLocation */
2035 genericFileComposeLocation,
2036 /* pfnComposeName */
2037 genericFileComposeName,
2038 /* pfnCompact */
2039 NULL,
2040 /* pfnResize */
2041 NULL,
2042 /* pfnRepair */
2043 NULL,
2044 /* pfnTraverseMetadata */
2045 NULL,
2046 /* u32VersionEnd */
2047 VD_IMGBACKEND_VERSION
2048};
Note: See TracBrowser for help on using the repository browser.

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