VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 21 months ago

Copyright year updates by scm.

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