VirtualBox

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

Last change on this file since 87048 was 87048, checked in by vboxsync, 4 years ago

CUE, DevATA: Added support for MODE2/2352 tracks in CUE sheets and IDE/ATAPI emulation (see bugref:6975).

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