Skip to content

Commit

Permalink
Don't rely on file extensions when opening images
Browse files Browse the repository at this point in the history
Instead check the file magic. Especially on Android the reported
extension may be nonsense. This also fiddles with PSD loading a bit to
use the DP_Input that was used to read the magic directly instead of
opening a fresh file handle from a path.
  • Loading branch information
askmeaboutlo0m committed Nov 4, 2023
1 parent 8cdb8ac commit 552f36f
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 46 deletions.
6 changes: 2 additions & 4 deletions src/drawdance/bundled/psd_sdk/PsdFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@ File::~File(void)

// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
bool File::OpenRead(const char* filename)
bool File::OpenRead(void* user)
{
PSD_ASSERT_NOT_NULL(filename);

return DoOpenRead(filename);
return DoOpenRead(user);
}


Expand Down
4 changes: 2 additions & 2 deletions src/drawdance/bundled/psd_sdk/PsdFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class File
virtual ~File(void);

/// Tries to open a file for reading, and returns whether the operation was successful.
bool OpenRead(const char* filename);
bool OpenRead(void* user);

/// Tries to close a file, and returns whether the operation was successful.
bool Close(void);
Expand All @@ -53,7 +53,7 @@ class File
Allocator* m_allocator;

private:
virtual bool DoOpenRead(const char* filename) PSD_ABSTRACT;
virtual bool DoOpenRead(void* filename) PSD_ABSTRACT;
virtual bool DoClose(void) PSD_ABSTRACT;

virtual ReadOperation DoRead(void* buffer, uint32_t count, uint64_t position) PSD_ABSTRACT;
Expand Down
50 changes: 29 additions & 21 deletions src/drawdance/libengine/dpengine/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,40 +40,46 @@ struct DP_Image {
};


DP_Image *DP_image_new(int width, int height)
{
DP_ASSERT(width > 0);
DP_ASSERT(height > 0);
size_t count = DP_int_to_size(width) * DP_int_to_size(height);
DP_Image *img = DP_malloc_zeroed(DP_FLEX_SIZEOF(DP_Image, pixels, count));
img->width = width;
img->height = height;
return img;
}

static void assign_type(DP_ImageFileType *out_type, DP_ImageFileType type)
{
if (out_type) {
*out_type = type;
}
}

static bool guess_png(unsigned char *buf, size_t size)
static bool guess_png(const unsigned char *buf, size_t size)
{
unsigned char sig[] = {0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa};
return size >= sizeof(sig) && memcmp(buf, sig, sizeof(sig)) == 0;
}

static bool guess_jpeg(unsigned char *buf, size_t size)
static bool guess_jpeg(const unsigned char *buf, size_t size)
{
return size >= 4 && buf[0] == 0xff && buf[1] == 0xd8 && buf[2] == 0xff
&& ((buf[3] >= 0xe0 && buf[3] <= 0xef) || buf[3] == 0xdb);
}

static DP_Image *read_image_guess(DP_Input *input, DP_ImageFileType *out_type)
DP_Image *DP_image_new_from_file_guess(DP_Input *input,
const unsigned char *buf, size_t size,
DP_ImageFileType *out_type)
{
unsigned char buf[8];
bool error;
size_t read = DP_input_read(input, buf, sizeof(buf), &error);
if (error) {
return NULL;
}

DP_Image *(*read_fn)(DP_Input *);
if (guess_png(buf, read)) {
if (guess_png(buf, size)) {
assign_type(out_type, DP_IMAGE_FILE_TYPE_PNG);
read_fn = DP_image_png_read;
}
else if (guess_jpeg(buf, read)) {
else if (guess_jpeg(buf, size)) {
assign_type(out_type, DP_IMAGE_FILE_TYPE_JPEG);
read_fn = DP_image_jpeg_read;
}
Expand All @@ -83,23 +89,25 @@ static DP_Image *read_image_guess(DP_Input *input, DP_ImageFileType *out_type)
return NULL;
}

if (DP_input_rewind_by(input, read)) {
if (DP_input_rewind_by(input, size)) {
return read_fn(input);
}
else {
return NULL;
}
}

DP_Image *DP_image_new(int width, int height)
static DP_Image *read_image_guess(DP_Input *input, DP_ImageFileType *out_type)
{
DP_ASSERT(width > 0);
DP_ASSERT(height > 0);
size_t count = DP_int_to_size(width) * DP_int_to_size(height);
DP_Image *img = DP_malloc_zeroed(DP_FLEX_SIZEOF(DP_Image, pixels, count));
img->width = width;
img->height = height;
return img;
unsigned char buf[8];
bool error;
size_t read = DP_input_read(input, buf, sizeof(buf), &error);
if (error) {
return NULL;
}
else {
return DP_image_new_from_file_guess(input, buf, read, out_type);
}
}

DP_Image *DP_image_new_from_file(DP_Input *input, DP_ImageFileType type,
Expand Down
6 changes: 6 additions & 0 deletions src/drawdance/libengine/dpengine/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ typedef DP_Pixel8 (*DP_ImageGetPixelFn)(void *user, int x, int y);

DP_Image *DP_image_new(int width, int height);

// Expects `size` bytes to have been read into `buf` to check file magic. 8
// bytes are enough. This function takes care of rewinding `input` if needed.
DP_Image *DP_image_new_from_file_guess(DP_Input *input,
const unsigned char *buf, size_t size,
DP_ImageFileType *out_type);

DP_Image *DP_image_new_from_file(DP_Input *input, DP_ImageFileType type,
DP_ImageFileType *out_type);

Expand Down
55 changes: 42 additions & 13 deletions src/drawdance/libengine/dpengine/load.c
Original file line number Diff line number Diff line change
Expand Up @@ -1210,11 +1210,11 @@ static DP_CanvasState *load_ora(DP_DrawContext *dc, const char *path,

DP_CanvasState *load_flat_image(DP_DrawContext *dc, DP_Input *input,
const char *flat_image_layer_title,
const unsigned char *buf, size_t size,
DP_LoadResult *out_result)
{
DP_ImageFileType type;
DP_Image *img =
DP_image_new_from_file(input, DP_IMAGE_FILE_TYPE_GUESS, &type);
DP_Image *img = DP_image_new_from_file_guess(input, buf, size, &type);
if (!img) {
assign_load_result(out_result, type == DP_IMAGE_FILE_TYPE_UNKNOWN
? DP_LOAD_RESULT_UNKNOWN_FORMAT
Expand Down Expand Up @@ -1258,28 +1258,57 @@ DP_CanvasState *load_flat_image(DP_DrawContext *dc, DP_Input *input,
}


static bool guess_zip(const unsigned char *buf, size_t size)
{
return size >= 4 && buf[0] == 0x50 && buf[1] == 0x4B && buf[2] == 0x03
&& (buf[3] == 0x04 || buf[3] == 0x06 || buf[3] == 0x08);
}

static bool guess_psd(const unsigned char *buf, size_t size)
{
return size >= 4 && buf[0] == 0x38 && buf[1] == 0x42 && buf[2] == 0x50
&& buf[3] == 0x53;
}

static DP_CanvasState *load(DP_DrawContext *dc, const char *path,
const char *flat_image_layer_title,
DP_LoadResult *out_result)
{
const char *dot = strrchr(path, '.');
if (DP_str_equal_lowercase(dot, ".ora")) {
return load_ora(dc, path, NULL, NULL, out_result);
}

if (DP_str_equal_lowercase(dot, ".psd")) {
return DP_load_psd(dc, path, out_result);
}

DP_Input *input = DP_file_input_new_from_path(path);
if (!input) {
assign_load_result(out_result, DP_LOAD_RESULT_OPEN_ERROR);
return NULL;
}

DP_CanvasState *cs =
load_flat_image(dc, input, flat_image_layer_title, out_result);
unsigned char buf[8];
bool error;
size_t read = DP_input_read(input, buf, sizeof(buf), &error);
if (error) {
assign_load_result(out_result, DP_LOAD_RESULT_READ_ERROR);
return NULL;
}

// We could also check if there's an uncompressed mimetype file at the
// beginning of the ZIP archive like the spec says, but since we only
// support one ZIP-based format, that's not necessary and would preclude ORA
// files with the pretty unimportant defect of compressing a file wrong.
if (guess_zip(buf, read)) {
DP_input_free(input);
return load_ora(dc, path, NULL, NULL, out_result);
}

if (guess_psd(buf, read)) {
if (DP_input_rewind_by(input, read)) {
return DP_load_psd(dc, input, out_result);
}
else {
assign_load_result(out_result, DP_LOAD_RESULT_READ_ERROR);
return NULL;
}
}

DP_CanvasState *cs = load_flat_image(dc, input, flat_image_layer_title, buf,
read, out_result);
DP_input_free(input);
return cs;
}
Expand Down
3 changes: 2 additions & 1 deletion src/drawdance/libengine/dpengine/load.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

typedef struct DP_CanvasState DP_CanvasState;
typedef struct DP_DrawContext DP_DrawContext;
typedef struct DP_Input DP_Input;
typedef struct DP_Player DP_Player;


Expand Down Expand Up @@ -61,7 +62,7 @@ DP_CanvasState *DP_load_ora(DP_DrawContext *dc, const char *path,
DP_LoadFixedLayerFn on_fixed_layer, void *user,
DP_LoadResult *out_result);

DP_CanvasState *DP_load_psd(DP_DrawContext *dc, const char *path,
DP_CanvasState *DP_load_psd(DP_DrawContext *dc, DP_Input *input,
DP_LoadResult *out_result);

DP_Player *DP_load_recording(const char *path, DP_LoadResult *out_result);
Expand Down
10 changes: 5 additions & 5 deletions src/drawdance/libengine/dpengine/load_psd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ class DP_PsdFile final : public psd::File {
DP_PsdFile &operator=(DP_PsdFile &&) = delete;

private:
bool DoOpenRead(const char *filename) override
bool DoOpenRead(void *user) override
{
m_input = DP_file_input_new_from_path(filename);
m_input = static_cast<DP_Input *>(user);
return m_input != nullptr;
}

Expand Down Expand Up @@ -401,14 +401,14 @@ static DP_TransientCanvasState *extract_layers(psd::Document *document,
return tcs;
}

extern "C" DP_CanvasState *DP_load_psd(DP_DrawContext *dc, const char *path,
extern "C" DP_CanvasState *DP_load_psd(DP_DrawContext *dc, DP_Input *input,
DP_LoadResult *out_result)
{
psd::MallocAllocator allocator;
DP_PsdFile file(&allocator);
bool opened = file.OpenRead(path);
bool opened = file.OpenRead(input);
if (!opened) {
DP_error_set("Failed to open PSD file '%s'", path);
DP_error_set("Failed to open PSD file");
assign_load_result(out_result, DP_LOAD_RESULT_OPEN_ERROR);
return nullptr;
}
Expand Down

0 comments on commit 552f36f

Please sign in to comment.