Skip to content

Commit

Permalink
Add a feature to calculate a crc checksum.
Browse files Browse the repository at this point in the history
  • Loading branch information
unknownbrackets committed Oct 26, 2014
1 parent 2f85dfe commit 395d115
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 16 deletions.
52 changes: 36 additions & 16 deletions cli/cli.cpp
@@ -1,8 +1,9 @@
#include "../src/compress.h"
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <string>
#include "../src/compress.h"
#include "../src/checksum.h"
#include "../libuv/include/uv.h"

void show_version() {
Expand All @@ -17,6 +18,7 @@ void show_help(const char *arg0) {
fprintf(stderr, "\n");
fprintf(stderr, " --threads=N Specify N threads for I/O and compression\n");
fprintf(stderr, " --quiet Suppress status output\n");
fprintf(stderr, " --crc Log CRC32 checksums, ignore output files and methods\n");
fprintf(stderr, " --fast Use only basic zlib for fastest result\n");
// TODO: Bring this back once it's functional.
//fprintf(stderr, " --smallest Force compression of all sectors for smallest result\n");
Expand Down Expand Up @@ -53,12 +55,14 @@ struct Arguments {
int threads;
uint32_t flags;
bool quiet;
bool crc;
};

void default_args(Arguments &args) {
args.threads = 0;
args.flags = maxcso::TASKFLAG_NO_ZOPFLI;
args.quiet = false;
args.crc = false;
}

int parse_args(Arguments &args, int argc, char *argv[]) {
Expand All @@ -74,6 +78,8 @@ int parse_args(Arguments &args, int argc, char *argv[]) {
return 1;
} else if (has_arg_value(i, argv, "--threads", val)) {
args.threads = atoi(val);
} else if (has_arg(i, argv, "--crc")) {
args.crc = true;
} else if (has_arg(i, argv, "--quiet")) {
args.quiet = true;
} else if (has_arg(i, argv, "--fast")) {
Expand Down Expand Up @@ -127,22 +133,30 @@ int validate_args(const char *arg0, Arguments &args) {
return 1;
}

// Automatically write to .cso files if not specified.
for (size_t i = args.outputs.size(); i < args.inputs.size(); ++i) {
if (args.inputs[i].size() <= 4) {
continue;
if (args.crc) {
if (args.outputs.size()) {
show_help(arg0);
fprintf(stderr, "\nERROR: Output files not used with --crc.\n");
return 1;
}
} else {
// Automatically write to .cso files if not specified.
for (size_t i = args.outputs.size(); i < args.inputs.size(); ++i) {
if (args.inputs[i].size() <= 4) {
continue;
}

const std::string ext = args.inputs[i].substr(args.inputs[i].size() - 4);
if (ext == ".iso" || ext == ".ISO") {
args.outputs.push_back(args.inputs[i].substr(0, args.inputs[i].size() - 4) + ".cso");
const std::string ext = args.inputs[i].substr(args.inputs[i].size() - 4);
if (ext == ".iso" || ext == ".ISO") {
args.outputs.push_back(args.inputs[i].substr(0, args.inputs[i].size() - 4) + ".cso");
}
}
}

if (args.inputs.size() != args.outputs.size()) {
show_help(arg0);
fprintf(stderr, "\nERROR: Too few output files.\n");
return 1;
if (args.inputs.size() != args.outputs.size()) {
show_help(arg0);
fprintf(stderr, "\nERROR: Too few output files.\n");
return 1;
}
}

if (args.inputs.empty()) {
Expand Down Expand Up @@ -268,14 +282,20 @@ int main(int argc, char *argv[]) {
for (size_t i = 0; i < args.inputs.size(); ++i) {
maxcso::Task task;
task.input = args.inputs[i];
task.output = args.outputs[i];
if (!args.crc) {
task.output = args.outputs[i];
}
task.progress = progress;
task.error = error;
task.flags = args.flags;
tasks.push_back(std::move(task));
}

maxcso::Compress(tasks);

if (args.crc) {
maxcso::Checksum(tasks);
} else {
maxcso::Compress(tasks);
}

uv_tty_reset_mode();
uv_loop_close(&loop);
Expand Down
158 changes: 158 additions & 0 deletions src/checksum.cpp
@@ -0,0 +1,158 @@
#include <functional>
#include <fcntl.h>
#include <map>
#include "checksum.h"
#include "uv_helper.h"
#include "input.h"
#include "buffer_pool.h"
#include "cso.h"
#define ZLIB_CONST
#include "zlib.h"

namespace maxcso {

class ChecksumTask {
public:
ChecksumTask(uv_loop_t *loop, const Task &t)
: task_(t), loop_(loop), input_(-1), inputHandler_(loop) {
}
~ChecksumTask() {
Cleanup();
}

void Enqueue();
void Cleanup();

private:
void HandleBuffer(uint8_t *buffer);

void Notify(TaskStatus status, int64_t pos = -1, int64_t total = -1, int64_t written = -1) {
if (status == TASK_INPROGRESS || status == TASK_SUCCESS) {
task_.progress(&task_, status, pos, total, written);
} else {
task_.error(&task_, status, nullptr);
}
}
void Notify(TaskStatus status, const char *reason) {
task_.error(&task_, status, reason);
}

void BeginProcessing();

UVHelper uv_;
const Task &task_;
uv_loop_t *loop_;

uv_fs_t read_;

uv_file input_;
Input inputHandler_;

int64_t pos_;
int64_t size_;
uint32_t crc_;
std::map<int64_t, uint8_t *> pendingBuffers_;
};

void ChecksumTask::Enqueue() {
// We open input and output in order in case there are errors.
uv_.fs_open(loop_, &read_, task_.input.c_str(), O_RDONLY, 0444, [this](uv_fs_t *req) {
if (req->result < 0) {
Notify(TASK_BAD_INPUT, "Could not open input file");
} else {
input_ = static_cast<uv_file>(req->result);

BeginProcessing();
}
uv_fs_req_cleanup(req);
});
}

void ChecksumTask::Cleanup() {
if (input_ >= 0) {
uv_fs_close(loop_, &read_, input_, nullptr);
uv_fs_req_cleanup(&read_);
input_ = -1;
}
for (auto pair : pendingBuffers_) {
pool.Release(pair.second);
}
pendingBuffers_.clear();
}

void ChecksumTask::BeginProcessing() {
pos_ = 0;

inputHandler_.OnFinish([this](bool success, const char *reason) {
if (!success) {
Notify(TASK_INVALID_DATA, reason);
}
});

inputHandler_.OnBegin([this](int64_t size) {
crc_ = crc32(0L, Z_NULL, 0);
size_ = size;
Notify(TASK_INPROGRESS, 0, size, 0);
});
inputHandler_.Pipe(input_, [this](int64_t pos, uint8_t *buffer) {
// In case we allow the buffers to come out of order, let's use a queue.
if (pos_ == pos) {
HandleBuffer(buffer);
} else {
pendingBuffers_[pos] = buffer;
}
});
}

void ChecksumTask::HandleBuffer(uint8_t *buffer) {
// We're doing this on the main thread, but it should be okay.
crc_ = crc32(crc_, buffer, SECTOR_SIZE);
pool.Release(buffer);
pos_ += SECTOR_SIZE;

// Flush any in the queue that we can now use.
if (!pendingBuffers_.empty()) {
auto it = pendingBuffers_.begin();
auto begin = it;
for (; it != pendingBuffers_.end(); ++it) {
if (it->first != pos_) {
break;
}

crc_ = crc32(crc_, it->second, SECTOR_SIZE);
pool.Release(it->second);
pos_ += SECTOR_SIZE;
}

// If we used any, erase them.
if (begin != it) {
pendingBuffers_.erase(begin, it);
}
}

Notify(TASK_INPROGRESS, pos_, size_, 0);

if (pos_ == size_) {
char temp[128];
sprintf(temp, "CRC32: %08x", crc_);
Notify(TASK_SUCCESS, temp);
}
}

void Checksum(const std::vector<Task> &tasks) {
uv_loop_t loop;
uv_loop_init(&loop);

for (const Task t : tasks) {
ChecksumTask task(&loop, t);
task.Enqueue();
uv_run(&loop, UV_RUN_DEFAULT);
}

// Run any remaining events from destructors.
uv_run(&loop, UV_RUN_DEFAULT);

uv_loop_close(&loop);
}

};
13 changes: 13 additions & 0 deletions src/checksum.h
@@ -0,0 +1,13 @@
#pragma once

#include <string>
#include <vector>
#include <functional>
#include <cstdint>
#include "compress.h"

namespace maxcso {

void Checksum(const std::vector<Task> &tasks);

};
2 changes: 2 additions & 0 deletions src/src.vcxproj
Expand Up @@ -20,6 +20,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="buffer_pool.cpp" />
<ClCompile Include="checksum.cpp" />
<ClCompile Include="compress.cpp" />
<ClCompile Include="input.cpp" />
<ClCompile Include="output.cpp" />
Expand All @@ -28,6 +29,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="buffer_pool.h" />
<ClInclude Include="checksum.h" />
<ClInclude Include="compress.h" />
<ClInclude Include="cso.h" />
<ClInclude Include="dax.h" />
Expand Down
2 changes: 2 additions & 0 deletions src/src.vcxproj.filters
Expand Up @@ -7,6 +7,7 @@
<ClCompile Include="buffer_pool.cpp" />
<ClCompile Include="output.cpp" />
<ClCompile Include="sector.cpp" />
<ClCompile Include="checksum.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="compress.h" />
Expand All @@ -17,5 +18,6 @@
<ClInclude Include="sector.h" />
<ClInclude Include="cso.h" />
<ClInclude Include="dax.h" />
<ClInclude Include="checksum.h" />
</ItemGroup>
</Project>

0 comments on commit 395d115

Please sign in to comment.