// ------------------------------------------------------------------------------------------------ #include "Base/ScriptSrc.hpp" // ------------------------------------------------------------------------------------------------ #include #include #include // ------------------------------------------------------------------------------------------------ namespace SqMod { /* ------------------------------------------------------------------------------------------------ * Helper class to ensure the file handle is closed regardless of the situation. */ class FileHandle { public: // -------------------------------------------------------------------------------------------- std::FILE * mFile; // Handle to the opened file. /* -------------------------------------------------------------------------------------------- * Default constructor. */ FileHandle(CSStr path) : mFile(std::fopen(path, "rb")) { if (!mFile) { throw std::runtime_error(ToStrF("Unable to open script source (%s)", path)); } } /* -------------------------------------------------------------------------------------------- * Copy constructor. (disabled) */ FileHandle(const FileHandle & o) = delete; /* -------------------------------------------------------------------------------------------- * Move constructor. (disabled) */ FileHandle(FileHandle && o) = delete; /* -------------------------------------------------------------------------------------------- * Destructor. */ ~FileHandle() { if (mFile) { std::fclose(mFile); } } /* -------------------------------------------------------------------------------------------- * Copy assignment operator. (disabled) */ FileHandle & operator = (const FileHandle & o) = delete; /* -------------------------------------------------------------------------------------------- * Move assignment operator. (disabled) */ FileHandle & operator = (FileHandle && o) = delete; /* -------------------------------------------------------------------------------------------- * Implicit conversion to the managed file handle. */ operator std::FILE * () { return mFile; } /* -------------------------------------------------------------------------------------------- * Implicit conversion to the managed file handle. */ operator std::FILE * () const { return mFile; } }; // ------------------------------------------------------------------------------------------------ void ScriptSrc::Process() { // Attempt to open the specified file FileHandle fp(mPath.c_str()); // First 2 bytes of the file will tell if this is a compiled script std::uint16_t tag; // Go to the end of the file std::fseek(fp, 0, SEEK_END); // Calculate buffer size from beginning to current position const LongI length = std::ftell(fp); // Go back to the beginning std::fseek(fp, 0, SEEK_SET); // Read the first 2 bytes of the file and determine the file type if ((length >= 2) && (std::fread(&tag, 1, 2, fp) != 2 || tag == SQ_BYTECODE_STREAM_TAG)) { return; // Probably an empty file or compiled script } // Allocate enough space to hold the file data mData.resize(length, 0); // Read the file contents into allocated data std::fread(&mData[0], 1, length, fp); // Where the last line ended size_t line_start = 0, line_end = 0; // Process the file data and locate new lines for (String::const_iterator itr = mData.cbegin(); itr != mData.cend();) { // Is this a Unix style line ending? if (*itr == '\n') { // Extract the line length line_end = std::distance(mData.cbegin(), itr); // Store the beginning of the line mLine.emplace_back(line_start, line_end); // Advance to the next line line_start = line_end+1; // The line end character was not included ++itr; } // Is this a Windows style line ending? else if (*itr == '\r') { if (*(++itr) == '\n') { // Extract the line length line_end = std::distance(mData.cbegin(), itr)-1; // Store the beginning of the line mLine.emplace_back(line_start, line_end); // Advance to the next line line_start = line_end+2; // The line end character was not included ++itr; } } else { ++itr; } } // Should we add the last line as well? if (mData.size() - line_start > 0) { mLine.emplace_back(line_start, mData.size()); } // Specify that this script contains line information mInfo = true; } // ------------------------------------------------------------------------------------------------ ScriptSrc::ScriptSrc(HSQUIRRELVM vm, String && path, bool delay, bool info) : mExec(vm) , mPath(std::move(path)) , mData() , mLine() , mInfo(info) , mDelay(delay) { // Is the specified virtual machine invalid? if (!vm) { throw std::runtime_error("Invalid virtual machine pointer"); } // Is the specified path empty? else if (mPath.empty()) { throw std::runtime_error("Invalid or empty script path"); } // Should we load the file contents for debugging purposes? else if (mInfo) { Process(); } } } // Namespace:: SqMod