diff --git a/osufs/osufs/blkio.c b/osufs/osufs/blkio.c index a7ba77be458df3221b15fccd2504d4199830c7e1..31170694094a2637703be26fa396fe449c5c25f7 100644 --- a/osufs/osufs/blkio.c +++ b/osufs/osufs/blkio.c @@ -16,6 +16,8 @@ #include <errno.h> #include <string.h> +#include <assert.h> + #ifdef _WIN32 #error "Win32 NOT supported" @@ -90,7 +92,7 @@ mdev *blkio_open(const char *filename) { if (base == MAP_FAILED) { fprintf(stderr, "MMap failed.\n"); fprintf(stderr, "%s (%d)\n", strerror(errno), errno); - return NULL; + base = NULL; } if (close(fd) == -1) { fprintf(stderr, "Failed to close file\n"); @@ -108,3 +110,28 @@ void blkio_close(mdev *dev) { free(dev); } +void blkio_read(mdev *dev, uint16_t addr, void *buf) { + assert((addr*512) < dev->len && "Requested address beyond device size"); + ssize_t ret = pread(dev->fd, buf, 512, addr*512); + if (ret == -1) { + perror("Failed to read block device"); + abort(); + } else if (ret != 512) { + fprintf(stderr, "Could not fetch 512 bytes in one read. " + "Bytes read: %zd", ret); + abort(); + } +} + +void blkio_write(mdev *dev, uint16_t addr, void *buf) { + assert((addr*512) < dev->len && "Writing beyond device size"); + ssize_t ret = pwrite(dev->fd, buf, 512, addr*512); + if (ret == -1) { + perror("Failed to read block device"); + abort(); + } else if (ret != 512) { + fprintf(stderr, "Could not write 512 bytes in one write. " + "Bytes written: %zd", ret); + abort(); + } +} diff --git a/osufs/osufs/blkio.h b/osufs/osufs/blkio.h index 9e87fb462c951b675a9dd3bf9c4e4ab3d86c76ba..bd330ddfdf39dd305bdb4c7644adc831a4be4525 100644 --- a/osufs/osufs/blkio.h +++ b/osufs/osufs/blkio.h @@ -15,14 +15,17 @@ extern "C" { #include <inttypes.h> -typedef struct mdev_t { - void *base; - uint64_t len; - uint32_t fd; -} mdev; + typedef struct mdev_t { + void *base; + uint64_t len; + uint32_t fd; + } mdev; -mdev *blkio_open(const char *filename); -void blkio_close(mdev *dev); + mdev *blkio_open(const char *filename); + void blkio_close(mdev *dev); + + void blkio_read(mdev *dev, uint16_t addr, void *buf); + void blkio_write(mdev *dev, uint16_t addr, void *buf); #ifdef __cplusplus } diff --git a/osufs/osufs/imgenc.c b/osufs/osufs/imgenc.c new file mode 100644 index 0000000000000000000000000000000000000000..19d46fac6608d086bc8f9b3274a09ca7984a5273 --- /dev/null +++ b/osufs/osufs/imgenc.c @@ -0,0 +1,146 @@ +// +// imgenc.c +// osufs +// +// Created by Fang Lu on 12/3/17. +// Copyright © 2017 Fang Lu. All rights reserved. +// + +#include "imgenc.h" + +#include <stdio.h> +#include <string.h> +#include "libpng-wrapper.h" + +uint16_t img_write_dither128(const char *filename, mdev *dev, uint16_t addr) { + char cbuf[1024]; + int ret; + + system("rm -f source.png plt.png dither.png"); + + // Resize + sprintf(cbuf, "ffmpeg -i %s -vf \"scale=640:480\" source.png", filename); + ret = system(cbuf); + if (ret != 0) { + fprintf(stderr, "ffmpeg resize failed with exit code: %d\n", ret); + return addr; + } + + // PaletteGen + ret = system("ffmpeg -i source.png -vf \"palettegen=max_colors=128\" plt.png"); + if (ret != 0) { + fprintf(stderr, "ffmpeg palettegen failed with exit code: %d\n", ret); + return addr; + } + + // Dithering + ret = system("ffmpeg -i source.png -i plt.png -lavfi \"paletteuse=dither=floyd_steinberg\" -y dither.png"); + if (ret != 0) { + fprintf(stderr, "ffmpeg dithering failed with exit code: %d\n", ret); + } + + FILE *fp = fopen("dither.png", "rb"); + if (!fp) { + perror("Failed to open dithered image"); + return addr; + } + + PNGImage img = pngimg_read("dither.png"); + if (img.color_type != PNG_COLOR_TYPE_PALETTE) { + fprintf(stderr, "Internal Error: dithered image is not index!\n"); + return addr; + } + + png_colorp plt; + int nplt; + if (!png_get_PLTE(img.png_ptr, img.info_ptr, &plt, &nplt)) { + fprintf(stderr, "Internal Error: failed to read palette!\n"); + return addr; + } + printf("Read %d palette entries\n", nplt); + for (int i=0; i<nplt; i++) { + printf("%02x #%02x%02x%02x\n", i, plt[i].red, plt[i].green, plt[i].blue); + } + + // Dump palette + uint8_t buf[512], *pltbuf = buf + 3; + buf[0] = 'P'; + buf[1] = 'L'; + buf[2] = 'T'; + for (int i=0; i<128; i++) { + pltbuf[i*3+0] = plt[i].red; + pltbuf[i*3+1] = plt[i].green; + pltbuf[i*3+2] = plt[i].blue; + } + blkio_write(dev, addr++, buf); + + // Dump image, row major + size_t bufsize = png_get_rowbytes(img.png_ptr, img.info_ptr) * img.height; + size_t bufptr = 0; + while (bufsize > 0) { + blkio_write(dev, addr++, img.row_pointers[0]+bufptr); + bufptr += 512; + bufsize -= 512; + } + + pngimg_release(&img); + + fclose(fp); + + return addr; +} + +size_t img_write_rgb16(const char *filename, mdev *dev, uint16_t addr) { + char cbuf[1024]; + int ret; + + system("rm -f source.bmp"); + + // Resize + sprintf(cbuf, "ffmpeg -i %s -vf \"scale=640:480\" -pix_fmt rgb24 source.png", filename); + ret = system(cbuf); + if (ret != 0) { + fprintf(stderr, "ffmpeg resize failed with exit code: %d\n", ret); + return addr; + } + + FILE *fp = fopen("source.png", "rb"); + if (!fp) { + perror("Failed to open resized image"); + return addr; + } + + PNGImage img = pngimg_read("dither.png"); + if (img.color_type != PNG_COLOR_TYPE_RGB) { + fprintf(stderr, "Internal Error: resized image is not RGB24!\n"); + return addr; + } + + // Dump image as RGB16 (R5 G6 B5), row major + // Each 512 byte block holds 256 pixels + uint16_t buf[256]; + size_t npixels = img.width * img.height; + int bufptr, pixptr = 0; + uint8_t r, g, b; + png_bytep pixdata = img.row_pointers[0]; + while (pixptr < npixels) { + bufptr = 0; + while (bufptr < 256) { + r = pixdata[pixptr*3+0]; + g = pixdata[pixptr*3+1]; + b = pixdata[pixptr*3+2]; + buf[bufptr++] = 0; + pixptr++; + if (pixptr >= npixels) + break; + } + // Dump buffer + blkio_write(dev, addr++, buf); + } + + pngimg_release(&img); + + fclose(fp); + + return 0; +} diff --git a/osufs/osufs/imgenc.h b/osufs/osufs/imgenc.h new file mode 100644 index 0000000000000000000000000000000000000000..ca3062bef6892279f479743d91f4767b63287fda --- /dev/null +++ b/osufs/osufs/imgenc.h @@ -0,0 +1,26 @@ +// +// imgenc.h +// osufs +// +// Created by Fang Lu on 12/3/17. +// Copyright © 2017 Fang Lu. All rights reserved. +// + +#ifndef imgenc_h +#define imgenc_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include "blkio.h" + + uint16_t img_write_dither128(const char *filename, mdev *dev, uint16_t addr); + uint16_t img_write_r(const char *filename, mdev *dev, uint16_t addr); + +#ifdef __cplusplus +} +#endif + +#endif /* imgenc_h */ diff --git a/osufs/osufs/libpng-wrapper.c b/osufs/osufs/libpng-wrapper.c new file mode 100644 index 0000000000000000000000000000000000000000..5a744f61eeb3a5382ecd160fa4729254502b8f94 --- /dev/null +++ b/osufs/osufs/libpng-wrapper.c @@ -0,0 +1,147 @@ +// +// libpng-wrapper.c +// mp6 +// +// Created by Fang Lu on 11/16/17. +// Adapted from libpng example code at http://zarb.org/~gc/html/libpng.html +// + +#include "libpng-wrapper.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PNGImage pngimg_read(const char* file_name) { + PNGImage ret; + memset(&ret, 0, sizeof(ret)); + uint8_t header[8]; // 8 is the maximum size that can be checked + + /* open file and test for it being a png */ + FILE *fp = fopen(file_name, "rb"); + if (!fp) + return ret; + fread(header, 1, 8, fp); + if (png_sig_cmp(header, 0, 8)) + return ret; + + + /* initialize stuff */ + ret.png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!ret.png_ptr) + return ret; + + ret.info_ptr = png_create_info_struct(ret.png_ptr); + if (!ret.info_ptr) + return ret; + + if (setjmp(png_jmpbuf(ret.png_ptr))) + return ret; + + png_init_io(ret.png_ptr, fp); + png_set_sig_bytes(ret.png_ptr, 8); + + png_read_info(ret.png_ptr, ret.info_ptr); + + ret.width = png_get_image_width(ret.png_ptr, ret.info_ptr); + ret.height = png_get_image_height(ret.png_ptr, ret.info_ptr); + ret.color_type = png_get_color_type(ret.png_ptr, ret.info_ptr); + ret.bit_depth = png_get_bit_depth(ret.png_ptr, ret.info_ptr); + + ret.number_of_passes = png_set_interlace_handling(ret.png_ptr); + png_read_update_info(ret.png_ptr, ret.info_ptr); + + + /* read file */ + if (setjmp(png_jmpbuf(ret.png_ptr))) + return ret; + + size_t rowbytes = png_get_rowbytes(ret.png_ptr,ret.info_ptr); + ret.row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * ret.height); + png_bytep pixdata = (png_bytep) malloc(ret.height * rowbytes); + if (!pixdata) + return ret; + for (int i=0; i<ret.height; i++) + ret.row_pointers[i] = pixdata + i * rowbytes; + + png_read_image(ret.png_ptr, ret.row_pointers); + + fclose(fp); + + return ret; +} + +int pngimg_write(const char* file_name, png_bytep pixdata, + size_t width, size_t height, int alpha) { + /* create file */ + FILE *fp = fopen(file_name, "wb"); + if (!fp) + return 0; + + PNGImage ps; + memset(&ps, 0, sizeof(ps)); + + /* initialize stuff */ + ps.png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!ps.png_ptr) + return 0; + + ps.info_ptr = png_create_info_struct(ps.png_ptr); + if (!ps.info_ptr) + return 0; + + if (setjmp(png_jmpbuf(ps.png_ptr))) + return 0; + + png_init_io(ps.png_ptr, fp); + + + /* write header */ + if (setjmp(png_jmpbuf(ps.png_ptr))) + return 0; + + png_set_IHDR(ps.png_ptr, ps.info_ptr, width, height, 8, + (alpha ? PNG_COLOR_TYPE_RGBA :PNG_COLOR_TYPE_RGB), + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + png_write_info(ps.png_ptr, ps.info_ptr); + + /* write bytes */ + if (setjmp(png_jmpbuf(ps.png_ptr))) + return 0; + + size_t rowbytes = (alpha ? 4 : 3) * width; + ps.row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height); + for (int i=0; i<height; i++) + ps.row_pointers[i] = pixdata + i * rowbytes; + + png_write_image(ps.png_ptr, ps.row_pointers); + + free(ps.row_pointers); + + /* end write */ + if (setjmp(png_jmpbuf(ps.png_ptr))) + return 0; + + png_write_end(ps.png_ptr, NULL); + + png_destroy_write_struct(&ps.png_ptr, &ps.info_ptr); + + fclose(fp); + return 1; +} + +void pngimg_release(PNGImage *img) { + if (img->png_ptr && img->info_ptr) { + png_destroy_read_struct(&img->png_ptr, &img->info_ptr, NULL); + } + if (!img->row_pointers) + return; + if (img->row_pointers[0]) + free(img->row_pointers[0]); + free(img->row_pointers); + memset(img, 0, sizeof(*img)); +} diff --git a/osufs/osufs/libpng-wrapper.h b/osufs/osufs/libpng-wrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..171dc986468028a069dcd63347428298e83cf525 --- /dev/null +++ b/osufs/osufs/libpng-wrapper.h @@ -0,0 +1,41 @@ +// +// libpng-wrapper.h +// mp6 +// +// Created by Fang Lu on 11/16/17. +// Copyright © 2017 Fang Lu. All rights reserved. +// + +#ifndef libpng_wrapper_h +#define libpng_wrapper_h + +#include <inttypes.h> + +#include "png.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct PNGImage_t { + int width, height; + png_byte color_type; + png_byte bit_depth; + + png_structp png_ptr; + png_infop info_ptr; + int number_of_passes; + png_bytep * row_pointers; +} PNGImage; + +PNGImage pngimg_read(const char* file_name); +int pngimg_write(const char* file_name, png_bytep pixdata, + size_t width, size_t height, int alpha); +void pngimg_release(PNGImage *img); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* libpng_wrapper_h */ diff --git a/osufs/osufs/osufs.c b/osufs/osufs/osufs.c index 66cc943e0676fbb1e2ca5a40251653d0d2bab7b3..e41444f8c2112864fef5b9ff7a2cc7d6ece6d540 100644 --- a/osufs/osufs/osufs.c +++ b/osufs/osufs/osufs.c @@ -7,3 +7,36 @@ // #include "osufs.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +osu_meta osu_read_meta(mdev *dev) { + osu_meta ret; + blkio_read(dev, 0, &ret); + return ret; +} + +osu_song osu_read_song(mdev *dev, int idx) { + osu_song ret; + blkio_read(dev, 1+idx, &ret); + return ret; +} + +void osu_write_meta(mdev *dev, osu_meta *meta) { + blkio_write(dev, 0, meta); +} + +void osu_write_song(mdev *dev, int idx, osu_song *song) { + blkio_write(dev, idx+1, song); +} + +void osu_init_device(mdev *dev) { + osu_meta header; + strncpy(header.magic, "osu!", 4); + header.songs = 0; + header.available_block = 64; + osu_write_meta(dev, &header); +} + diff --git a/osufs/osufs/osufs.h b/osufs/osufs/osufs.h index a3e445d92dfe4730a0c1af744bccca7d92806b3b..e0b6af314253b3025acc01102e885a13ec2ea752 100644 --- a/osufs/osufs/osufs.h +++ b/osufs/osufs/osufs.h @@ -14,8 +14,48 @@ extern "C" { #endif #include <stdio.h> +#include <inttypes.h> +#include "blkio.h" + + typedef struct __attribute__((__packed__)) osu_meta_t { + char magic[4]; // 0 - 3 + uint16_t songs; // 4 - 5 + uint16_t available_block; // 6 - 7 + + char _padding[504]; // 8 - 511 + } osu_meta; + typedef struct __attribute__((__packed__)) osu_song_t { + char magic[4]; // 0 - 3 + uint16_t cover_begin, cover_len; // 4 - 7 + uint16_t audio_begin, audio_len; // 8 - 11 + uint16_t beatmap_begin, beatmap_len; // 12 - 15 + int16_t difficulty; // 16 - 17 + int16_t hp_drain; // 18 - 19 + int16_t circle_size; // 20 - 21 + int16_t approach_rate; // 22 - 23 + int16_t slider_mult; // 24 - 25 + int16_t slider_tick; // 26 - 27 + uint8_t combo_colors[3][8]; // 28 - 51 + uint32_t preview_offset; // 52 - 56 + uint32_t beatmap_id; // 56 - 60 + uint32_t beatmap_setid; // 60 - 64 + + char title[64]; // 64 - 127 + char artist[64]; // 128 - 191 + char creator[32]; // 192 - 223 + char version[32]; // 224 - 255 + + char _padding[256]; // 256-511 + } osu_song; + osu_meta osu_read_meta(mdev *dev); + osu_song osu_read_song(mdev *dev, int idx); + void osu_write_meta(mdev *dev, osu_meta *meta); + void osu_write_song(mdev *dev, int idx, osu_song *song); + + void osu_init_device(mdev *dev); + #ifdef __cplusplus } #endif diff --git a/osufs/osufs/wavenc.c b/osufs/osufs/wavenc.c new file mode 100644 index 0000000000000000000000000000000000000000..98641fb014d38b6c42909fbfa4d238b77abfe941 --- /dev/null +++ b/osufs/osufs/wavenc.c @@ -0,0 +1,80 @@ +// +// wavenc.c +// osufs +// +// Created by Fang Lu on 12/3/17. +// Copyright © 2017 Fang Lu. All rights reserved. +// + +#include "wavenc.h" + +#include <stdio.h> +#include <string.h> + +uint16_t wav_write(const char *filename, mdev *dev, uint16_t addr) { + char cbuf[1024]; + int ret; + + system("rm -f resample.wav"); + + // Resample and mixdown channels + sprintf(cbuf, "ffmpeg -i %s -map_metadata -1 -vn -ac 1 -ar 44100 resample.wav", filename); + ret = system(cbuf); + if (ret != 0) { + fprintf(stderr, "ffmpeg resample failed with exit code: %d\n", ret); + return addr; + } + + FILE *fp = fopen("resample.wav", "rb"); + if (!fp) { + perror("Failed to open dithered image"); + return addr; + } + + // Read header + fread(cbuf, 1, 12, fp); + if (strncmp(cbuf, "RIFF", 4) != 0 || strncmp(cbuf+8, "WAVE", 4) != 0) { + fprintf(stderr, "Internal error: ffmpeg output not readable\n"); + fclose(fp); + return addr; + } + + // Find data chunk + uint32_t chunksize; + size_t nread; + while (1) { + nread = fread(cbuf, 1, 4, fp); + if (nread != 4) { + perror("Peek output wave failed"); + fprintf(stderr, "Chould not find audio data chunk\n"); + return addr; + } + nread = fread(&chunksize, 4, 1, fp); + if (nread != 1) { + perror("Peek output wave failed"); + fprintf(stderr, "Could not find audio data chunk\n"); + return addr; + } + if (strncmp(cbuf, "data", 4) == 0) + break; + else { + if (fseek(fp, chunksize, SEEK_CUR) != 0) { + perror("Seek in output wave failed"); + return addr; + } + } + } + uint16_t buf[256]; + while (chunksize > 0) { + nread = fread(buf, 2, 256, fp); + chunksize -= nread; + if (nread != 512 && chunksize != 0) { + perror("Read output wave failed"); + return addr; + } + blkio_write(dev, addr++, buf); + } + + fclose(fp); + return addr; +} diff --git a/osufs/osufs/wavenc.h b/osufs/osufs/wavenc.h new file mode 100644 index 0000000000000000000000000000000000000000..b0691767453a59059ef97da9d0689b0966f6e610 --- /dev/null +++ b/osufs/osufs/wavenc.h @@ -0,0 +1,25 @@ +// +// wavenc.h +// osufs +// +// Created by Fang Lu on 12/3/17. +// Copyright © 2017 Fang Lu. All rights reserved. +// + +#ifndef wavenc_h +#define wavenc_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include "blkio.h" + + uint16_t wav_write(const char *filename, mdev *dev, uint16_t addr); + +#ifdef __cplusplus +} +#endif + +#endif /* wavenc_h */