382 lines
12 KiB
C++
382 lines
12 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library.
|
|
Copyright (c) 2022 - Raw Material Software Limited
|
|
|
|
JUCE is an open source library subject to commercial or open-source
|
|
licensing.
|
|
|
|
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
|
Agreement and JUCE Privacy Policy.
|
|
|
|
End User License Agreement: www.juce.com/juce-7-licence
|
|
Privacy Policy: www.juce.com/juce-privacy-policy
|
|
|
|
Or: You may also use this code under the terms of the GPL v3 (see
|
|
www.gnu.org/licenses).
|
|
|
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
|
DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <optional>
|
|
#include <vector>
|
|
|
|
//==============================================================================
|
|
struct FileHelpers
|
|
{
|
|
static std::string getCurrentWorkingDirectory()
|
|
{
|
|
std::vector<char> buffer (1024);
|
|
|
|
while (getcwd (buffer.data(), buffer.size() - 1) == nullptr && errno == ERANGE)
|
|
buffer.resize (buffer.size() * 2 / 3);
|
|
|
|
return { buffer.data() };
|
|
}
|
|
|
|
static bool endsWith (const std::string& s, char c)
|
|
{
|
|
if (s.length() == 0)
|
|
return false;
|
|
|
|
return *s.rbegin() == c;
|
|
}
|
|
|
|
static std::string appendedPaths (const std::string& first, const std::string& second)
|
|
{
|
|
return endsWith (first, '/') ? first + second : first + "/" + second;
|
|
}
|
|
|
|
static bool exists (const std::string& path)
|
|
{
|
|
return ! path.empty() && access (path.c_str(), F_OK) == 0;
|
|
}
|
|
|
|
static bool deleteFile (const std::string& path)
|
|
{
|
|
if (! exists (path))
|
|
return true;
|
|
|
|
return remove (path.c_str()) == 0;
|
|
}
|
|
|
|
static std::string getFilename (const std::string& path)
|
|
{
|
|
return { std::find_if (path.rbegin(), path.rend(), [] (auto c) { return c == '/'; }).base(),
|
|
path.end() };
|
|
}
|
|
|
|
static bool isDirectory (const std::string& path)
|
|
{
|
|
#if defined (__FreeBSD__) || defined (__OpenBSD__)
|
|
#define JUCE_STAT stat
|
|
#else
|
|
#define JUCE_STAT stat64
|
|
#endif
|
|
|
|
struct JUCE_STAT info;
|
|
|
|
return ! path.empty()
|
|
&& JUCE_STAT (path.c_str(), &info) == 0
|
|
&& ((info.st_mode & S_IFDIR) != 0);
|
|
}
|
|
|
|
static std::string getParentDirectory (const std::string& path)
|
|
{
|
|
std::string p { path.begin(),
|
|
std::find_if (path.rbegin(),
|
|
path.rend(),
|
|
[] (auto c) { return c == '/'; }).base() };
|
|
|
|
// Trim the ending slash, but only if not root
|
|
if (endsWith (p, '/') && p.length() > 1)
|
|
return { p.begin(), p.end() - 1 };
|
|
|
|
return p;
|
|
}
|
|
|
|
static bool createDirectory (const std::string& path)
|
|
{
|
|
if (isDirectory (path))
|
|
return true;
|
|
|
|
const auto parentDir = getParentDirectory (path);
|
|
|
|
if (path == parentDir)
|
|
return false;
|
|
|
|
if (createDirectory (parentDir))
|
|
return mkdir (path.c_str(), 0777) != -1;
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
//==============================================================================
|
|
struct StringHelpers
|
|
{
|
|
static bool isQuoteCharacter (char c)
|
|
{
|
|
return c == '"' || c == '\'';
|
|
}
|
|
|
|
static std::string unquoted (const std::string& str)
|
|
{
|
|
if (str.length() == 0 || (! isQuoteCharacter (str[0])))
|
|
return str;
|
|
|
|
return str.substr (1, str.length() - (isQuoteCharacter (str[str.length() - 1]) ? 1 : 0));
|
|
}
|
|
|
|
static void ltrim (std::string& s)
|
|
{
|
|
s.erase (s.begin(), std::find_if (s.begin(), s.end(), [] (int c) { return ! std::isspace (c); }));
|
|
}
|
|
|
|
static void rtrim (std::string& s)
|
|
{
|
|
s.erase (std::find_if (s.rbegin(), s.rend(), [] (int c) { return ! std::isspace (c); }).base(), s.end());
|
|
}
|
|
|
|
static std::string trimmed (const std::string& str)
|
|
{
|
|
auto result = str;
|
|
ltrim (result);
|
|
rtrim (result);
|
|
return result;
|
|
}
|
|
|
|
static std::string replaced (const std::string& str, char charToReplace, char replaceWith)
|
|
{
|
|
auto result = str;
|
|
std::replace (result.begin(), result.end(), charToReplace, replaceWith);
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//==============================================================================
|
|
static bool addFile (const std::string& filePath,
|
|
const std::string& binaryNamespace,
|
|
std::ofstream& headerStream,
|
|
std::ofstream& cppStream,
|
|
bool verbose)
|
|
{
|
|
std::ifstream fileStream (filePath, std::ios::in | std::ios::binary | std::ios::ate);
|
|
|
|
if (! fileStream.is_open())
|
|
{
|
|
std::cerr << "Failed to open input file " << filePath << std::endl;
|
|
return false;
|
|
}
|
|
|
|
std::vector<char> buffer ((size_t) fileStream.tellg());
|
|
fileStream.seekg (0);
|
|
fileStream.read (buffer.data(), static_cast<std::streamsize> (buffer.size()));
|
|
|
|
const auto variableName = StringHelpers::replaced (StringHelpers::replaced (FileHelpers::getFilename (filePath),
|
|
' ',
|
|
'_'),
|
|
'.',
|
|
'_');
|
|
|
|
if (verbose)
|
|
{
|
|
std::cout << "Adding " << variableName << ": "
|
|
<< buffer.size() << " bytes" << std::endl;
|
|
}
|
|
|
|
headerStream << " extern const char* " << variableName << ";" << std::endl
|
|
<< " const int " << variableName << "Size = "
|
|
<< buffer.size() << ";" << std::endl;
|
|
|
|
cppStream << "static const unsigned char temp0[] = {";
|
|
|
|
auto* data = (const uint8_t*) buffer.data();
|
|
|
|
for (size_t i = 0; i < buffer.size() - 1; ++i)
|
|
{
|
|
cppStream << (int) data[i] << ",";
|
|
|
|
if ((i % 40) == 39)
|
|
cppStream << std::endl << " ";
|
|
}
|
|
|
|
cppStream << (int) data[buffer.size() - 1] << ",0,0};" << std::endl;
|
|
cppStream << "const char* " << binaryNamespace << "::" << variableName
|
|
<< " = (const char*) temp0" << ";" << std::endl << std::endl;
|
|
|
|
return true;
|
|
}
|
|
|
|
//==============================================================================
|
|
class Arguments
|
|
{
|
|
public:
|
|
enum class PositionalArguments
|
|
{
|
|
sourceFile = 0,
|
|
targetDirectory,
|
|
targetFilename,
|
|
binaryNamespace
|
|
};
|
|
|
|
static std::optional<Arguments> create (int argc, char* argv[])
|
|
{
|
|
std::vector<std::string> arguments;
|
|
bool verbose = false;
|
|
|
|
for (int i = 1; i < argc; ++i)
|
|
{
|
|
std::string arg { argv[i] };
|
|
|
|
if (arg == "-v" || arg == "--verbose")
|
|
verbose = true;
|
|
else
|
|
arguments.emplace_back (std::move (arg));
|
|
}
|
|
|
|
if (arguments.size() != static_cast<size_t> (PositionalArguments::binaryNamespace) + 1)
|
|
return std::nullopt;
|
|
|
|
return Arguments { std::move (arguments), verbose };
|
|
}
|
|
|
|
std::string get (PositionalArguments argument) const
|
|
{
|
|
return arguments[static_cast<size_t> (argument)];
|
|
}
|
|
|
|
bool isVerbose() const
|
|
{
|
|
return verbose;
|
|
}
|
|
|
|
private:
|
|
Arguments (std::vector<std::string> args, bool verboseIn)
|
|
: arguments (std::move (args)), verbose (verboseIn)
|
|
{
|
|
}
|
|
|
|
std::vector<std::string> arguments;
|
|
bool verbose = false;
|
|
};
|
|
|
|
//==============================================================================
|
|
int main (int argc, char* argv[])
|
|
{
|
|
const auto arguments = Arguments::create (argc, argv);
|
|
|
|
if (! arguments.has_value())
|
|
{
|
|
std::cout << " Usage: SimpleBinaryBuilder [-v | --verbose] sourcefile targetdirectory targetfilename namespace"
|
|
<< std::endl << std::endl
|
|
<< " SimpleBinaryBuilder will encode the provided source file into" << std::endl
|
|
<< " two files called (targetfilename).cpp and (targetfilename).h," << std::endl
|
|
<< " which it will write into the specified target directory." << std::endl
|
|
<< " The target directory will be automatically created if necessary. The binary" << std::endl
|
|
<< " resource will be available in the given namespace." << std::endl << std::endl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
const auto currentWorkingDirectory = FileHelpers::getCurrentWorkingDirectory();
|
|
|
|
using ArgType = Arguments::PositionalArguments;
|
|
|
|
const auto sourceFile = FileHelpers::appendedPaths (currentWorkingDirectory,
|
|
StringHelpers::unquoted (arguments->get (ArgType::sourceFile)));
|
|
|
|
if (! FileHelpers::exists (sourceFile))
|
|
{
|
|
std::cerr << "Source file doesn't exist: "
|
|
<< sourceFile
|
|
<< std::endl << std::endl;
|
|
|
|
return 1;
|
|
}
|
|
|
|
const auto targetDirectory = FileHelpers::appendedPaths (currentWorkingDirectory,
|
|
StringHelpers::unquoted (arguments->get (ArgType::targetDirectory)));
|
|
|
|
if (! FileHelpers::exists (targetDirectory))
|
|
{
|
|
if (! FileHelpers::createDirectory (targetDirectory))
|
|
{
|
|
std::cerr << "Failed to create target directory: " << targetDirectory << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
const auto className = StringHelpers::trimmed (arguments->get (ArgType::targetFilename));
|
|
const auto binaryNamespace = StringHelpers::trimmed (arguments->get (ArgType::binaryNamespace));
|
|
|
|
const auto headerFilePath = FileHelpers::appendedPaths (targetDirectory, className + ".h");
|
|
const auto cppFilePath = FileHelpers::appendedPaths (targetDirectory, className + ".cpp");
|
|
|
|
if (arguments->isVerbose())
|
|
{
|
|
std::cout << "Creating " << headerFilePath
|
|
<< " and " << cppFilePath
|
|
<< " from file " << sourceFile
|
|
<< "..." << std::endl << std::endl;
|
|
}
|
|
|
|
if (! FileHelpers::deleteFile (headerFilePath))
|
|
{
|
|
std::cerr << "Failed to remove old header file: " << headerFilePath << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
if (! FileHelpers::deleteFile (cppFilePath))
|
|
{
|
|
std::cerr << "Failed to remove old source file: " << cppFilePath << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
std::ofstream header (headerFilePath);
|
|
|
|
if (! header.is_open())
|
|
{
|
|
std::cerr << "Failed to open " << headerFilePath << std::endl;
|
|
|
|
return 1;
|
|
}
|
|
|
|
std::ofstream cpp (cppFilePath);
|
|
|
|
if (! cpp.is_open())
|
|
{
|
|
std::cerr << "Failed to open " << headerFilePath << std::endl;
|
|
|
|
return 1;
|
|
}
|
|
|
|
header << "/* (Auto-generated binary data file). */" << std::endl << std::endl
|
|
<< "#pragma once" << std::endl << std::endl
|
|
<< "namespace " << binaryNamespace << std::endl
|
|
<< "{" << std::endl;
|
|
|
|
cpp << "/* (Auto-generated binary data file). */" << std::endl << std::endl
|
|
<< "#include " << std::quoted (className + ".h") << std::endl << std::endl;
|
|
|
|
if (! addFile (sourceFile, binaryNamespace, header, cpp, arguments->isVerbose()))
|
|
return 1;
|
|
|
|
header << "}" << std::endl << std::endl;
|
|
|
|
return 0;
|
|
}
|