Compare commits

..

11 Commits

Author SHA1 Message Date
cae1791eba Add a9 file submissions 2023-12-05 13:33:01 -04:00
6ff99deff7 Add a8 file submissions 2023-12-05 13:32:04 -04:00
c6dd294bc0 Remove unused import a9 2023-12-05 13:29:57 -04:00
6a6fd9c37c Tests in a9 2023-12-05 13:28:08 -04:00
112535e8e7 Use LF in a9 2023-12-05 13:28:08 -04:00
93635bc1d6 Remove unneeded function 2023-12-05 12:57:16 -04:00
530ab9a135 Add comments and error checking to A9 2023-12-05 12:47:36 -04:00
bea609e01c Use exit define 2023-12-04 20:55:45 -04:00
136a4fe960 Code reformatting 2023-12-04 20:55:07 -04:00
123e0f56bc Add test shell scripts 2023-12-04 20:53:40 -04:00
e79158e3f1 Make Tests pass 2023-12-04 20:53:31 -04:00
11 changed files with 353 additions and 298 deletions

Binary file not shown.

View File

@ -0,0 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All CTest" type="CTestRunConfiguration" factoryName="CTestRun" PROGRAM_PARAMS="--extra-verbose" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$CMakeCurrentLocalGenerationDir$" PASS_PARENT_ENVS_2="true" CONFIG_NAME="Debug" RUN_PATH="$CTestCurrentExecutableName$" EXPLICIT_BUILD_TARGET_NAME="all" TEST_MODE="PATTERN">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
<option name="BeforeTestRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@ -0,0 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="images" type="CTestRunConfiguration" factoryName="CTestRun" PROGRAM_PARAMS="--extra-verbose" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$CMakeCurrentLocalGenerationDir$" PASS_PARENT_ENVS_2="true" CONFIG_NAME="Debug" RUN_PATH="$CTestCurrentExecutableName$" EXPLICIT_BUILD_TARGET_NAME="all" TEST_METHOD="images" TEST_MODE="SUITE_TEST">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
<option name="BeforeTestRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@ -0,0 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="output" type="CTestRunConfiguration" factoryName="CTestRun" PROGRAM_PARAMS="--extra-verbose" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$CMakeCurrentLocalGenerationDir$" PASS_PARENT_ENVS_2="true" CONFIG_NAME="Debug" RUN_PATH="$CTestCurrentExecutableName$" EXPLICIT_BUILD_TARGET_NAME="all" TEST_METHOD="output" TEST_MODE="SUITE_TEST">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
<option name="BeforeTestRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@ -4,3 +4,7 @@ set(CMAKE_C_STANDARD 99)
add_executable(Assignment9 src/main.c
src/png.c
src/png.h)
enable_testing()
add_test(NAME output COMMAND test_text.sh WORKING_DIRECTORY ../tests)
add_test(NAME images COMMAND test_images.sh WORKING_DIRECTORY ../tests)

View File

@ -3,36 +3,38 @@
#include <limits.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include "png.h"
int main(int argc, char *argv[]) {
// Error handling
if (argc != 2) {
printf("Usage: %s <png file>\n", argv[0]);
exit(1);
exit(EXIT_FAILURE);
}
if (strlen(argv[1]) > PATH_MAX) {
printf("Path too long\n");
exit(1);
exit(EXIT_FAILURE);
}
// Read file
char *path = argv[1];
char* png_buffer = load_file(get_fd(path));
char *png_buffer = load_file(path);
if (!is_png(png_buffer)) {
printf("It's not a PNG file\n");
exit(1);
exit(EXIT_FAILURE);
} else {
printf("It's a PNG file\n");
}
png_chunk **chunks = get_png_chunks(png_buffer);
free(png_buffer);
// Done with buffer, as chunks are a structured way to access the data
// Iterate over chunks to display info and "decrypt" IDAT chunks
int size = get_number_of_chunks(chunks);
for (int i = 0; i < size; i++) {
// Check if header is IDAT or IEND
bool is_idat = memcmp(chunks[i]->type, "IDAT", 4) == 0;
bool is_iend = memcmp(chunks[i]->type, "IEND", 4) == 0;
if (is_idat || is_iend) {
printf("Found %.4s chunk\n", chunks[i]->type);
printf("Found %.4s\n", chunks[i]->type);
if (is_idat) {
xor_data(chunks[i]);
}
@ -41,11 +43,7 @@ int main(int argc, char *argv[]) {
}
printf("Chunk size is:%d\n", chunks[i]->length);
}
// Uncomment to not overwrite original file
//char new_path[PATH_MAX];
//getcwd(new_path, sizeof(new_path));
//strcat(new_path, "/output.png");
//write_png_chunks(new_path, chunks);
// Write file and free memory
write_png_chunks(path, chunks);
destroy_chunks(chunks);
return EXIT_SUCCESS;

View File

@ -35,38 +35,24 @@
#include <stdbool.h>
#include "png.h"
int get_fd(char *path) {
FILE *get_file(char *path) {
FILE *fp = fopen(path, "rb");
if (fp == NULL) {
perror("fopen");
exit(1);
}
int fd = fileno(fp);
if (fd == -1) {
perror("fileno");
exit(1);
}
return fd;
}
FILE *get_fp(int fd) {
FILE *fp = fdopen(fd, "rb");
if (fp == NULL) {
perror("fdopen");
exit(1);
exit(EXIT_FAILURE);
}
return fp;
}
char *load_file(int fd) {
FILE *fp = get_fp(fd);
char *load_file(char *path) {
FILE *fp = get_file(path);
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
rewind(fp);
char *buffer = malloc(size);
if (buffer == NULL) {
perror("malloc");
exit(1);
exit(EXIT_FAILURE);
}
fread(buffer, 1, size, fp);
fclose(fp);
@ -81,15 +67,16 @@ png_chunk *get_png_chunk(char *buffer, unsigned int offset) {
png_chunk *chunk = malloc(sizeof(png_chunk));
if (chunk == NULL) {
perror("malloc");
exit(1);
exit(EXIT_FAILURE);
}
memcpy(&chunk->length, buffer + offset + 0, 4);
// Convert length to host byte order because PNG lengths is in network byte order/Big Endian
chunk->length = ntohl(chunk->length);
memcpy(&chunk->type, buffer + offset + 4, 4);
chunk->data = malloc(chunk->length);
if (chunk->data == NULL) {
perror("malloc");
exit(1);
exit(EXIT_FAILURE);
}
memcpy(chunk->data, buffer + offset + 8, chunk->length);
memcpy(&chunk->crc, buffer + offset + 8 + chunk->length, 4);
@ -100,9 +87,11 @@ png_chunk **get_png_chunks(char *buffer) {
png_chunk **chunks = malloc(sizeof(png_chunk *));
if (chunks == NULL) {
perror("malloc");
exit(1);
exit(EXIT_FAILURE);
}
// Running offset of the buffer
unsigned int offset = PNG_SIGNATURE_SIZE;
// Index of the current chunk
int i = 0;
while (true) {
chunks[i] = get_png_chunk(buffer, offset);
@ -112,11 +101,14 @@ png_chunk **get_png_chunks(char *buffer) {
break;
}
i++;
chunks = realloc(chunks, sizeof(png_chunk *) * (i + 1));
if (chunks == NULL) {
// Add one because realloc is 1-indexed when multiplying by sizeof
png_chunk **err_check = realloc(chunks, sizeof(png_chunk *) * (i + 1));
if (err_check == NULL) {
destroy_chunks(chunks);
perror("realloc");
exit(1);
exit(EXIT_FAILURE);
}
chunks = err_check;
}
return chunks;
}
@ -126,6 +118,7 @@ int get_number_of_chunks(png_chunk **chunks) {
while (memcmp(chunks[i]->type, "IEND", 4) != 0) {
i++;
}
// Add one for the IEND chunk
return ++i;
}
@ -135,8 +128,6 @@ void xor_data(png_chunk *chunk) {
}
}
void destroy_chunks(png_chunk **chunks) {
unsigned int size = get_number_of_chunks(chunks);
for (int i = 0; i < size; i++) {
@ -150,14 +141,16 @@ void write_png_chunks(char *path, png_chunk **chunks) {
FILE *fp = fopen(path, "wb");
if (fp == NULL) {
perror("fopen");
exit(1);
exit(EXIT_FAILURE);
}
fwrite(PNG_SIGNATURE, PNG_SIGNATURE_SIZE, 1, fp);
unsigned int size = get_number_of_chunks(chunks);
for (int i = 0; i < size; i++) {
// Convert length to network byte order for writing
chunks[i]->length = htonl(chunks[i]->length);
fwrite(&chunks[i]->length, 4, 1, fp);
fwrite(chunks[i]->type, 4, 1, fp);
// Convert back so we can accurately use it to write the length of the data
chunks[i]->length = ntohl(chunks[i]->length);
fwrite(chunks[i]->data, chunks[i]->length, 1, fp);
fwrite(&chunks[i]->crc, 4, 1, fp);

View File

@ -30,15 +30,14 @@
#pragma once
// Needed for uint types
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature
#define PNG_SIGNATURE "\211PNG\r\n\032\n"
#define PNG_SIGNATURE_SIZE 8
// The key used to "encrypt/decrypt" the PNG chunks
#define KEY 42
typedef struct png_chunk {
@ -48,14 +47,11 @@ typedef struct png_chunk {
uint32_t crc;
} png_chunk;
// Get file descriptor from path
// Get FILE from path
int get_fd(char *path);
// Get FILE* from file descriptor
FILE *get_fp(int fd);
// Store file in heap memory
char *load_file(int fd);
char *load_file(char *path);
// Check if file is a PNG
int is_png(char *buffer);

Binary file not shown.

View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Executable path
EXECUTABLE=../build/Assignment9
test_reverse() {
start=$(date +%s)
cp "$1" in.png
cp in.png out.png
$EXECUTABLE out.png > /dev/null
$EXECUTABLE out.png > /dev/null
diff in.png out.png
rm in.png out.png
end=$(date +%s)
echo "Test $1 took $((end - start)) seconds"
}
test_reverse test1.png
# test_reverse test2.png # This png is invalid, so it will fail
test_reverse test3.png
test_reverse test4.png

View File

@ -0,0 +1,18 @@
#!/usr/bin/env bash
EXECUTABLE=../build/Assignment9
# Test the text output
$EXECUTABLE test1.png | diff output1.txt -
$EXECUTABLE test2.png | diff output2.txt -
$EXECUTABLE test3.png | diff output3.txt -
$EXECUTABLE test4.png | diff output4.txt -
# Reverse the images
$EXECUTABLE test1.png > /dev/null
$EXECUTABLE test2.png > /dev/null
$EXECUTABLE test3.png > /dev/null
$EXECUTABLE test4.png > /dev/null
echo "If there is no diff output, then the test passed."