VirtualBox

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

Last change on this file since 105006 was 99739, checked in by vboxsync, 19 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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