VirtualBox

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

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

Storage/CUE: build fixes (documentation)

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