VirtualBox

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

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

Storage: Add backend to handle CUE/BIN images (not yet working as the previous infrastructure changes need to be wired up to the CD/DVD drive emulations)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 66.4 KB
Line 
1/* $Id: CUE.cpp 66140 2017-03-16 17:29:31Z 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/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');
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 enmKey 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 enmKey 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"), pThis->pszFilename);
981 }
982 }
983 else
984 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
985 N_("CUE: Error parsing '%s', the data mode is not supported"), pThis->pszFilename);
986 }
987 else
988 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
989 N_("CUE: Error parsing '%s', expected data mode"), pThis->pszFilename);
990 }
991 else
992 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
993 N_("CUE: Error parsing '%s', track number must be between 01 and 99"), pThis->pszFilename);
994 }
995 else
996 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
997 N_("CUE: Error parsing '%s', expected track number after TRACK directive"), pThis->pszFilename);
998
999 return rc;
1000}
1001
1002/**
1003 * Parses a list of tracks which must come after a FILE directive.
1004 *
1005 * @returns VBox status code.
1006 * @param pThis The CUE image state.
1007 * @param pTokenizer The tokenizer state.
1008 */
1009static int cueParseTrackList(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer)
1010{
1011 int rc = VINF_SUCCESS;
1012
1013 while ( RT_SUCCESS(rc)
1014 && cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_TRACK))
1015 rc = cueParseTrack(pThis, pTokenizer);
1016
1017 return rc;
1018}
1019
1020/**
1021 * Parses the remainder of a FILE directive.
1022 *
1023 * @returns VBox status code.
1024 * @param pThis The CUE image state.
1025 * @param pTokenizer The tokenizer state.
1026 */
1027static int cueParseFile(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer)
1028{
1029 int rc = VINF_SUCCESS;
1030
1031 /* First must come a string constant followed by a keyword giving the file type. */
1032 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_STRING)
1033 {
1034 rc = cueTokenizerConsumeStringDup(pTokenizer, &pThis->pszDataFilename);
1035 if (RT_SUCCESS(rc))
1036 {
1037 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_KEYWORD)
1038 {
1039 if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_BINARY))
1040 rc = cueParseTrackList(pThis, pTokenizer);
1041 else
1042 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1043 N_("CUE: Error parsing '%s', the file type is not supported (only BINARY)"), pThis->pszFilename);
1044 }
1045 else
1046 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1047 N_("CUE: Error parsing '%s', expected file type"), pThis->pszFilename);
1048 }
1049 else
1050 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1051 N_("CUE: Error parsing '%s', failed to allocate memory for filename"), pThis->pszFilename);
1052 }
1053 else
1054 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1055 N_("CUE: Error parsing '%s', expected filename after FILE directive"), pThis->pszFilename);
1056
1057 return rc;
1058}
1059
1060/**
1061 * Parses the keyword in the given tokenizer.
1062 *
1063 * @returns VBox status code.
1064 * @param pThis The CUE image state.
1065 * @param pTokenizer The tokenizer state.
1066 */
1067static int cueParseKeyword(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer)
1068{
1069 int rc = VINF_SUCCESS;
1070
1071 if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_FILE))
1072 rc = cueParseFile(pThis, pTokenizer);
1073 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_TITLE))
1074 rc = cueParseAndSkipStringRemainder(pThis, pTokenizer, "TITLE");
1075 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_PERFORMER))
1076 rc = cueParseAndSkipStringRemainder(pThis, pTokenizer, "PERFORMER");
1077 else if (cueTokenizerSkipIfIsKeywordEqual(pTokenizer, CUEKEYWORD_SONGWRITER))
1078 rc = cueParseAndSkipStringRemainder(pThis, pTokenizer, "SONGWRITER");
1079 else /* Skip all other keywords we don't need/support. */
1080 cueTokenizerConsume(pTokenizer);
1081
1082 return rc;
1083}
1084
1085
1086/**
1087 * Parses the CUE sheet from the given tokenizer.
1088 *
1089 * @returns VBox status code.
1090 * @param pThis The CUE image state.
1091 * @param pTokenizer The tokenizer state.
1092 */
1093static int cueParseFromTokenizer(PCUEIMAGE pThis, PCUETOKENIZER pTokenizer)
1094{
1095 int rc = VINF_SUCCESS;
1096
1097 LogFlowFunc(("pThis=%p\n", pThis));
1098
1099 /* We don't support multiple FILE directives for now. */
1100 if (cueTokenizerGetTokenType(pTokenizer) == CUETOKENTYPE_KEYWORD)
1101 rc = cueParseKeyword(pThis, pTokenizer);
1102 else
1103 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1104 N_("CUE: Error parsing '%s', expected a keyword"), pThis->pszFilename);
1105
1106 if ( RT_SUCCESS(rc)
1107 && cueTokenizerGetTokenType(pTokenizer) != CUETOKENTYPE_EOS)
1108 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1109 N_("CUE: Error parsing '%s', expected end of stream"), pThis->pszFilename);
1110
1111 LogFlowFunc(("returns rc=%Rrc\n", rc));
1112 return rc;
1113}
1114
1115/**
1116 * Finalizes the track list of the image.
1117 *
1118 * @returns VBox status code.
1119 * @param pThis The CUE image state.
1120 * @param cbImage Size of the image data in bytes.
1121 */
1122static int cueTrackListFinalize(PCUEIMAGE pThis, uint64_t cbImage)
1123{
1124 int rc = VINF_SUCCESS;
1125
1126 if ( pThis->cTracksMax == 0
1127 || pThis->pRegionList->aRegions[0].offRegion == UINT64_MAX)
1128 return vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1129 N_("CUE: Error parsing '%s', detected empty track list"), pThis->pszFilename);
1130
1131 /*
1132 * Fixup the track list to contain the proper sizes now that we parsed all tracks,
1133 * check also that there are no gaps in the list.
1134 */
1135 uint32_t cTracks = 1;
1136 uint64_t offDisk = 0;
1137 for (uint32_t i = 1; i < pThis->cTracksMax; i++)
1138 {
1139 PVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[i];
1140 PVDREGIONDESC pRegionPrev = &pThis->pRegionList->aRegions[i - 1];
1141 if (pRegion->offRegion != UINT64_MAX)
1142 {
1143 cTracks++;
1144 pRegionPrev->cRegionBlocksOrBytes = pRegionPrev->cbBlock * pRegion->offRegion;
1145 offDisk += pRegionPrev->cRegionBlocksOrBytes;
1146
1147 if (cbImage < pRegionPrev->cRegionBlocksOrBytes)
1148 return vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1149 N_("CUE: Error parsing '%s', image file is too small for track list"),
1150 pThis->pszFilename);
1151
1152 cbImage -= pRegionPrev->cRegionBlocksOrBytes;
1153 pRegion->offRegion = offDisk;
1154 }
1155 else
1156 break;
1157 }
1158
1159 /* Fixup last track. */
1160 PVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[cTracks - 1];
1161 pRegion->cRegionBlocksOrBytes = cbImage;
1162
1163 pThis->pRegionList->cRegions = cTracks;
1164 pThis->pRegionList->fFlags = 0;
1165
1166 /* Check that there are no gaps in the track list. */
1167 for (uint32_t i = cTracks; cTracks < pThis->cTracksMax; i++)
1168 {
1169 pRegion = &pThis->pRegionList->aRegions[i];
1170 if (pRegion->offRegion != UINT64_MAX)
1171 {
1172 rc = vdIfError(pThis->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1173 N_("CUE: Error parsing '%s', detected gaps in the track list"), pThis->pszFilename);
1174 break;
1175 }
1176 }
1177
1178 return rc;
1179}
1180
1181/**
1182 * Internal. Free all allocated space for representing an image except pThis,
1183 * and optionally delete the image from disk.
1184 */
1185static int cueFreeImage(PCUEIMAGE pThis, bool fDelete)
1186{
1187 int rc = VINF_SUCCESS;
1188
1189 /* Freeing a never allocated image (e.g. because the open failed) is
1190 * not signalled as an error. After all nothing bad happens. */
1191 if (pThis)
1192 {
1193 if (pThis->pStorage)
1194 {
1195 rc = vdIfIoIntFileClose(pThis->pIfIo, pThis->pStorage);
1196 pThis->pStorage = NULL;
1197 }
1198
1199 if (pThis->pStorageData)
1200 {
1201 rc = vdIfIoIntFileClose(pThis->pIfIo, pThis->pStorageData);
1202 pThis->pStorageData = NULL;
1203 }
1204
1205 if (pThis->pRegionList)
1206 {
1207 RTMemFree(pThis->pRegionList);
1208 pThis->pRegionList = NULL;
1209 }
1210
1211 if (pThis->pszDataFilename)
1212 {
1213 RTStrFree(pThis->pszDataFilename);
1214 pThis->pszDataFilename = NULL;
1215 }
1216
1217 if (fDelete && pThis->pszFilename)
1218 vdIfIoIntFileDelete(pThis->pIfIo, pThis->pszFilename);
1219 }
1220
1221 LogFlowFunc(("returns %Rrc\n", rc));
1222 return rc;
1223}
1224
1225/**
1226 * Internal: Open an image, constructing all necessary data structures.
1227 */
1228static int cueOpenImage(PCUEIMAGE pThis, unsigned uOpenFlags)
1229{
1230 pThis->uOpenFlags = uOpenFlags;
1231
1232 pThis->pIfError = VDIfErrorGet(pThis->pVDIfsDisk);
1233 pThis->pIfIo = VDIfIoIntGet(pThis->pVDIfsImage);
1234 AssertPtrReturn(pThis->pIfIo, VERR_INVALID_PARAMETER);
1235
1236 /* Open the image. */
1237 int rc = vdIfIoIntFileOpen(pThis->pIfIo, pThis->pszFilename,
1238 VDOpenFlagsToFileOpenFlags(uOpenFlags,
1239 false /* fCreate */),
1240 &pThis->pStorage);
1241 if (RT_SUCCESS(rc))
1242 {
1243 uint64_t cbFile;
1244 /* The descriptor file shouldn't be huge, so limit ourselfs to 16KB for now. */
1245 rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorage, &cbFile);
1246 if ( RT_SUCCESS(rc)
1247 && cbFile <= _16K - 1)
1248 {
1249 char szInput[_16K];
1250 RT_ZERO(szInput);
1251
1252 rc = vdIfIoIntFileReadSync(pThis->pIfIo, pThis->pStorage, 0,
1253 &szInput, cbFile);
1254 if (RT_SUCCESS(rc))
1255 {
1256 RTStrPurgeEncoding(&szInput[0]);
1257 PCUETOKENIZER pTokenizer = cueTokenizerCreate(&szInput[0]);
1258 if (pTokenizer)
1259 {
1260 rc = cueParseFromTokenizer(pThis, pTokenizer);
1261 RTMemFree(pTokenizer);
1262 if (RT_SUCCESS(rc))
1263 {
1264 /* Open the backing file. */
1265 char szBackingFile[RTPATH_MAX];
1266 rc = RTStrCopy(&szBackingFile[0], sizeof(szBackingFile), pThis->pszFilename);
1267 if (RT_SUCCESS(rc))
1268 {
1269 RTPathStripFilename(&szBackingFile[0]);
1270 rc = RTPathAppend(&szBackingFile[0], sizeof(szBackingFile), pThis->pszDataFilename);
1271 if (RT_SUCCESS(rc))
1272 {
1273 rc = vdIfIoIntFileOpen(pThis->pIfIo, szBackingFile,
1274 VDOpenFlagsToFileOpenFlags(uOpenFlags,
1275 false /* fCreate */),
1276 &pThis->pStorageData);
1277 if (RT_SUCCESS(rc))
1278 {
1279 rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorageData, &cbFile);
1280 if (RT_SUCCESS(rc))
1281 rc = cueTrackListFinalize(pThis, cbFile);
1282 else
1283 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1284 N_("CUE: Unable to query size of backing file '%s'"),
1285 szBackingFile);
1286 }
1287 else
1288 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1289 N_("CUE: Unable to open backing file '%s'"),
1290 szBackingFile);
1291 }
1292 else
1293 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1294 N_("CUE: Error constructing backing filename from '%s'"),
1295 pThis->pszFilename);
1296 }
1297 else
1298 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS,
1299 N_("CUE: Error constructing backing filename from '%s'"),
1300 pThis->pszFilename);
1301 }
1302 }
1303 }
1304 else
1305 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS, N_("CUE: Error reading '%s'"), pThis->pszFilename);
1306 }
1307 else if (RT_SUCCESS(rc))
1308 rc = vdIfError(pThis->pIfError, rc, RT_SRC_POS, N_("CUE: The descriptor file '%s' is too huge (%llu vs %llu)"),
1309 pThis->pszFilename, cbFile, _16K - 1);
1310 }
1311 /* else: Do NOT signal an appropriate error here, as the VD layer has the
1312 * choice of retrying the open if it failed. */
1313
1314 if (RT_FAILURE(rc))
1315 cueFreeImage(pThis, false);
1316 return rc;
1317}
1318
1319/**
1320 * Converts the data form enumeration to a string.
1321 *
1322 * @returns String name of the given data form.
1323 * @param enmDataForm The data form.
1324 */
1325static const char *cueRegionDataFormStringify(VDREGIONDATAFORM enmDataForm)
1326{
1327 switch (enmDataForm)
1328 {
1329 #define DATAFORM2STR(tag) case VDREGIONDATAFORM_##tag: return #tag
1330
1331 DATAFORM2STR(INVALID);
1332 DATAFORM2STR(RAW);
1333 DATAFORM2STR(CDDA);
1334 DATAFORM2STR(CDDA_PAUSE);
1335 DATAFORM2STR(MODE1_2048);
1336 DATAFORM2STR(MODE1_2352);
1337 DATAFORM2STR(MODE1_0);
1338 DATAFORM2STR(XA_2336);
1339 DATAFORM2STR(XA_2352);
1340 DATAFORM2STR(XA_0);
1341 DATAFORM2STR(MODE2_2336);
1342 DATAFORM2STR(MODE2_2352);
1343 DATAFORM2STR(MODE2_0);
1344
1345 #undef DATAFORM2STR
1346
1347 default:
1348 {
1349 AssertMsgFailed(("Unknown data form %d! forgot to add it to the switch?\n", enmDataForm));
1350 return "UNKNOWN!";
1351 }
1352 }
1353}
1354
1355/**
1356 * Converts the data form enumeration to a string.
1357 *
1358 * @returns String name of the given data form.
1359 * @param enmDataForm The data form.
1360 */
1361static const char *cueRegionMetadataFormStringify(VDREGIONMETADATAFORM enmMetadataForm)
1362{
1363 switch (enmMetadataForm)
1364 {
1365 #define METADATAFORM2STR(tag) case VDREGIONMETADATAFORM_##tag: return #tag
1366
1367 METADATAFORM2STR(INVALID);
1368 METADATAFORM2STR(RAW);
1369 METADATAFORM2STR(NONE);
1370
1371 #undef METADATAFORM2STR
1372
1373 default:
1374 {
1375 AssertMsgFailed(("Unknown metadata form %d! forgot to add it to the switch?\n", enmMetadataForm));
1376 return "UNKNOWN!";
1377 }
1378 }
1379}
1380
1381/**
1382 * Returns the region containing the given offset.
1383 *
1384 * @returns Pointer to the region or NULL if not found.
1385 * @param pThis The CUE image state.
1386 * @param uOffset The offset to look for.
1387 */
1388static PCVDREGIONDESC cueRegionQueryByOffset(PCUEIMAGE pThis, uint64_t uOffset)
1389{
1390 for (uint32_t i = 0; i < pThis->pRegionList->cRegions; i++)
1391 {
1392 PCVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[i];
1393 if ( pRegion->offRegion <= uOffset
1394 && pRegion->offRegion + pRegion->cRegionBlocksOrBytes > uOffset)
1395 return pRegion;
1396 }
1397
1398 return NULL;
1399}
1400
1401/** @copydoc VDIMAGEBACKEND::pfnProbe */
1402static DECLCALLBACK(int) cueProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1403 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
1404{
1405 RT_NOREF1(pVDIfsDisk);
1406 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
1407 int rc = VINF_SUCCESS;
1408
1409 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
1410
1411 PCUEIMAGE pThis = (PCUEIMAGE)RTMemAllocZ(sizeof(CUEIMAGE));
1412 if (RT_LIKELY(pThis))
1413 {
1414 pThis->pszFilename = pszFilename;
1415 pThis->pStorage = NULL;
1416 pThis->pVDIfsDisk = pVDIfsDisk;
1417 pThis->pVDIfsImage = pVDIfsImage;
1418
1419 rc = cueOpenImage(pThis, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
1420 cueFreeImage(pThis, false);
1421 RTMemFree(pThis);
1422
1423 if (RT_SUCCESS(rc))
1424 *penmType = VDTYPE_DVD;
1425 else
1426 rc = VERR_VD_GEN_INVALID_HEADER;
1427 }
1428 else
1429 rc = VERR_NO_MEMORY;
1430
1431 LogFlowFunc(("returns %Rrc\n", rc));
1432 return rc;
1433}
1434
1435/** @copydoc VDIMAGEBACKEND::pfnOpen */
1436static DECLCALLBACK(int) cueOpen(const char *pszFilename, unsigned uOpenFlags,
1437 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1438 VDTYPE enmType, void **ppBackendData)
1439{
1440 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n",
1441 pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
1442 int rc;
1443 PCUEIMAGE pThis;
1444
1445 /* Check open flags. All valid flags are supported. */
1446 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
1447 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
1448 AssertReturn(enmType == VDTYPE_DVD, VERR_NOT_SUPPORTED);
1449
1450 pThis = (PCUEIMAGE)RTMemAllocZ(sizeof(CUEIMAGE));
1451 if (RT_LIKELY(pThis))
1452 {
1453 pThis->pszFilename = pszFilename;
1454 pThis->pStorage = NULL;
1455 pThis->pVDIfsDisk = pVDIfsDisk;
1456 pThis->pVDIfsImage = pVDIfsImage;
1457
1458 rc = cueOpenImage(pThis, uOpenFlags);
1459 if (RT_SUCCESS(rc))
1460 *ppBackendData = pThis;
1461 else
1462 RTMemFree(pThis);
1463 }
1464 else
1465 rc = VERR_NO_MEMORY;
1466
1467 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1468 return rc;
1469}
1470
1471/** @copydoc VDIMAGEBACKEND::pfnClose */
1472static DECLCALLBACK(int) cueClose(void *pBackendData, bool fDelete)
1473{
1474 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1475 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1476 int rc = cueFreeImage(pThis, fDelete);
1477 RTMemFree(pThis);
1478
1479 LogFlowFunc(("returns %Rrc\n", rc));
1480 return rc;
1481}
1482
1483/** @copydoc VDIMAGEBACKEND::pfnRead */
1484static DECLCALLBACK(int) cueRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1485 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1486{
1487 LogFlowFunc(("pBackendData=%#p uOffset=%llu cbToRead=%zu pIoCtx=%#p pcbActuallyRead=%#p\n",
1488 pBackendData, uOffset, cbToRead, pIoCtx, pcbActuallyRead));
1489 int rc = VINF_SUCCESS;
1490 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1491
1492 /* Get the region */
1493 PCVDREGIONDESC pRegion = cueRegionQueryByOffset(pThis, uOffset);
1494 if (pRegion)
1495 {
1496 /* Clip read size to remain in the region (not necessary I think). */
1497 uint64_t offRead = uOffset - pRegion->offRegion;
1498
1499 cbToRead = RT_MIN(cbToRead, pRegion->cRegionBlocksOrBytes - offRead);
1500 Assert(!(cbToRead % pRegion->cbBlock));
1501
1502 rc = vdIfIoIntFileReadUser(pThis->pIfIo, pThis->pStorageData, uOffset,
1503 pIoCtx, cbToRead);
1504 if (RT_SUCCESS(rc))
1505 *pcbActuallyRead = cbToRead;
1506 }
1507 else
1508 rc = VERR_INVALID_PARAMETER;
1509
1510 return rc;
1511}
1512
1513/** @copydoc VDIMAGEBACKEND::pfnWrite */
1514static DECLCALLBACK(int) cueWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
1515 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
1516 size_t *pcbPostRead, unsigned fWrite)
1517{
1518 RT_NOREF5(pIoCtx, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite);
1519 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
1520 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
1521 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1522 int rc;
1523
1524 AssertPtr(pThis);
1525
1526 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1527 rc = VERR_VD_IMAGE_READ_ONLY;
1528 else
1529 rc = VERR_NOT_SUPPORTED;
1530
1531 LogFlowFunc(("returns %Rrc\n", rc));
1532 return rc;
1533}
1534
1535/** @copydoc VDIMAGEBACKEND::pfnFlush */
1536static DECLCALLBACK(int) cueFlush(void *pBackendData, PVDIOCTX pIoCtx)
1537{
1538 RT_NOREF2(pBackendData, pIoCtx);
1539
1540 return VINF_SUCCESS;
1541}
1542
1543/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
1544static DECLCALLBACK(unsigned) cueGetVersion(void *pBackendData)
1545{
1546 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1547 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1548
1549 AssertPtrReturn(pThis, 0);
1550
1551 return 1;
1552}
1553
1554/** @copydoc VDIMAGEBACKEND::pfnGetSectorSize */
1555static DECLCALLBACK(uint32_t) cueGetSectorSize(void *pBackendData)
1556{
1557 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1558 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1559 uint32_t cb = 0;
1560
1561 AssertPtrReturn(pThis, 0);
1562
1563 LogFlowFunc(("returns %u\n", cb));
1564 return cb;
1565}
1566
1567/** @copydoc VDIMAGEBACKEND::pfnGetSize */
1568static DECLCALLBACK(uint64_t) cueGetSize(void *pBackendData)
1569{
1570 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1571 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1572 uint64_t cb = 0;
1573
1574 AssertPtrReturn(pThis, 0);
1575
1576 LogFlowFunc(("returns %llu\n", cb));
1577 return cb;
1578}
1579
1580/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
1581static DECLCALLBACK(uint64_t) cueGetFileSize(void *pBackendData)
1582{
1583 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1584 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1585
1586 AssertPtrReturn(pThis, 0);
1587
1588 uint64_t cbFile = 0;
1589 if (pThis->pStorage)
1590 {
1591 int rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorage, &cbFile);
1592 if (RT_FAILURE(rc))
1593 cbFile = 0; /* Make sure it is 0 */
1594 }
1595
1596 LogFlowFunc(("returns %lld\n", cbFile));
1597 return cbFile;
1598}
1599
1600/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
1601static DECLCALLBACK(int) cueGetPCHSGeometry(void *pBackendData,
1602 PVDGEOMETRY pPCHSGeometry)
1603{
1604 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1605 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1606 int rc = VINF_SUCCESS;
1607
1608 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1609
1610 rc = VERR_NOT_SUPPORTED;
1611
1612 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1613 return rc;
1614}
1615
1616/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
1617static DECLCALLBACK(int) cueSetPCHSGeometry(void *pBackendData,
1618 PCVDGEOMETRY pPCHSGeometry)
1619{
1620 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
1621 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1622 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1623 int rc = VINF_SUCCESS;
1624
1625 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1626
1627 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1628 rc = VERR_VD_IMAGE_READ_ONLY;
1629 else
1630 rc = VERR_NOT_SUPPORTED;
1631
1632 LogFlowFunc(("returns %Rrc\n", rc));
1633 return rc;
1634}
1635
1636/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
1637static DECLCALLBACK(int) cueGetLCHSGeometry(void *pBackendData,
1638 PVDGEOMETRY pLCHSGeometry)
1639{
1640 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1641 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1642 int rc = VINF_SUCCESS;
1643
1644 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1645
1646 rc = VERR_NOT_SUPPORTED;
1647
1648 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1649 return rc;
1650}
1651
1652/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
1653static DECLCALLBACK(int) cueSetLCHSGeometry(void *pBackendData,
1654 PCVDGEOMETRY pLCHSGeometry)
1655{
1656 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
1657 pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1658 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1659 int rc = VINF_SUCCESS;
1660
1661 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1662
1663 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1664 rc = VERR_VD_IMAGE_READ_ONLY;
1665 else
1666 rc = VERR_NOT_SUPPORTED;
1667
1668 LogFlowFunc(("returns %Rrc\n", rc));
1669 return rc;
1670}
1671
1672/** @copydoc VDIMAGEBACKEND::pfnQueryRegions */
1673static DECLCALLBACK(int) cueQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
1674{
1675 LogFlowFunc(("pBackendData=%#p ppRegionList=%#p\n", pBackendData, ppRegionList));
1676 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1677
1678 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1679
1680 *ppRegionList = pThis->pRegionList;
1681 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
1682 return VINF_SUCCESS;
1683}
1684
1685/** @copydoc VDIMAGEBACKEND::pfnRegionListRelease */
1686static DECLCALLBACK(void) cueRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
1687{
1688 RT_NOREF1(pRegionList);
1689 LogFlowFunc(("pBackendData=%#p pRegionList=%#p\n", pBackendData, pRegionList));
1690 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1691 AssertPtr(pThis); RT_NOREF(pThis);
1692
1693 /* Nothing to do here. */
1694}
1695
1696/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
1697static DECLCALLBACK(unsigned) cueGetImageFlags(void *pBackendData)
1698{
1699 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1700 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1701
1702 AssertPtrReturn(pThis, 0);
1703
1704 LogFlowFunc(("returns %#x\n", pThis->uImageFlags));
1705 return pThis->uImageFlags;
1706}
1707
1708/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
1709static DECLCALLBACK(unsigned) cueGetOpenFlags(void *pBackendData)
1710{
1711 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1712 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1713
1714 AssertPtrReturn(pThis, 0);
1715
1716 LogFlowFunc(("returns %#x\n", pThis->uOpenFlags));
1717 return pThis->uOpenFlags;
1718}
1719
1720/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
1721static DECLCALLBACK(int) cueSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
1722{
1723 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
1724 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1725 int rc = VINF_SUCCESS;
1726
1727 /* Image must be opened and the new flags must be valid. */
1728 if (!pThis || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
1729 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
1730 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
1731 rc = VERR_INVALID_PARAMETER;
1732 else
1733 {
1734 /* Implement this operation via reopening the image. */
1735 rc = cueFreeImage(pThis, false);
1736 if (RT_SUCCESS(rc))
1737 rc = cueOpenImage(pThis, uOpenFlags);
1738 }
1739
1740 LogFlowFunc(("returns %Rrc\n", rc));
1741 return rc;
1742}
1743
1744/** @copydoc VDIMAGEBACKEND::pfnGetComment */
1745static DECLCALLBACK(int) cueGetComment(void *pBackendData, char *pszComment,
1746 size_t cbComment)
1747{
1748 RT_NOREF2(pszComment, cbComment);
1749 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
1750 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1751
1752 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1753
1754 LogFlowFunc(("returns %Rrc comment='%s'\n", VERR_NOT_SUPPORTED, pszComment));
1755 return VERR_NOT_SUPPORTED;
1756}
1757
1758/** @copydoc VDIMAGEBACKEND::pfnSetComment */
1759static DECLCALLBACK(int) cueSetComment(void *pBackendData, const char *pszComment)
1760{
1761 RT_NOREF1(pszComment);
1762 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
1763 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1764
1765 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1766
1767 int rc;
1768 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1769 rc = VERR_VD_IMAGE_READ_ONLY;
1770 else
1771 rc = VERR_NOT_SUPPORTED;
1772
1773 LogFlowFunc(("returns %Rrc\n", rc));
1774 return rc;
1775}
1776
1777/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
1778static DECLCALLBACK(int) cueGetUuid(void *pBackendData, PRTUUID pUuid)
1779{
1780 RT_NOREF1(pUuid);
1781 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1782 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1783
1784 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1785
1786 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
1787 return VERR_NOT_SUPPORTED;
1788}
1789
1790/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
1791static DECLCALLBACK(int) cueSetUuid(void *pBackendData, PCRTUUID pUuid)
1792{
1793 RT_NOREF1(pUuid);
1794 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1795 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1796
1797 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1798
1799 int rc;
1800 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1801 rc = VERR_VD_IMAGE_READ_ONLY;
1802 else
1803 rc = VERR_NOT_SUPPORTED;
1804
1805 LogFlowFunc(("returns %Rrc\n", rc));
1806 return rc;
1807}
1808
1809/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
1810static DECLCALLBACK(int) cueGetModificationUuid(void *pBackendData, PRTUUID pUuid)
1811{
1812 RT_NOREF1(pUuid);
1813 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1814 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1815
1816 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1817
1818 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
1819 return VERR_NOT_SUPPORTED;
1820}
1821
1822/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
1823static DECLCALLBACK(int) cueSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
1824{
1825 RT_NOREF1(pUuid);
1826 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1827 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1828
1829 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1830
1831 int rc;
1832 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1833 rc = VERR_VD_IMAGE_READ_ONLY;
1834 else
1835 rc = VERR_NOT_SUPPORTED;
1836
1837 LogFlowFunc(("returns %Rrc\n", rc));
1838 return rc;
1839}
1840
1841/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
1842static DECLCALLBACK(int) cueGetParentUuid(void *pBackendData, PRTUUID pUuid)
1843{
1844 RT_NOREF1(pUuid);
1845 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1846 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1847
1848 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1849
1850 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
1851 return VERR_NOT_SUPPORTED;
1852}
1853
1854/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
1855static DECLCALLBACK(int) cueSetParentUuid(void *pBackendData, PCRTUUID pUuid)
1856{
1857 RT_NOREF1(pUuid);
1858 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1859 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1860
1861 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1862
1863 int rc;
1864 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1865 rc = VERR_VD_IMAGE_READ_ONLY;
1866 else
1867 rc = VERR_NOT_SUPPORTED;
1868
1869 LogFlowFunc(("returns %Rrc\n", rc));
1870 return rc;
1871}
1872
1873/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
1874static DECLCALLBACK(int) cueGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
1875{
1876 RT_NOREF1(pUuid);
1877 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1878 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1879
1880 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1881
1882 int rc;
1883 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1884 rc = VERR_VD_IMAGE_READ_ONLY;
1885 else
1886 rc = VERR_NOT_SUPPORTED;
1887
1888 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
1889 return rc;
1890}
1891
1892/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
1893static DECLCALLBACK(int) cueSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
1894{
1895 RT_NOREF1(pUuid);
1896 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1897 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1898
1899 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1900
1901 int rc;
1902 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1903 rc = VERR_VD_IMAGE_READ_ONLY;
1904 else
1905 rc = VERR_NOT_SUPPORTED;
1906
1907 LogFlowFunc(("returns %Rrc\n", rc));
1908 return rc;
1909}
1910
1911/** @copydoc VDIMAGEBACKEND::pfnDump */
1912static DECLCALLBACK(void) cueDump(void *pBackendData)
1913{
1914 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1915
1916 AssertPtrReturnVoid(pThis);
1917 vdIfErrorMessage(pThis->pIfError, "Dumping CUE image \"%s\" mode=%s uOpenFlags=%X File=%#p\n",
1918 pThis->pszFilename,
1919 (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
1920 pThis->uOpenFlags,
1921 pThis->pStorage);
1922 vdIfErrorMessage(pThis->pIfError, "Backing File \"%s\" File=%#p\n",
1923 pThis->pszDataFilename, pThis->pStorageData);
1924 vdIfErrorMessage(pThis->pIfError, "Number of tracks: %u\n", pThis->pRegionList->cRegions);
1925 for (uint32_t i = 0; i < pThis->pRegionList->cRegions; i++)
1926 {
1927 PCVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[i];
1928
1929 vdIfErrorMessage(pThis->pIfError, "------------------------ Track %u ------------------------\n", i);
1930 vdIfErrorMessage(pThis->pIfError, "Start=%llu Size=%llu BlockSize=%llu DataSize=%llu MetadataSize=%llu\n",
1931 pRegion->offRegion, pRegion->cRegionBlocksOrBytes, pRegion->cbBlock, pRegion->cbData,
1932 pRegion->cbMetadata);
1933 vdIfErrorMessage(pThis->pIfError, "DataForm=%s MetadataForm=%s\n",
1934 cueRegionDataFormStringify(pRegion->enmDataForm),
1935 cueRegionMetadataFormStringify(pRegion->enmMetadataForm));
1936 }
1937}
1938
1939
1940
1941const VDIMAGEBACKEND g_CueBackend =
1942{
1943 /* u32Version */
1944 VD_IMGBACKEND_VERSION,
1945 /* pszBackendName */
1946 "CUE",
1947 /* uBackendCaps */
1948 VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS,
1949 /* paFileExtensions */
1950 s_aCueFileExtensions,
1951 /* paConfigInfo */
1952 NULL,
1953 /* pfnProbe */
1954 cueProbe,
1955 /* pfnOpen */
1956 cueOpen,
1957 /* pfnCreate */
1958 NULL,
1959 /* pfnRename */
1960 NULL,
1961 /* pfnClose */
1962 cueClose,
1963 /* pfnRead */
1964 cueRead,
1965 /* pfnWrite */
1966 cueWrite,
1967 /* pfnFlush */
1968 cueFlush,
1969 /* pfnDiscard */
1970 NULL,
1971 /* pfnGetVersion */
1972 cueGetVersion,
1973 /* pfnGetSectorSize */
1974 cueGetSectorSize,
1975 /* pfnGetSize */
1976 cueGetSize,
1977 /* pfnGetFileSize */
1978 cueGetFileSize,
1979 /* pfnGetPCHSGeometry */
1980 cueGetPCHSGeometry,
1981 /* pfnSetPCHSGeometry */
1982 cueSetPCHSGeometry,
1983 /* pfnGetLCHSGeometry */
1984 cueGetLCHSGeometry,
1985 /* pfnSetLCHSGeometry */
1986 cueSetLCHSGeometry,
1987 /* pfnQueryRegions */
1988 cueQueryRegions,
1989 /* pfnRegionListRelease */
1990 cueRegionListRelease,
1991 /* pfnGetImageFlags */
1992 cueGetImageFlags,
1993 /* pfnGetOpenFlags */
1994 cueGetOpenFlags,
1995 /* pfnSetOpenFlags */
1996 cueSetOpenFlags,
1997 /* pfnGetComment */
1998 cueGetComment,
1999 /* pfnSetComment */
2000 cueSetComment,
2001 /* pfnGetUuid */
2002 cueGetUuid,
2003 /* pfnSetUuid */
2004 cueSetUuid,
2005 /* pfnGetModificationUuid */
2006 cueGetModificationUuid,
2007 /* pfnSetModificationUuid */
2008 cueSetModificationUuid,
2009 /* pfnGetParentUuid */
2010 cueGetParentUuid,
2011 /* pfnSetParentUuid */
2012 cueSetParentUuid,
2013 /* pfnGetParentModificationUuid */
2014 cueGetParentModificationUuid,
2015 /* pfnSetParentModificationUuid */
2016 cueSetParentModificationUuid,
2017 /* pfnDump */
2018 cueDump,
2019 /* pfnGetTimestamp */
2020 NULL,
2021 /* pfnGetParentTimestamp */
2022 NULL,
2023 /* pfnSetParentTimestamp */
2024 NULL,
2025 /* pfnGetParentFilename */
2026 NULL,
2027 /* pfnSetParentFilename */
2028 NULL,
2029 /* pfnComposeLocation */
2030 genericFileComposeLocation,
2031 /* pfnComposeName */
2032 genericFileComposeName,
2033 /* pfnCompact */
2034 NULL,
2035 /* pfnResize */
2036 NULL,
2037 /* pfnRepair */
2038 NULL,
2039 /* pfnTraverseMetadata */
2040 NULL,
2041 /* u32VersionEnd */
2042 VD_IMGBACKEND_VERSION
2043};
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