VirtualBox

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

Last change on this file since 90778 was 90296, checked in by vboxsync, 3 years ago

Storage/CUE: Fix comment, CDDA audio samples are stored in little endian actually

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.1 KB
Line 
1/* $Id: CUE.cpp 90296 2021-07-22 19:25:57Z vboxsync $ */
2/** @file
3 * CUE - CUE/BIN Disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2017-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VD_CUE
23#include <VBox/vd-plugin.h>
24#include <VBox/err.h>
25
26#include <VBox/log.h>
27#include <VBox/scsiinline.h>
28#include <iprt/assert.h>
29#include <iprt/asm.h>
30#include <iprt/alloc.h>
31#include <iprt/cdefs.h>
32#include <iprt/ctype.h>
33#include <iprt/path.h>
34#include <iprt/string.h>
35
36#include "VDBackends.h"
37#include "VDBackendsInline.h"
38
39
40/*********************************************************************************************************************************
41* Constants And Macros, Structures and Typedefs *
42*********************************************************************************************************************************/
43
44/**
45 * CUE descriptor file token type.
46 */
47typedef enum CUETOKENTYPE
48{
49 /** Invalid token type. */
50 CUETOKENTYPE_INVALID = 0,
51 /** Reserved keyword. */
52 CUETOKENTYPE_KEYWORD,
53 /** String token. */
54 CUETOKENTYPE_STRING,
55 /** Unsigned integer. */
56 CUETOKENTYPE_INTEGER_UNSIGNED,
57 /** MSF (mm:ss:ff) location token. */
58 CUETOKENTYPE_MSF,
59 /** Error token (unexpected character found). */
60 CUETOKENTYPE_ERROR,
61 /** End of stream token. */
62 CUETOKENTYPE_EOS
63} CUETOKENTYPE;
64
65/**
66 * CUE reservered keyword type.
67 */
68typedef enum CUEKEYWORD
69{
70 /** Invalid keyword. */
71 CUEKEYWORD_INVALID = 0,
72 /** FILE. */
73 CUEKEYWORD_FILE,
74 /** BINARY */
75 CUEKEYWORD_BINARY,
76 /** MOTOROLA */
77 CUEKEYWORD_MOTOROLA,
78 /** WAVE */
79 CUEKEYWORD_WAVE,
80 /** MP3 */
81 CUEKEYWORD_MP3,
82 /** AIFF */
83 CUEKEYWORD_AIFF,
84 /** CATALOG */
85 CUEKEYWORD_CATALOG,
86 CUEKEYWORD_CDTEXTFILE,
87 CUEKEYWORD_FLAGS,
88 CUEKEYWORD_INDEX,
89 CUEKEYWORD_ISRC,
90 CUEKEYWORD_PERFORMER,
91 CUEKEYWORD_POSTGAP,
92 CUEKEYWORD_PREGAP,
93 CUEKEYWORD_SONGWRITER,
94 CUEKEYWORD_TITLE,
95 CUEKEYWORD_TRACK,
96 CUEKEYWORD_MODE1_2048,
97 CUEKEYWORD_MODE1_2352,
98 CUEKEYWORD_MODE2_2352,
99 CUEKEYWORD_AUDIO,
100 CUEKEYWORD_REM
101} CUEKEYWORD;
102
103/**
104 * CUE sheet token.
105 */
106typedef struct CUETOKEN
107{
108 /** The token type. */
109 CUETOKENTYPE enmType;
110 /** Token type dependent data. */
111 union
112 {
113 /** Keyword token. */
114 struct
115 {
116 /** The keyword enumerator. */
117 CUEKEYWORD enmKeyword;
118 } Keyword;
119 /** String token (without quotation marks). */
120 struct
121 {
122 /** Pointer to the start of the string. */
123 const char *psz;
124 /** Number of characters for the string excluding the null terminator. */
125 size_t cch;
126 } String;
127 /** Integer token. */
128 struct
129 {
130 /** Numerical constant. */
131 uint64_t u64;
132 } Int;
133 /** MSF location token. */
134 struct
135 {
136 /** Minute part. */
137 uint8_t u8Minute;
138 /** Second part. */
139 uint8_t u8Second;
140 /** Frame part. */
141 uint8_t u8Frame;
142 } Msf;
143 } Type;
144} CUETOKEN;
145/** Pointer to a CUE sheet token. */
146typedef CUETOKEN *PCUETOKEN;
147/** Pointer to a const CUE sheet token. */
148typedef const CUETOKEN *PCCUETOKEN;
149
150/**
151 * CUE tokenizer state.
152 */
153typedef struct CUETOKENIZER
154{
155 /** Char buffer to read from. */
156 const char *pszInput;
157 /** Token 1. */
158 CUETOKEN Token1;
159 /** Token 2. */
160 CUETOKEN Token2;
161 /** Pointer to the current active token. */
162 PCUETOKEN pTokenCurr;
163 /** The next token in the input stream (used for peeking). */
164 PCUETOKEN pTokenNext;
165} CUETOKENIZER;
166/** Pointer to a CUE tokenizer state. */
167typedef CUETOKENIZER *PCUETOKENIZER;
168
169/**
170 * CUE keyword entry.
171 */
172typedef struct CUEKEYWORDDESC
173{
174 /** Keyword string. */
175 const char *pszKeyword;
176 /** Size of the string in characters without zero terminator. */
177 size_t cchKeyword;
178 /** Keyword type. */
179 CUEKEYWORD enmKeyword;
180} CUEKEYWORDDESC;
181/** Pointer to a CUE keyword entry. */
182typedef CUEKEYWORDDESC *PCUEKEYWORDDESC;
183/** Pointer to a const CUE keyword entry. */
184typedef const CUEKEYWORDDESC *PCCUEKEYWORDDESC;
185
186/**
187 * CUE image data structure.
188 */
189typedef struct CUEIMAGE
190{
191 /** Image name. */
192 const char *pszFilename;
193 /** Storage handle. */
194 PVDIOSTORAGE pStorage;
195 /** The backing file containing the actual data. */
196 char *pszDataFilename;
197 /** Storage handle for the backing file. */
198 PVDIOSTORAGE pStorageData;
199
200 /** Pointer to the per-disk VD interface list. */
201 PVDINTERFACE pVDIfsDisk;
202 /** Pointer to the per-image VD interface list. */
203 PVDINTERFACE pVDIfsImage;
204 /** Error interface. */
205 PVDINTERFACEERROR pIfError;
206 /** I/O interface. */
207 PVDINTERFACEIOINT pIfIo;
208
209 /** Open flags passed by VD layer. */
210 unsigned uOpenFlags;
211 /** Image flags defined during creation or determined during open. */
212 unsigned uImageFlags;
213 /** Maximum number of tracks the region list can hold. */
214 uint32_t cTracksMax;
215 /** Pointer to our internal region list. */
216 PVDREGIONLIST pRegionList;
217 /** Flag whether the backing file is little (BINARY) or big (MOTOROLA) endian. */
218 bool fLittleEndian;
219} CUEIMAGE, *PCUEIMAGE;
220
221
222/*********************************************************************************************************************************
223* Static Variables *
224*********************************************************************************************************************************/
225
226/** NULL-terminated array of supported file extensions. */
227static const VDFILEEXTENSION s_aCueFileExtensions[] =
228{
229 {"cue", VDTYPE_OPTICAL_DISC},
230 {NULL, VDTYPE_INVALID}
231};
232
233/**
234 * Known keywords.
235 */
236static const CUEKEYWORDDESC g_aCueKeywords[] =
237{
238 {RT_STR_TUPLE("FILE"), CUEKEYWORD_FILE},
239 {RT_STR_TUPLE("BINARY"), CUEKEYWORD_BINARY},
240 {RT_STR_TUPLE("MOTOROLA"), CUEKEYWORD_MOTOROLA},
241 {RT_STR_TUPLE("WAVE"), CUEKEYWORD_WAVE},
242 {RT_STR_TUPLE("MP3"), CUEKEYWORD_MP3},
243 {RT_STR_TUPLE("AIFF"), CUEKEYWORD_AIFF},
244 {RT_STR_TUPLE("CATALOG"), CUEKEYWORD_CATALOG},
245 {RT_STR_TUPLE("CDTEXTFILE"), CUEKEYWORD_CDTEXTFILE},
246 {RT_STR_TUPLE("FLAGS"), CUEKEYWORD_FLAGS},
247 {RT_STR_TUPLE("INDEX"), CUEKEYWORD_INDEX},
248 {RT_STR_TUPLE("ISRC"), CUEKEYWORD_ISRC},
249 {RT_STR_TUPLE("PERFORMER"), CUEKEYWORD_PERFORMER},
250 {RT_STR_TUPLE("POSTGAP"), CUEKEYWORD_POSTGAP},
251 {RT_STR_TUPLE("PREGAP"), CUEKEYWORD_PREGAP},
252 {RT_STR_TUPLE("SONGWRITER"), CUEKEYWORD_SONGWRITER},
253 {RT_STR_TUPLE("TITLE"), CUEKEYWORD_TITLE},
254 {RT_STR_TUPLE("TRACK"), CUEKEYWORD_TRACK},
255 {RT_STR_TUPLE("MODE1/2048"), CUEKEYWORD_MODE1_2048},
256 {RT_STR_TUPLE("MODE1/2352"), CUEKEYWORD_MODE1_2352},
257 {RT_STR_TUPLE("MODE2/2352"), CUEKEYWORD_MODE2_2352},
258 {RT_STR_TUPLE("AUDIO"), CUEKEYWORD_AUDIO},
259 {RT_STR_TUPLE("REM"), CUEKEYWORD_REM}
260};
261
262
263/*********************************************************************************************************************************
264* Internal Functions *
265*********************************************************************************************************************************/
266
267
268/**
269 * Converts a MSF formatted address value read from the given buffer
270 * to an LBA number. MSF 00:00:00 equals LBA 0.
271 *
272 * @returns The LBA number.
273 * @param pbBuf The buffer to read the MSF formatted address
274 * from.
275 */
276DECLINLINE(uint32_t) cueMSF2LBA(const uint8_t *pbBuf)
277{
278 return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2];
279}
280
281/**
282 * Ensures that the region list can hold up to the given number of tracks.
283 *
284 * @returns VBox status code.
285 * @param pThis The CUE image state.
286 * @param cTracksMax Maximum number of tracks.
287 */
288static int cueEnsureRegionListSize(PCUEIMAGE pThis, uint32_t cTracksMax)
289{
290 int rc = VINF_SUCCESS;
291
292 if (pThis->cTracksMax < cTracksMax)
293 {
294 PVDREGIONLIST pRegionListNew = (PVDREGIONLIST)RTMemRealloc(pThis->pRegionList,
295 RT_UOFFSETOF_DYN(VDREGIONLIST, aRegions[cTracksMax]));
296 if (pRegionListNew)
297 {
298 /* Init all the new allocated tracks. */
299 for (uint32_t i = pThis->cTracksMax; i < cTracksMax; i++)
300 pRegionListNew->aRegions[i].offRegion = UINT64_MAX;
301 pThis->pRegionList = pRegionListNew;
302 pThis->cTracksMax = cTracksMax;
303 }
304 else
305 rc = VERR_NO_MEMORY;
306 }
307
308 return rc;
309}
310
311/**
312 * Returns whether the tokenizer reached the end of the stream.
313 *
314 * @returns true if the tokenizer reached the end of stream marker
315 * false otherwise.
316 * @param pTokenizer The tokenizer state.
317 */
318DECLINLINE(bool) cueTokenizerIsEos(PCUETOKENIZER pTokenizer)
319{
320 return *pTokenizer->pszInput == '\0';
321}
322
323/**
324 * Skip one character in the input stream.
325 *
326 * @returns nothing.
327 * @param pTokenizer The tokenizer state.
328 */
329DECLINLINE(void) cueTokenizerSkipCh(PCUETOKENIZER pTokenizer)
330{
331 /* Never ever go past EOS. */
332 if (!cueTokenizerIsEos(pTokenizer))
333 pTokenizer->pszInput++;
334}
335
336/**
337 * Returns the next char in the input buffer without advancing it.
338 *
339 * @returns Next character in the input buffer.
340 * @param pTokenizer The tokenizer state.
341 */
342DECLINLINE(char) cueTokenizerPeekCh(PCUETOKENIZER pTokenizer)
343{
344 return cueTokenizerIsEos(pTokenizer)
345 ? '\0'
346 : *(pTokenizer->pszInput + 1);
347}
348
349/**
350 * Returns the next character in the input buffer advancing the internal
351 * position.
352 *
353 * @returns Next character in the stream.
354 * @param pTokenizer The tokenizer state.
355 */
356DECLINLINE(char) cueTokenizerGetCh(PCUETOKENIZER pTokenizer)
357{
358 char ch;
359
360 if (cueTokenizerIsEos(pTokenizer))
361 ch = '\0';
362 else
363 ch = *pTokenizer->pszInput;
364
365 return ch;
366}
367
368/**
369 * Sets a new line for the tokenizer.
370 *
371 * @returns nothing.
372 * @param pTokenizer The tokenizer state.
373 * @param cSkip How many characters to skip.
374 */
375DECLINLINE(void) cueTokenizerNewLine(PCUETOKENIZER pTokenizer, unsigned cSkip)
376{
377 pTokenizer->pszInput += cSkip;
378}
379
380/**
381 * Checks whether the current position in the input stream is a new line
382 * and skips it.
383 *
384 * @returns Flag whether there was a new line at the current position
385 * in the input buffer.
386 * @param pTokenizer The tokenizer state.
387 */
388DECLINLINE(bool) cueTokenizerIsSkipNewLine(PCUETOKENIZER pTokenizer)
389{
390 bool fNewline = true;
391
392 if ( cueTokenizerGetCh(pTokenizer) == '\r'
393 && cueTokenizerPeekCh(pTokenizer) == '\n')
394 cueTokenizerNewLine(pTokenizer, 2);
395 else if (cueTokenizerGetCh(pTokenizer) == '\n')
396 cueTokenizerNewLine(pTokenizer, 1);
397 else
398 fNewline = false;
399
400 return fNewline;
401}
402
403/**
404 * Skip all whitespace starting from the current input buffer position.
405 * Skips all present comments too.
406 *
407 * @returns nothing.
408 * @param pTokenizer The tokenizer state.
409 */
410DECLINLINE(void) cueTokenizerSkipWhitespace(PCUETOKENIZER pTokenizer)
411{
412 while (!cueTokenizerIsEos(pTokenizer))
413 {
414 while ( cueTokenizerGetCh(pTokenizer) == ' '
415 || cueTokenizerGetCh(pTokenizer) == '\t')
416 cueTokenizerSkipCh(pTokenizer);
417
418 if ( !cueTokenizerIsEos(pTokenizer)
419 && !cueTokenizerIsSkipNewLine(pTokenizer))
420 break; /* Skipped everything, next is some real content. */
421 }
422}
423
424/**
425 * Skips a multi line comment.
426 *
427 * @returns nothing.
428 * @param pTokenizer The tokenizer state.
429 */
430DECLINLINE(void) cueTokenizerSkipComment(PCUETOKENIZER pTokenizer)
431{
432 while ( !cueTokenizerIsEos(pTokenizer)
433 && !cueTokenizerIsSkipNewLine(pTokenizer))
434 cueTokenizerSkipCh(pTokenizer);
435 cueTokenizerSkipWhitespace(pTokenizer);
436}
437
438/**
439 * Get an identifier token from the tokenizer.
440 *
441 * @returns nothing.
442 * @param pTokenizer The tokenizer state.
443 * @param pToken The uninitialized token.
444 */
445static void cueTokenizerGetKeyword(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
446{
447 char ch;
448 unsigned cchKeyword = 0;
449 bool fIsKeyword = false;
450 bool fIsComment;
451 const char *pszKeyword;
452
453 Assert(RT_C_IS_ALPHA(*pTokenizer->pszInput));
454
455 do
456 {
457 fIsComment = false;
458 pszKeyword = pTokenizer->pszInput;
459
460 do
461 {
462 cchKeyword++;
463 cueTokenizerSkipCh(pTokenizer);
464 ch = cueTokenizerGetCh(pTokenizer);
465 }
466 while (RT_C_IS_ALNUM(ch) || ch == '_' || ch == '/' || ch == '.');
467
468 /* Check whether we got a keyword or a string constant. */
469 for (unsigned i = 0; i < RT_ELEMENTS(g_aCueKeywords); i++)
470 {
471 if (!RTStrNCmp(g_aCueKeywords[i].pszKeyword, pszKeyword, RT_MIN(cchKeyword, g_aCueKeywords[i].cchKeyword)))
472 {
473 if (g_aCueKeywords[i].enmKeyword == CUEKEYWORD_REM)
474 {
475 /* The REM keyword is handled here as it indicates a comment which we just skip. */
476 cueTokenizerSkipComment(pTokenizer);
477 fIsComment = true;
478 }
479 else
480 {
481 fIsKeyword = true;
482 pToken->enmType = CUETOKENTYPE_KEYWORD;
483 pToken->Type.Keyword.enmKeyword = g_aCueKeywords[i].enmKeyword;
484 }
485 break;
486 }
487 }
488 } while (fIsComment);
489
490 /* Make it a string. */
491 if (ch == '\0')
492 pToken->enmType = CUETOKENTYPE_EOS;
493 else if (!fIsKeyword)
494 {
495 pToken->enmType = CUETOKENTYPE_STRING;
496 pToken->Type.String.psz = pszKeyword;
497 pToken->Type.String.cch = cchKeyword;
498 }
499}
500
501/**
502 * Get an integer value or MSF location indicator from the tokenizer.
503 *
504 * @returns nothing.
505 * @param pTokenizer The tokenizer state.
506 * @param pToken The uninitialized token.
507 */
508static void cueTokenizerGetIntegerOrMsf(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
509{
510 char szInt[20 + 1]; /* Maximum which fits into an unsigned 64bit integer + zero terminator. */
511 unsigned cchInt = 0;
512 bool fMsf = false;
513 char ch = cueTokenizerGetCh(pTokenizer);
514
515 Assert(RT_C_IS_DIGIT(ch));
516 RT_ZERO(szInt);
517
518 /* Go through the characters and check for the : mark which denotes MSF location indicator. */
519 do
520 {
521 szInt[cchInt++] = ch;
522 cueTokenizerSkipCh(pTokenizer);
523 ch = cueTokenizerGetCh(pTokenizer);
524 if (ch == ':')
525 fMsf = true;
526 }
527 while ( (RT_C_IS_DIGIT(ch) || ch == ':')
528 && cchInt < sizeof(szInt));
529
530 if (cchInt < sizeof(szInt) - 1)
531 {
532 if (fMsf)
533 {
534 /* Check that the format matches our expectations (mm:ss:ff). */
535 if ( cchInt == 8 && szInt[2] == ':' && szInt[5] == ':')
536 {
537 /* Parse the single fields. */
538 szInt[2] = '\0';
539 szInt[5] = '\0';
540
541 int rc = RTStrToUInt8Full(&szInt[0], 10, &pToken->Type.Msf.u8Minute);
542 if (RT_SUCCESS(rc))
543 rc = RTStrToUInt8Full(&szInt[3], 10, &pToken->Type.Msf.u8Second);
544 if (RT_SUCCESS(rc))
545 rc = RTStrToUInt8Full(&szInt[6], 10, &pToken->Type.Msf.u8Frame);
546 if (RT_SUCCESS(rc))
547 pToken->enmType = CUETOKENTYPE_MSF;
548 else
549 pToken->enmType = CUETOKENTYPE_ERROR;
550 }
551 else
552 pToken->enmType = CUETOKENTYPE_ERROR;
553 }
554 else
555 {
556 pToken->enmType = CUETOKENTYPE_INTEGER_UNSIGNED;
557 int rc = RTStrToUInt64Full(&szInt[0], 10, &pToken->Type.Int.u64);
558 if (RT_FAILURE(rc))
559 pToken->enmType = CUETOKENTYPE_ERROR;
560 }
561 }
562 else
563 pToken->enmType = CUETOKENTYPE_ERROR;
564}
565
566/**
567 * Parses a string constant.
568 *
569 * @returns nothing.
570 * @param pTokenizer The tokenizer state.
571 * @param pToken The uninitialized token.
572 *
573 * @remarks: No escape sequences allowed at this time.
574 */
575static void cueTokenizerGetStringConst(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
576{
577 unsigned cchStr = 0;
578
579 Assert(cueTokenizerGetCh(pTokenizer) == '\"');
580 cueTokenizerSkipCh(pTokenizer); /* Skip " */
581
582 pToken->enmType = CUETOKENTYPE_STRING;
583 pToken->Type.String.psz = pTokenizer->pszInput;
584
585 while ( !cueTokenizerIsEos(pTokenizer)
586 && cueTokenizerGetCh(pTokenizer) != '\"')
587 {
588 cchStr++;
589 cueTokenizerSkipCh(pTokenizer);
590 }
591
592 /* End of stream without a closing quote is an error. */
593 if (RT_UNLIKELY(cueTokenizerIsEos(pTokenizer)))
594 pToken->enmType = CUETOKENTYPE_ERROR;
595 else
596 {
597 cueTokenizerSkipCh(pTokenizer); /* Skip closing " */
598 pToken->Type.String.cch = cchStr;
599 }
600}
601
602/**
603 * Get the end of stream token.
604 *
605 * @returns nothing.
606 * @param pTokenizer The tokenizer state.
607 * @param pToken The uninitialized token.
608 */
609static void cueTokenizerGetEos(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
610{
611 Assert(cueTokenizerGetCh(pTokenizer) == '\0'); RT_NOREF(pTokenizer);
612
613 pToken->enmType = CUETOKENTYPE_EOS;
614}
615
616/**
617 * Read the next token from the tokenizer stream.
618 *
619 * @returns nothing.
620 * @param pTokenizer The tokenizer to read from.
621 * @param pToken Uninitialized token to fill the token data into.
622 */
623static void cueTokenizerReadNextToken(PCUETOKENIZER pTokenizer, PCUETOKEN pToken)
624{
625 /* Skip all eventually existing whitespace, newlines and comments first. */
626 cueTokenizerSkipWhitespace(pTokenizer);
627
628 char ch = cueTokenizerGetCh(pTokenizer);
629 if (RT_C_IS_ALPHA(ch))
630 cueTokenizerGetKeyword(pTokenizer, pToken);
631 else if (RT_C_IS_DIGIT(ch))
632 cueTokenizerGetIntegerOrMsf(pTokenizer, pToken);
633 else if (ch == '\"')
634 cueTokenizerGetStringConst(pTokenizer, pToken);
635 else if (ch == '\0')
636 cueTokenizerGetEos(pTokenizer, pToken);
637 else
638 pToken->enmType = CUETOKENTYPE_ERROR;
639}
640
641/**
642 * Create a new tokenizer.
643 *
644 * @returns Pointer to the new tokenizer state on success.
645 * NULL if out of memory.
646 * @param pszInput The input to create the tokenizer for.
647 */
648static PCUETOKENIZER cueTokenizerCreate(const char *pszInput)
649{
650 PCUETOKENIZER pTokenizer = (PCUETOKENIZER)RTMemAllocZ(sizeof(CUETOKENIZER));
651 if (pTokenizer)
652 {
653 pTokenizer->pszInput = pszInput;
654 pTokenizer->pTokenCurr = &pTokenizer->Token1;
655 pTokenizer->pTokenNext = &pTokenizer->Token2;
656 /* Fill the tokenizer with two first tokens. */
657 cueTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenCurr);
658 cueTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
659 }
660
661 return pTokenizer;
662}
663
664/**
665 * Get the current token in the input stream.
666 *
667 * @returns Pointer to the next token in the stream.
668 * @param pTokenizer The tokenizer to destroy.
669 */
670DECLINLINE(PCCUETOKEN) cueTokenizerGetToken(PCUETOKENIZER pTokenizer)
671{
672 return pTokenizer->pTokenCurr;
673}
674
675/**
676 * Get the class of the current token.
677 *
678 * @returns Class of the current token.
679 * @param pTokenizer The tokenizer state.
680 */
681DECLINLINE(CUETOKENTYPE) cueTokenizerGetTokenType(PCUETOKENIZER pTokenizer)
682{
683 return pTokenizer->pTokenCurr->enmType;
684}
685
686/**
687 * Consume the current token advancing to the next in the stream.
688 *
689 * @returns nothing.
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 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
1454
1455 PCUEIMAGE pThis = (PCUEIMAGE)RTMemAllocZ(sizeof(CUEIMAGE));
1456 if (RT_LIKELY(pThis))
1457 {
1458 pThis->pszFilename = pszFilename;
1459 pThis->pStorage = NULL;
1460 pThis->pVDIfsDisk = pVDIfsDisk;
1461 pThis->pVDIfsImage = pVDIfsImage;
1462
1463 rc = cueOpenImage(pThis, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
1464 cueFreeImage(pThis, false);
1465 RTMemFree(pThis);
1466
1467 if (RT_SUCCESS(rc))
1468 *penmType = VDTYPE_OPTICAL_DISC;
1469 else
1470 rc = VERR_VD_GEN_INVALID_HEADER;
1471 }
1472 else
1473 rc = VERR_NO_MEMORY;
1474
1475 LogFlowFunc(("returns %Rrc\n", rc));
1476 return rc;
1477}
1478
1479/** @copydoc VDIMAGEBACKEND::pfnOpen */
1480static DECLCALLBACK(int) cueOpen(const char *pszFilename, unsigned uOpenFlags,
1481 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1482 VDTYPE enmType, void **ppBackendData)
1483{
1484 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n",
1485 pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
1486 int rc;
1487 PCUEIMAGE pThis;
1488
1489 /* Check open flags. All valid flags are supported. */
1490 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
1491 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
1492 AssertReturn(enmType == VDTYPE_OPTICAL_DISC, VERR_NOT_SUPPORTED);
1493
1494 pThis = (PCUEIMAGE)RTMemAllocZ(sizeof(CUEIMAGE));
1495 if (RT_LIKELY(pThis))
1496 {
1497 pThis->pszFilename = pszFilename;
1498 pThis->pStorage = NULL;
1499 pThis->pVDIfsDisk = pVDIfsDisk;
1500 pThis->pVDIfsImage = pVDIfsImage;
1501
1502 rc = cueOpenImage(pThis, uOpenFlags);
1503 if (RT_SUCCESS(rc))
1504 *ppBackendData = pThis;
1505 else
1506 RTMemFree(pThis);
1507 }
1508 else
1509 rc = VERR_NO_MEMORY;
1510
1511 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1512 return rc;
1513}
1514
1515/** @copydoc VDIMAGEBACKEND::pfnClose */
1516static DECLCALLBACK(int) cueClose(void *pBackendData, bool fDelete)
1517{
1518 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1519 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1520 int rc = cueFreeImage(pThis, fDelete);
1521 RTMemFree(pThis);
1522
1523 LogFlowFunc(("returns %Rrc\n", rc));
1524 return rc;
1525}
1526
1527/** @copydoc VDIMAGEBACKEND::pfnRead */
1528static DECLCALLBACK(int) cueRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1529 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1530{
1531 LogFlowFunc(("pBackendData=%#p uOffset=%llu cbToRead=%zu pIoCtx=%#p pcbActuallyRead=%#p\n",
1532 pBackendData, uOffset, cbToRead, pIoCtx, pcbActuallyRead));
1533 int rc = VINF_SUCCESS;
1534 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1535
1536 /* Get the region */
1537 PCVDREGIONDESC pRegion = cueRegionQueryByOffset(pThis, uOffset);
1538 if (pRegion)
1539 {
1540 /* Clip read size to remain in the region (not necessary I think). */
1541 uint64_t offRead = uOffset - pRegion->offRegion;
1542
1543 cbToRead = RT_MIN(cbToRead, pRegion->cRegionBlocksOrBytes - offRead);
1544 Assert(!(cbToRead % pRegion->cbBlock));
1545
1546 /* Need to convert audio data samples to little endian. */
1547 if ( pRegion->enmDataForm == VDREGIONDATAFORM_CDDA
1548 && !pThis->fLittleEndian)
1549 {
1550 *pcbActuallyRead = cbToRead;
1551
1552 while (cbToRead)
1553 {
1554 RTSGSEG Segment;
1555 unsigned cSegments = 1;
1556 size_t cbSeg = 0;
1557
1558 cbSeg = vdIfIoIntIoCtxSegArrayCreate(pThis->pIfIo, pIoCtx, &Segment,
1559 &cSegments, cbToRead);
1560
1561 rc = vdIfIoIntFileReadSync(pThis->pIfIo, pThis->pStorageData, uOffset, Segment.pvSeg, cbSeg);
1562 if (RT_FAILURE(rc))
1563 break;
1564
1565 uint16_t *pu16Buf = (uint16_t *)Segment.pvSeg;
1566 for (uint32_t i = 0; i < cbSeg / sizeof(uint16_t); i++)
1567 {
1568 *pu16Buf = RT_BSWAP_U16(*pu16Buf);
1569 pu16Buf++;
1570 }
1571
1572 cbToRead -= RT_MIN(cbToRead, cbSeg);
1573 uOffset += cbSeg;
1574 }
1575 }
1576 else
1577 {
1578 rc = vdIfIoIntFileReadUser(pThis->pIfIo, pThis->pStorageData, uOffset,
1579 pIoCtx, cbToRead);
1580 if (RT_SUCCESS(rc))
1581 *pcbActuallyRead = cbToRead;
1582 }
1583 }
1584 else
1585 rc = VERR_INVALID_PARAMETER;
1586
1587 return rc;
1588}
1589
1590/** @copydoc VDIMAGEBACKEND::pfnWrite */
1591static DECLCALLBACK(int) cueWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
1592 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
1593 size_t *pcbPostRead, unsigned fWrite)
1594{
1595 RT_NOREF7(uOffset, cbToWrite, pIoCtx, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite);
1596 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
1597 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
1598 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1599 int rc;
1600
1601 AssertPtr(pThis);
1602
1603 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1604 rc = VERR_VD_IMAGE_READ_ONLY;
1605 else
1606 rc = VERR_NOT_SUPPORTED;
1607
1608 LogFlowFunc(("returns %Rrc\n", rc));
1609 return rc;
1610}
1611
1612/** @copydoc VDIMAGEBACKEND::pfnFlush */
1613static DECLCALLBACK(int) cueFlush(void *pBackendData, PVDIOCTX pIoCtx)
1614{
1615 RT_NOREF2(pBackendData, pIoCtx);
1616
1617 return VINF_SUCCESS;
1618}
1619
1620/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
1621static DECLCALLBACK(unsigned) cueGetVersion(void *pBackendData)
1622{
1623 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1624 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1625
1626 AssertPtrReturn(pThis, 0);
1627
1628 return 1;
1629}
1630
1631/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
1632static DECLCALLBACK(uint64_t) cueGetFileSize(void *pBackendData)
1633{
1634 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1635 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1636
1637 AssertPtrReturn(pThis, 0);
1638
1639 uint64_t cbFile = 0;
1640 if (pThis->pStorage)
1641 {
1642 int rc = vdIfIoIntFileGetSize(pThis->pIfIo, pThis->pStorageData, &cbFile);
1643 if (RT_FAILURE(rc))
1644 cbFile = 0; /* Make sure it is 0 */
1645 }
1646
1647 LogFlowFunc(("returns %lld\n", cbFile));
1648 return cbFile;
1649}
1650
1651/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
1652static DECLCALLBACK(int) cueGetPCHSGeometry(void *pBackendData,
1653 PVDGEOMETRY pPCHSGeometry)
1654{
1655 RT_NOREF1(pPCHSGeometry);
1656 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1657 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1658 int rc = VINF_SUCCESS;
1659
1660 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1661
1662 rc = VERR_NOT_SUPPORTED;
1663
1664 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1665 return rc;
1666}
1667
1668/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
1669static DECLCALLBACK(int) cueSetPCHSGeometry(void *pBackendData,
1670 PCVDGEOMETRY pPCHSGeometry)
1671{
1672 RT_NOREF1(pPCHSGeometry);
1673 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
1674 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1675 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1676 int rc = VINF_SUCCESS;
1677
1678 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1679
1680 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1681 rc = VERR_VD_IMAGE_READ_ONLY;
1682 else
1683 rc = VERR_NOT_SUPPORTED;
1684
1685 LogFlowFunc(("returns %Rrc\n", rc));
1686 return rc;
1687}
1688
1689/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
1690static DECLCALLBACK(int) cueGetLCHSGeometry(void *pBackendData,
1691 PVDGEOMETRY pLCHSGeometry)
1692{
1693 RT_NOREF1(pLCHSGeometry);
1694 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1695 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1696 int rc = VINF_SUCCESS;
1697
1698 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1699
1700 rc = VERR_NOT_SUPPORTED;
1701
1702 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1703 return rc;
1704}
1705
1706/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
1707static DECLCALLBACK(int) cueSetLCHSGeometry(void *pBackendData,
1708 PCVDGEOMETRY pLCHSGeometry)
1709{
1710 RT_NOREF1(pLCHSGeometry);
1711 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
1712 pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1713 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1714 int rc = VINF_SUCCESS;
1715
1716 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1717
1718 if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1719 rc = VERR_VD_IMAGE_READ_ONLY;
1720 else
1721 rc = VERR_NOT_SUPPORTED;
1722
1723 LogFlowFunc(("returns %Rrc\n", rc));
1724 return rc;
1725}
1726
1727/** @copydoc VDIMAGEBACKEND::pfnQueryRegions */
1728static DECLCALLBACK(int) cueQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
1729{
1730 LogFlowFunc(("pBackendData=%#p ppRegionList=%#p\n", pBackendData, ppRegionList));
1731 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1732
1733 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1734
1735 *ppRegionList = pThis->pRegionList;
1736 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
1737 return VINF_SUCCESS;
1738}
1739
1740/** @copydoc VDIMAGEBACKEND::pfnRegionListRelease */
1741static DECLCALLBACK(void) cueRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
1742{
1743 RT_NOREF1(pRegionList);
1744 LogFlowFunc(("pBackendData=%#p pRegionList=%#p\n", pBackendData, pRegionList));
1745 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1746 AssertPtr(pThis); RT_NOREF(pThis);
1747
1748 /* Nothing to do here. */
1749}
1750
1751/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
1752static DECLCALLBACK(unsigned) cueGetImageFlags(void *pBackendData)
1753{
1754 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1755 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1756
1757 AssertPtrReturn(pThis, 0);
1758
1759 LogFlowFunc(("returns %#x\n", pThis->uImageFlags));
1760 return pThis->uImageFlags;
1761}
1762
1763/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
1764static DECLCALLBACK(unsigned) cueGetOpenFlags(void *pBackendData)
1765{
1766 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1767 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1768
1769 AssertPtrReturn(pThis, 0);
1770
1771 LogFlowFunc(("returns %#x\n", pThis->uOpenFlags));
1772 return pThis->uOpenFlags;
1773}
1774
1775/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
1776static DECLCALLBACK(int) cueSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
1777{
1778 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
1779 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1780 int rc = VINF_SUCCESS;
1781
1782 /* Image must be opened and the new flags must be valid. */
1783 if (!pThis || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
1784 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
1785 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
1786 rc = VERR_INVALID_PARAMETER;
1787 else
1788 {
1789 /* Implement this operation via reopening the image. */
1790 rc = cueFreeImage(pThis, false);
1791 if (RT_SUCCESS(rc))
1792 rc = cueOpenImage(pThis, uOpenFlags);
1793 }
1794
1795 LogFlowFunc(("returns %Rrc\n", rc));
1796 return rc;
1797}
1798
1799/** @copydoc VDIMAGEBACKEND::pfnGetComment */
1800VD_BACKEND_CALLBACK_GET_COMMENT_DEF_NOT_SUPPORTED(cueGetComment);
1801
1802/** @copydoc VDIMAGEBACKEND::pfnSetComment */
1803VD_BACKEND_CALLBACK_SET_COMMENT_DEF_NOT_SUPPORTED(cueSetComment, PCUEIMAGE);
1804
1805/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
1806VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(cueGetUuid);
1807
1808/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
1809VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(cueSetUuid, PCUEIMAGE);
1810
1811/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
1812VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(cueGetModificationUuid);
1813
1814/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
1815VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(cueSetModificationUuid, PCUEIMAGE);
1816
1817/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
1818VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(cueGetParentUuid);
1819
1820/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
1821VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(cueSetParentUuid, PCUEIMAGE);
1822
1823/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
1824VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(cueGetParentModificationUuid);
1825
1826/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
1827VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(cueSetParentModificationUuid, PCUEIMAGE);
1828
1829/** @copydoc VDIMAGEBACKEND::pfnDump */
1830static DECLCALLBACK(void) cueDump(void *pBackendData)
1831{
1832 PCUEIMAGE pThis = (PCUEIMAGE)pBackendData;
1833
1834 AssertPtrReturnVoid(pThis);
1835 vdIfErrorMessage(pThis->pIfError, "Dumping CUE image \"%s\" mode=%s uOpenFlags=%X File=%#p\n",
1836 pThis->pszFilename,
1837 (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
1838 pThis->uOpenFlags,
1839 pThis->pStorage);
1840 vdIfErrorMessage(pThis->pIfError, "Backing File \"%s\" File=%#p\n",
1841 pThis->pszDataFilename, pThis->pStorageData);
1842 vdIfErrorMessage(pThis->pIfError, "Number of tracks: %u\n", pThis->pRegionList->cRegions);
1843 for (uint32_t i = 0; i < pThis->pRegionList->cRegions; i++)
1844 {
1845 PCVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[i];
1846
1847 vdIfErrorMessage(pThis->pIfError, "------------------------ Track %u ------------------------\n", i);
1848 vdIfErrorMessage(pThis->pIfError, "Start=%llu Size=%llu BlockSize=%llu DataSize=%llu MetadataSize=%llu\n",
1849 pRegion->offRegion, pRegion->cRegionBlocksOrBytes, pRegion->cbBlock, pRegion->cbData,
1850 pRegion->cbMetadata);
1851 vdIfErrorMessage(pThis->pIfError, "DataForm=%s MetadataForm=%s\n",
1852 cueRegionDataFormStringify(pRegion->enmDataForm),
1853 cueRegionMetadataFormStringify(pRegion->enmMetadataForm));
1854 }
1855}
1856
1857
1858
1859const VDIMAGEBACKEND g_CueBackend =
1860{
1861 /* u32Version */
1862 VD_IMGBACKEND_VERSION,
1863 /* pszBackendName */
1864 "CUE",
1865 /* uBackendCaps */
1866 VD_CAP_FILE | VD_CAP_VFS,
1867 /* paFileExtensions */
1868 s_aCueFileExtensions,
1869 /* paConfigInfo */
1870 NULL,
1871 /* pfnProbe */
1872 cueProbe,
1873 /* pfnOpen */
1874 cueOpen,
1875 /* pfnCreate */
1876 NULL,
1877 /* pfnRename */
1878 NULL,
1879 /* pfnClose */
1880 cueClose,
1881 /* pfnRead */
1882 cueRead,
1883 /* pfnWrite */
1884 cueWrite,
1885 /* pfnFlush */
1886 cueFlush,
1887 /* pfnDiscard */
1888 NULL,
1889 /* pfnGetVersion */
1890 cueGetVersion,
1891 /* pfnGetFileSize */
1892 cueGetFileSize,
1893 /* pfnGetPCHSGeometry */
1894 cueGetPCHSGeometry,
1895 /* pfnSetPCHSGeometry */
1896 cueSetPCHSGeometry,
1897 /* pfnGetLCHSGeometry */
1898 cueGetLCHSGeometry,
1899 /* pfnSetLCHSGeometry */
1900 cueSetLCHSGeometry,
1901 /* pfnQueryRegions */
1902 cueQueryRegions,
1903 /* pfnRegionListRelease */
1904 cueRegionListRelease,
1905 /* pfnGetImageFlags */
1906 cueGetImageFlags,
1907 /* pfnGetOpenFlags */
1908 cueGetOpenFlags,
1909 /* pfnSetOpenFlags */
1910 cueSetOpenFlags,
1911 /* pfnGetComment */
1912 cueGetComment,
1913 /* pfnSetComment */
1914 cueSetComment,
1915 /* pfnGetUuid */
1916 cueGetUuid,
1917 /* pfnSetUuid */
1918 cueSetUuid,
1919 /* pfnGetModificationUuid */
1920 cueGetModificationUuid,
1921 /* pfnSetModificationUuid */
1922 cueSetModificationUuid,
1923 /* pfnGetParentUuid */
1924 cueGetParentUuid,
1925 /* pfnSetParentUuid */
1926 cueSetParentUuid,
1927 /* pfnGetParentModificationUuid */
1928 cueGetParentModificationUuid,
1929 /* pfnSetParentModificationUuid */
1930 cueSetParentModificationUuid,
1931 /* pfnDump */
1932 cueDump,
1933 /* pfnGetTimestamp */
1934 NULL,
1935 /* pfnGetParentTimestamp */
1936 NULL,
1937 /* pfnSetParentTimestamp */
1938 NULL,
1939 /* pfnGetParentFilename */
1940 NULL,
1941 /* pfnSetParentFilename */
1942 NULL,
1943 /* pfnComposeLocation */
1944 genericFileComposeLocation,
1945 /* pfnComposeName */
1946 genericFileComposeName,
1947 /* pfnCompact */
1948 NULL,
1949 /* pfnResize */
1950 NULL,
1951 /* pfnRepair */
1952 NULL,
1953 /* pfnTraverseMetadata */
1954 NULL,
1955 /* u32VersionEnd */
1956 VD_IMGBACKEND_VERSION
1957};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use