Skip to content

Commit

Permalink
feat: mass file conversion option (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
sirconan authored Sep 11, 2022
1 parent 73178e0 commit af6fd1b
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 89 deletions.
51 changes: 35 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,46 @@
# gdi-conversion
gdi-conversion is a small Node.js program to convert Dreamcast Game Images (cue and bin files) to GDI Images in order to run on GDEmu.

gdi-conversion is a small Node.js program to convert Dreamcast Game Images (cue and bin files) to GDI Images in order to run on GDEmu.

# Requirements

gdi-conversion tool has been developed on Linux with node 16.2.0 and npm 7.14. Three executables files have been built for Linux, Macos and Windows.

# How to use it for convert to GDI format?
- clone this repository
# How to use it

- clone repository
- run `npm install`
- run `node src/bin.js -c ./PATH_OF_THE_FILE_TO_CONVERT.cue` to convert CUE to GDI
- `output` folder is created in the source file folder and content gdi, bin and raw files, is ready for your GDEmu sdcard. `name.txt` file is created inside the `output` folder to store the game's name.
- run `node src/bin.js [option] <input>`

or
or

- Download last release available
- `gdi-conversion-linux -c ./PATH_OF_THE_FILE_TO_CONVERT.cue` or `gdi-conversion-macos -c ./PATH_OF_THE_FILE_TO_CONVERT.cue` or `gdi-conversion-win.exe -c ./PATH_OF_THE_FILE_TO_CONVERT.cue` depending used operating system
- Download last release available and depending used operating system run :

# How to use it to extract game's name from disk.gdi file ?
- clone this repository
- run `npm install`
- run `node src/bin.js -n ./PATH_OF_THE_FILE/disc.gdi` to extract game's name
- `name.txt` file is created in the same folder to store the game's name. When you use `-c` command to convert, `-n` command is automatically running
```
gdi-conversion-linux [option] <input>
gdi-conversion-macos [option] <input>
gdi-conversion-win.exe [option] <input>
```

# Parameters

- [option]:
- `-c` to convert cue to gdi
- `-n` to extract game's name from gdi file

- \<input\>: gdi or cue file path or folder path for mass file conversion

# Output
An `output` folder will be create next to `.cue` file containing conversion result
A `name.txt` file will be created next to `.gdi` file containing the game's name

or
# Examples
```
gdi-conversion-linux -c ./MyUser/
```
to convert recursively all .cue files in MyUser folder on linux

- Download last release available
- `gdi-conversion-linux -n ./PATH_OF_THE_FILE/disc.gdi` or `gdi-conversion-macos -n ./PATH_OF_THE_FILE/disc.gdi` or `gdi-conversion-win.exe -n ./PATH_OF_THE_FILE/disc.gdi` depending used operating system
```
gdi-conversion-win.exe -n ./Download/Game/disc.gdi
```
to convert only Game/disc.gdi file on window
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@
},
"name": "gdi-conversion",
"description": "gdi-conversion is a small node program to convert Dreamcast Game Images (cue and bin files) to GDI Images in order to run on GDEmu.",
"version": "1.1.1",
"main": "src/main.js",
"version": "1.2.0",
"bin": "src/bin.js",
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
Expand Down
27 changes: 16 additions & 11 deletions src/bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,34 @@ const path = require('path');
const fs = require('fs');
const gdiConversion = require('./convert');
const extractName = require('./name');
const getAllFiles = require('./getAllFiles');

const [ , , command, filePath] = process.argv;
const [ , , command, userPath] = process.argv;

const absPath = path.resolve(process.cwd(), filePath);
if (userPath === undefined) {
console.error('Error: cannot find path argument');
return;
}

const absPath = path.resolve(process.cwd(), userPath);

fs.access(absPath, (err) => {
if (err) {
console.error(`Error: ${filePath} does not exist`);
console.error(`Error: ${userPath} does not exist`);
return;
}

const workingDirectory = path.dirname(absPath);

switch (command) {
switch (command.toLowerCase()) {
case '-n':
extractName(absPath, workingDirectory);
getAllFiles(absPath, '.gdi').forEach(extractName);
return;
case '-c':
gdiConversion(absPath, workingDirectory);
getAllFiles(absPath, '.cue').forEach(gdiConversion);
return;
default:
console.log(`invalid parameter action: ${command}`);
console.log('-c to convert .cue file to .gdi');
console.log('-n to extract the game\'s name from the converted disc.gdi file');
console.error(`Error: invalid parameter action: ${command}`);
console.error('Use:')
console.error(' -c to convert .cue file to .gdi');
console.error(' -n to extract the game\'s name from the converted disc.gdi file');
}
});
98 changes: 50 additions & 48 deletions src/convert.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#! /usr/bin/env node
const path = require('path');
const parser = require('cue-parser');
const fs = require('fs');
const fsExtra = require('fs-extra');
Expand All @@ -9,63 +9,65 @@ const TRACK_TYPE_AUDIO = 'AUDIO';
const BLOCK_SIZE = 2352;
const OUTPUT_FOLDER = 'output';

module.exports = function(absPath, workingDirectory) {
const parseFile = (absPath) => {
try {
return parser.parse(absPath);
} catch (errSheet) {
console.error(`Cannot convert ${absPath}, only cue files are allowed`);
}
};
const parseFile = (absPath) => {
try {
return parser.parse(absPath);
} catch (errSheet) {
console.error(`Cannot convert ${absPath}, only cue files are allowed`);
}
};

const countIndexFrames= ({time}) => {
let result = time.frame;
result += (time.sec * 75);
result += ((time.min * 60) * 75);
return result;
};
const countIndexFrames= ({time}) => {
let result = time.frame;
result += (time.sec * 75);
result += ((time.min * 60) * 75);
return result;
};

const copyFileWithGapOffset = (inputFile, outputFile, size, frames) => {
const position = frames * BLOCK_SIZE;
const buffer = Buffer.alloc(size);
const copyFileWithGapOffset = (inputFile, outputFile, size, frames) => {
const position = frames * BLOCK_SIZE;

const buffer = Buffer.alloc(size);

fs.open(inputFile, 'r+', function (inputFileError, inputFd) {
if (inputFileError) {
throw inputFileError;
fs.open(inputFile, 'r+', function (inputFileError, inputFd) {
if (inputFileError) {
throw inputFileError;
}

fs.open(outputFile, 'w+', function (outputFileError, outputFd) {
if (outputFileError) {
throw outputFileError;
}

fs.open(outputFile, 'w+', function (outputFileError, outputFd) {
if (outputFileError) {
throw outputFileError;
fs.read(inputFd, buffer, 0, size, position, (error, bytesRead, bufferRead) => {
if (error) {
throw error;
}

fs.read(inputFd, buffer, 0, size, position, (error, bytesRead, bufferRead) => {
if (error) {
throw error;
}

fs.write(outputFd, bufferRead.slice(0, bytesRead), () => console.log(`${outputFile} created`));
});
fs.write(outputFd, bufferRead.slice(0, bytesRead), () => void 0);
});
});
});

return (size - position) / BLOCK_SIZE;
};
return (size - position) / BLOCK_SIZE;
};

const padTrackNumber = (currentTrack) => {
if (currentTrack < 10) {
return `0${currentTrack}`;
}
const padTrackNumber = (currentTrack) => {
if (currentTrack < 10) {
return `0${currentTrack}`;
}

return currentTrack;
};
return currentTrack;
};

const createSummaryFile = (workingDirectory, gdiOutput) => {
const outputGdiFilePath = `${workingDirectory}/${OUTPUT_FOLDER}/disc.gdi`;
fs.writeFile(outputGdiFilePath, gdiOutput, () => void 0);
};

module.exports = function(absPath) {
const workingDirectory = path.dirname(absPath);

const createSummaryFile = (gdiOutput) => {
const outputGdiFilePath = `${workingDirectory}/${OUTPUT_FOLDER}/disc.gdi`;
fs.writeFile(outputGdiFilePath, gdiOutput, () => console.log(`${outputGdiFilePath} created`));
};

let currentSector = 0;
fsExtra.emptyDirSync(`${workingDirectory}/${OUTPUT_FOLDER}`);

Expand All @@ -89,7 +91,7 @@ module.exports = function(absPath, workingDirectory) {
let sectorAmount = 0;

if (canPerformFullCopy) {
fs.copyFile(inputTrackFilePath, outputTrackFilePath, () => console.log(`${outputTrackFilePath} created`));
fs.copyFile(inputTrackFilePath, outputTrackFilePath, () => void 0);
sectorAmount = inputTrackFileSize / BLOCK_SIZE;
} else {
const gapOffset = countIndexFrames(currentTrack.indexes[1]);
Expand All @@ -108,7 +110,7 @@ module.exports = function(absPath, workingDirectory) {
}

if (index === files.length - 1) {
createSummaryFile(gdiOutput);
createSummaryFile(workingDirectory, gdiOutput);
extractName(`${workingDirectory}/${OUTPUT_FOLDER}/`);
}
});
Expand Down
30 changes: 30 additions & 0 deletions src/getAllFiles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const path = require('path');
const fs = require('fs');

const getAllFiles = (dirPath, arrayOfFiles, ext) => {
files = fs.readdirSync(dirPath)

arrayOfFiles = arrayOfFiles || []

files.forEach(function(file) {
const currentElement = fs.statSync(dirPath + "/" + file);
if (currentElement.isDirectory()) {
arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles, ext)
} else if (currentElement.isFile() && file.substr(file.length - 4) === ext) {
arrayOfFiles.push(path.join(dirPath, "/", file))
}
})

return arrayOfFiles
};

module.exports = function(dirPath, ext) {
const state = fs.lstatSync(dirPath);
const fileList = state.isDirectory() ? getAllFiles(dirPath, [], ext) : [dirPath];

if (fileList.length === 0) {
console.error(`Error: cannot find '${ext}' files in '${dirPath}' folder`);
}

return fileList;
};
12 changes: 5 additions & 7 deletions src/name.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
#! /usr/bin/env node
const fs = require('fs');
const path = require('path');
const lineReader = require('line-reader');

module.exports = function(gdiPath, workingDirectory) {
lineReader.eachLine(gdiPath, (line) => {
module.exports = function(absPath) {
const workingDirectory = path.dirname(absPath);
lineReader.eachLine(absPath, (line) => {
if (line.charAt(0) === '1') {
const [,,,, binName] = line.split(' ');

fs.open(`${workingDirectory}/${binName}`, 'r+', function (inputFileError, inputFd) {
const buffer = Buffer.alloc(128);

fs.read(inputFd, buffer, 0, 128, 0x90, (error, bytesRead, bufferRead) => {
const outputNameFilePath = `${workingDirectory}/name.txt`;
fs.writeFile(outputNameFilePath, bufferRead.toString().trim(), () =>
console.log(`${outputNameFilePath} created`)
);
fs.writeFile(outputNameFilePath, bufferRead.toString().trim(), () => void 0);
});
});
}
Expand Down

0 comments on commit af6fd1b

Please sign in to comment.