From af6fd1bf2f99fca462c5c3f0c9445745c837ce8a Mon Sep 17 00:00:00 2001 From: Damien Pichon Date: Sun, 11 Sep 2022 14:27:45 +0200 Subject: [PATCH] feat: mass file conversion option (#5) --- README.md | 51 ++++++++++++++++-------- package-lock.json | 8 ++-- package.json | 4 +- src/bin.js | 27 +++++++------ src/convert.js | 98 +++++++++++++++++++++++----------------------- src/getAllFiles.js | 30 ++++++++++++++ src/name.js | 12 +++--- 7 files changed, 141 insertions(+), 89 deletions(-) create mode 100644 src/getAllFiles.js diff --git a/README.md b/README.md index 7f39422..79dfcf8 100644 --- a/README.md +++ b/README.md @@ -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] ` -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] +gdi-conversion-macos [option] +gdi-conversion-win.exe [option] +``` + +# Parameters + +- [option]: + - `-c` to convert cue to gdi + - `-n` to extract game's name from gdi file + +- \: 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 diff --git a/package-lock.json b/package-lock.json index 118eb83..3cf0d8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,12 @@ { "name": "gdi-conversion", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "1.1.0", + "name": "gdi-conversion", + "version": "1.2.0", "license": "MIT", "dependencies": { "cue-parser": "^0.3.0", @@ -14,8 +15,7 @@ }, "bin": { "gdi-conversion": "src/bin.js" - }, - "devDependencies": {} + } }, "node_modules/chardet": { "version": "1.3.0", diff --git a/package.json b/package.json index eedb680..77bf70c 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/src/bin.js b/src/bin.js index 3fdf572..9ff8bc4 100644 --- a/src/bin.js +++ b/src/bin.js @@ -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'); } }); diff --git a/src/convert.js b/src/convert.js index e9b4440..096769a 100644 --- a/src/convert.js +++ b/src/convert.js @@ -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'); @@ -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}`); @@ -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]); @@ -108,7 +110,7 @@ module.exports = function(absPath, workingDirectory) { } if (index === files.length - 1) { - createSummaryFile(gdiOutput); + createSummaryFile(workingDirectory, gdiOutput); extractName(`${workingDirectory}/${OUTPUT_FOLDER}/`); } }); diff --git a/src/getAllFiles.js b/src/getAllFiles.js new file mode 100644 index 0000000..68d7037 --- /dev/null +++ b/src/getAllFiles.js @@ -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; +}; \ No newline at end of file diff --git a/src/name.js b/src/name.js index 2551d3c..aac7d45 100644 --- a/src/name.js +++ b/src/name.js @@ -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); }); }); }