Преглед изворни кода

Replace OOM checks with light wrappers around allocation functions.

master
Ben Kurtovic пре 9 година
родитељ
комит
13b7b29157
16 измењених фајлова са 108 додато и 172 уклоњено
  1. +1
    -4
      src/assembler.c
  2. +6
    -16
      src/assembler/errors.c
  3. +3
    -8
      src/assembler/hash_table.c
  4. +4
    -7
      src/assembler/instructions.c
  5. +4
    -11
      src/assembler/io.c
  6. +2
    -7
      src/assembler/parse_util.c
  7. +8
    -30
      src/assembler/preprocessor.c
  8. +9
    -33
      src/assembler/tokenizer.c
  9. +7
    -21
      src/config.c
  10. +2
    -8
      src/gamegear.c
  11. +13
    -11
      src/logging.h
  12. +3
    -9
      src/mmu.c
  13. +1
    -1
      src/mmu.h
  14. +3
    -6
      src/rom.c
  15. +2
    -0
      src/util.h
  16. +40
    -0
      src/util_alloc.h

+ 1
- 4
src/assembler.c Прегледај датотеку

@@ -201,10 +201,7 @@ size_t assemble(const LineBuffer *source, uint8_t **binary_ptr, ErrorInfo **ei_p
if ((error_info = resolve_symbols(&state)))
goto error;

uint8_t *binary = malloc(sizeof(uint8_t) * state.rom_size);
if (!binary)
OUT_OF_MEMORY()

uint8_t *binary = cr_malloc(sizeof(uint8_t) * state.rom_size);
serialize_binary(&state, binary);
*binary_ptr = binary;
retval = state.rom_size;


+ 6
- 16
src/assembler/errors.c Прегледај датотеку

@@ -6,7 +6,7 @@
#include "errors.h"
#include "state.h"
#include "../assembler.h"
#include "../logging.h"
#include "../util.h"

/* Error strings */

@@ -82,27 +82,20 @@ struct ErrorInfo {
*/
static ASMErrorLine* create_error_line(const ASMLine *line)
{
ASMErrorLine *el = malloc(sizeof(ASMErrorLine));
if (!el)
OUT_OF_MEMORY()

ASMErrorLine *el = cr_malloc(sizeof(ASMErrorLine));
const char *source = line->original->data;
size_t length = line->original->length;
if (!(el->data = malloc(sizeof(char) * length)))
OUT_OF_MEMORY()

// Ignore spaces at beginning:
while (length > 0 && (*source == ' ' || *source == '\t'))
source++, length--;

el->data = cr_malloc(sizeof(char) * length);
memcpy(el->data, source, length);

el->length = length;
el->lineno = line->original->lineno;

el->filename = strdup(line->filename);
if (!el->filename)
OUT_OF_MEMORY()

el->filename = cr_strdup(line->filename);
el->next = NULL;
return el;
}
@@ -119,10 +112,7 @@ static ASMErrorLine* create_error_line(const ASMLine *line)
ErrorInfo* error_info_create(
const ASMLine *line, ASMErrorType err_type, ASMErrorDesc err_desc)
{
ErrorInfo *einfo = malloc(sizeof(ErrorInfo));
if (!einfo)
OUT_OF_MEMORY()

ErrorInfo *einfo = cr_malloc(sizeof(ErrorInfo));
einfo->type = err_type;
einfo->desc = err_desc;
einfo->line = line ? create_error_line(line) : NULL;


+ 3
- 8
src/assembler/hash_table.c Прегледај датотеку

@@ -5,7 +5,7 @@
#include <string.h>

#include "hash_table.h"
#include "../logging.h"
#include "../util.h"

#define INITIAL_BUCKETS 127

@@ -61,13 +61,8 @@ static inline bool keyeq(const char *s1, const char *s2, ssize_t size)
HashTable* hash_table_new(
size_t key_offset, size_t next_offset, HashFreeCallback callback)
{
HashTable *table;
if (!(table = malloc(sizeof(HashTable))))
OUT_OF_MEMORY()

if (!(table->nodes = calloc(INITIAL_BUCKETS, sizeof(HashNode*))))
OUT_OF_MEMORY()

HashTable *table = cr_malloc(sizeof(HashTable));
table->nodes = cr_calloc(INITIAL_BUCKETS, sizeof(HashNode*));
table->buckets = INITIAL_BUCKETS;
table->key_offset = key_offset;
table->next_offset = next_offset;


+ 4
- 7
src/assembler/instructions.c Прегледај датотеку

@@ -7,7 +7,7 @@
#include "instructions.h"
#include "inst_args.h"
#include "parse_util.h"
#include "../logging.h"
#include "../util.h"

/* Helper macros for get_inst_parser() */

@@ -26,8 +26,7 @@

#define INST_ALLOC_(len) \
*length = len; \
if (!(*bytes = malloc(sizeof(uint8_t) * (len)))) \
OUT_OF_MEMORY()
*bytes = cr_malloc(sizeof(uint8_t) * (len));

#define INST_SET_(b, val) ((*bytes)[b] = val)
#define INST_SET1_(b1) INST_SET_(0, b1)
@@ -55,7 +54,7 @@ static ASMErrorDesc parse_inst_##mnemonic( \

#define INST_TAKES_NO_ARGS \
if (ap_info.arg) \
INST_ERROR(TOO_MANY_ARGS) \
INST_ERROR(TOO_MANY_ARGS)

#define INST_TAKES_ARGS(lo, hi) \
if (!ap_info.arg) \
@@ -104,9 +103,7 @@ static ASMErrorDesc parse_inst_##mnemonic( \
}

#define INST_RETURN_WITH_SYMBOL(len, label, ...) { \
*symbol = strdup(label); \
if (!(*symbol)) \
OUT_OF_MEMORY() \
*symbol = cr_strdup(label); \
INST_ALLOC_(len) \
INST_FILL_BYTES_(len - 2, __VA_ARGS__) \
return ED_NONE; \


+ 4
- 11
src/assembler/io.c Прегледај датотеку

@@ -9,6 +9,7 @@

#include "io.h"
#include "../logging.h"
#include "../util.h"

/*
Deallocate a LineBuffer previously created with read_source_file().
@@ -59,14 +60,9 @@ LineBuffer* read_source_file(const char *path, bool print_errors)
return NULL;
}

LineBuffer *source = malloc(sizeof(LineBuffer));
if (!source)
OUT_OF_MEMORY()

LineBuffer *source = cr_malloc(sizeof(LineBuffer));
source->lines = NULL;
source->filename = strdup(path);
if (!source->filename)
OUT_OF_MEMORY()
source->filename = cr_strdup(path);

Line dummy = {.next = NULL};
Line *line, *prev = &dummy;
@@ -90,10 +86,7 @@ LineBuffer* read_source_file(const char *path, bool print_errors)
return NULL;
}

line = malloc(sizeof(Line));
if (!line)
OUT_OF_MEMORY()

line = cr_malloc(sizeof(Line));
line->data = data;
line->length = feof(fp) ? len : (len - 1);
line->lineno = lineno++;


+ 2
- 7
src/assembler/parse_util.c Прегледај датотеку

@@ -7,7 +7,6 @@

#include "parse_util.h"
#include "directives.h"
#include "../logging.h"
#include "../util.h"

#define MAX_REGION_SIZE 32
@@ -183,9 +182,7 @@ bool parse_string(char **result, size_t *length, const char *arg, ssize_t size)
return false;

*length = size - 2;
*result = malloc(sizeof(char) * (*length));
if (!*result)
OUT_OF_MEMORY()
*result = cr_malloc(sizeof(char) * (*length));
memcpy(*result, arg + 1, *length);
return true;
}
@@ -217,9 +214,7 @@ bool parse_bytes(uint8_t **result, size_t *length, const char *arg, ssize_t size
}

nbytes++;
bytes = realloc(bytes, sizeof(uint8_t) * nbytes);
if (!bytes)
OUT_OF_MEMORY()
bytes = cr_realloc(bytes, sizeof(uint8_t) * nbytes);
bytes[nbytes - 1] = temp;

if (arg < end - 1 && *arg == ',' && *(arg + 1) == ' ')


+ 8
- 30
src/assembler/preprocessor.c Прегледај датотеку

@@ -121,14 +121,8 @@ static size_t read_labels(
return 0;
}

ASMLine *line = malloc(sizeof(ASMLine));
if (!line)
OUT_OF_MEMORY()

line->data = malloc(sizeof(char) * (i - start + 1));
if (!line->data)
OUT_OF_MEMORY()

ASMLine *line = cr_malloc(sizeof(ASMLine));
line->data = cr_malloc(sizeof(char) * (i - start + 1));
memcpy_lc(line->data, source + start, i - start + 1);
line->length = i - start + 1;
line->is_label = true;
@@ -160,10 +154,7 @@ static ASMLine* normalize_line(const char *source, size_t length)
source += offset;
length -= offset;

char *data = malloc(sizeof(char) * length);
if (!data)
OUT_OF_MEMORY()

char *data = cr_malloc(sizeof(char) * length);
size_t si, di, slashes = 0;
bool has_content = false, space_pending = false, in_string = false;
for (si = di = 0; si < length; si++) {
@@ -206,14 +197,8 @@ static ASMLine* normalize_line(const char *source, size_t length)
return head;
}

ASMLine *line = malloc(sizeof(ASMLine));
if (!line)
OUT_OF_MEMORY()

data = realloc(data, sizeof(char) * di);
if (!data)
OUT_OF_MEMORY()

ASMLine *line = cr_malloc(sizeof(ASMLine));
data = cr_realloc(data, sizeof(char) * di);
line->data = data;
line->length = di;
line->is_label = false;
@@ -239,10 +224,7 @@ static char* read_include_path(const ASMLine *line)
if (maxlen >= INT_MAX) // Allows us to safely downcast to int later
return NULL;

char *path = malloc(sizeof(char) * maxlen), *base, *dup;
if (!path)
OUT_OF_MEMORY()

char *path = cr_malloc(sizeof(char) * maxlen), *base, *dup;
if (!(i = DIRECTIVE_OFFSET(line, DIR_INCLUDE)))
goto error;
if (line->length - i <= 3) // Not long enough to hold a non-zero argument
@@ -252,8 +234,7 @@ static char* read_include_path(const ASMLine *line)
if (!parse_string(&base, &baselen, line->data + i, line->length - i))
goto error;

if (!(dup = strdup(line->filename)))
OUT_OF_MEMORY()
dup = cr_strdup(line->filename);

// TODO: should normalize filenames in some way to prevent accidental dupes
snprintf(path, maxlen, "%s/%.*s", dirname(dup), (int) baselen, base);
@@ -330,10 +311,7 @@ static ErrorInfo* build_asm_lines(
goto error;
}

ASMInclude *include = malloc(sizeof(ASMInclude));
if (!include)
OUT_OF_MEMORY()

ASMInclude *include = cr_malloc(sizeof(ASMInclude));
include->lines = incbuffer;
include->next = *includes;
*includes = include;


+ 9
- 33
src/assembler/tokenizer.c Прегледај датотеку

@@ -9,7 +9,6 @@
#include "instructions.h"
#include "inst_args.h"
#include "parse_util.h"
#include "../logging.h"
#include "../mmu.h"
#include "../rom.h"
#include "../util.h"
@@ -63,12 +62,8 @@ static void init_layout_info(ASMLayoutInfo *li, AssemblerState *state)
li->origin = NULL;
li->bank = 0;
li->cross_blocks = state->cross_blocks;

if (!(li->overlap_table = calloc(li->size, sizeof(const ASMLine*))))
OUT_OF_MEMORY()

if (!(li->overlap_origins = calloc(li->size, sizeof(const ASMLine*))))
OUT_OF_MEMORY()
li->overlap_table = cr_calloc(li->size, sizeof(const ASMLine*));
li->overlap_origins = cr_calloc(li->size, sizeof(const ASMLine*));

for (size_t i = 0; i < HEADER_SIZE; i++)
li->overlap_table[state->header.offset + i] = &header_sentinel;
@@ -104,10 +99,7 @@ static ErrorInfo* add_label_to_table(
if (argparse_condition(&cond, info))
return error_info_create(line, ET_SYMBOL, ED_SYM_IS_CONDITION);

char *symbol = strndup(line->data, line->length - 1);
if (!symbol)
OUT_OF_MEMORY()

char *symbol = cr_strndup(line->data, line->length - 1);
const ASMSymbol *current = asm_symtable_find(symtable, symbol);
if (current) {
ErrorInfo *ei = error_info_create(line, ET_SYMBOL, ED_SYM_DUPE_LABELS);
@@ -116,10 +108,7 @@ static ErrorInfo* add_label_to_table(
return ei;
}

ASMSymbol *label = malloc(sizeof(ASMSymbol));
if (!label)
OUT_OF_MEMORY()

ASMSymbol *label = cr_malloc(sizeof(ASMSymbol));
label->offset = map_into_slot(offset,
(slot >= 0) ? slot : default_bank_slot(offset / MMU_ROM_BANK_SIZE));
label->symbol = symbol;
@@ -169,13 +158,8 @@ static ErrorInfo* handle_define_directive(
if (!argparse_immediate(&imm, info))
return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG);

ASMDefine *define = malloc(sizeof(ASMDefine));
if (!define)
OUT_OF_MEMORY()

if (!(define->name = strndup(key, keylen)))
OUT_OF_MEMORY()

ASMDefine *define = cr_malloc(sizeof(ASMDefine));
define->name = cr_strndup(key, keylen);
define->value = imm;
define->line = line;
asm_deftable_insert(deftab, define);
@@ -285,9 +269,7 @@ static bool parse_space(
}

*length = bytes[0];
if (!(*result = malloc(sizeof(uint8_t) * (*length))))
OUT_OF_MEMORY()

*result = cr_malloc(sizeof(uint8_t) * (*length));
memset(*result, nbytes == 2 ? bytes[1] : 0, *length);
free(bytes);
return true;
@@ -328,10 +310,7 @@ static ErrorInfo* parse_data(
const char *arg = line->data + dir_offset;
size_t arglen = line->length - dir_offset;

ASMData *data = malloc(sizeof(ASMData));
if (!data)
OUT_OF_MEMORY()

ASMData *data = cr_malloc(sizeof(ASMData));
data->loc.offset = offset;
data->next = NULL;

@@ -386,10 +365,7 @@ static ErrorInfo* parse_instruction(
if (edesc != ED_NONE)
return error_info_create(line, ET_PARSER, edesc);

ASMInstruction *inst = malloc(sizeof(ASMInstruction));
if (!inst)
OUT_OF_MEMORY()

ASMInstruction *inst = cr_malloc(sizeof(ASMInstruction));
inst->loc.offset = offset;
inst->loc.length = length;
inst->bytes = bytes;


+ 7
- 21
src/config.c Прегледај датотеку

@@ -10,6 +10,7 @@

#include "config.h"
#include "logging.h"
#include "util.h"
#include "version.h"

/*
@@ -73,21 +74,15 @@ static int get_rom_paths(char ***path_ptr)

dirp = opendir(ROMS_DIR);
if (dirp) {
paths = malloc(sizeof(char*) * psize);
if (!paths)
OUT_OF_MEMORY()
paths = cr_malloc(sizeof(char*) * psize);
while ((entry = readdir(dirp))) {
path = entry->d_name;
if (ends_with(path, ".gg") || ends_with(path, ".bin")) {
if (npaths >= psize) {
paths = realloc(paths, sizeof(char*) * (psize *= 2));
if (!paths)
OUT_OF_MEMORY()
paths = cr_realloc(paths, sizeof(char*) * (psize *= 2));
}
paths[npaths] = malloc(sizeof(char*) *
paths[npaths] = cr_malloc(sizeof(char*) *
(strlen(path) + strlen(ROMS_DIR) + 1));
if (!paths[npaths])
OUT_OF_MEMORY()
strcpy(paths[npaths], ROMS_DIR "/");
strcat(paths[npaths], path);
npaths++;
@@ -159,9 +154,7 @@ static int parse_args(Config *config, int argc, char *argv[])
return CONFIG_EXIT_FAILURE;
}

path = malloc(sizeof(char) * (strlen(arg) + 1));
if (!path)
OUT_OF_MEMORY()
path = cr_malloc(sizeof(char) * (strlen(arg) + 1));
strcpy(path, arg);

if (paths_read == 1) {
@@ -264,9 +257,7 @@ static void guess_assembler_output_file(Config* config)
}
} while (ptr-- >= src);

config->dst_path = malloc(sizeof(char) * (until_ext + 5));
if (!config->dst_path)
OUT_OF_MEMORY()
config->dst_path = cr_malloc(sizeof(char) * (until_ext + 5));
strcpy(stpncpy(config->dst_path, src, until_ext), ext);
}

@@ -315,14 +306,9 @@ static bool sanity_check(Config* config)
*/
int config_create(Config** config_ptr, int argc, char* argv[])
{
Config *config;
Config *config = cr_malloc(sizeof(Config));
int retval;

if (!(config = malloc(sizeof(Config)))) {
OUT_OF_MEMORY()
return CONFIG_EXIT_FAILURE;
}

config->debug = false;
config->assemble = false;
config->disassemble = false;


+ 2
- 8
src/gamegear.c Прегледај датотеку

@@ -12,17 +12,11 @@

/*
Create and return a pointer to a new GameGear object.

If memory could not be allocated, OUT_OF_MEMORY() is triggered.
*/
GameGear* gamegear_create()
{
GameGear *gg = malloc(sizeof(GameGear));
if (!gg)
OUT_OF_MEMORY()

if (!mmu_init(&gg->mmu))
OUT_OF_MEMORY()
GameGear *gg = cr_malloc(sizeof(GameGear));
mmu_init(&gg->mmu);
z80_init(&gg->cpu, &gg->mmu);
gg->powered = false;
gg->exc_buffer[0] = '\0';


+ 13
- 11
src/logging.h Прегледај датотеку

@@ -10,27 +10,29 @@

/* Internal usage only */

#define LOG_MSG(dest, level, extra, after, ...) { \
#define LOG_MSG_(dest, level, extra, after, ...) { \
fprintf(dest, level ": " __VA_ARGS__); \
extra; \
fprintf(dest, "\n"); \
after; \
}
#define PRINT_ERRNO() fprintf(stderr, ": %s", strerror(errno))

#define LOG_ERR_(...) LOG_MSG_(stderr, __VA_ARGS__)
#define LOG_OUT_(...) LOG_MSG_(stdout, __VA_ARGS__)

#define PRINT_ERRNO_() fprintf(stderr, ": %s", strerror(errno))

/* Public logging macros */

#define FATAL(...) LOG_MSG(stderr, "fatal", {}, exit(EXIT_FAILURE), __VA_ARGS__)
#define FATAL_ERRNO(...) LOG_MSG(stderr, "fatal", PRINT_ERRNO(), exit(EXIT_FAILURE), __VA_ARGS__)
#define ERROR(...) LOG_MSG(stderr, "error", {}, {}, __VA_ARGS__)
#define ERROR_ERRNO(...) LOG_MSG(stderr, "error", PRINT_ERRNO(), {}, __VA_ARGS__)
#define WARN(...) LOG_MSG(stderr, "warning", {}, {}, __VA_ARGS__)
#define WARN_ERRNO(...) LOG_MSG(stderr, "warning", PRINT_ERRNO(), {}, __VA_ARGS__)
#define FATAL(...) LOG_ERR_("fatal", {}, exit(EXIT_FAILURE), __VA_ARGS__)
#define FATAL_ERRNO(...) LOG_ERR_("fatal", PRINT_ERRNO_(), exit(EXIT_FAILURE), __VA_ARGS__)
#define ERROR(...) LOG_ERR_("error", {}, {}, __VA_ARGS__)
#define ERROR_ERRNO(...) LOG_ERR_("error", PRINT_ERRNO_(), {}, __VA_ARGS__)
#define WARN(...) LOG_ERR_("warning", {}, {}, __VA_ARGS__)
#define WARN_ERRNO(...) LOG_ERR_("warning", PRINT_ERRNO_(), {}, __VA_ARGS__)

#ifdef DEBUG_MODE
#define DEBUG(...) LOG_MSG(stdout, "[DEBUG]", {}, {}, __VA_ARGS__)
#define DEBUG(...) LOG_OUT_("[DEBUG]", {}, {}, __VA_ARGS__)
#else
#define DEBUG(...) {}
#endif

#define OUT_OF_MEMORY() FATAL("couldn't allocate enough memory")

+ 3
- 9
src/mmu.c Прегледај датотеку

@@ -5,27 +5,21 @@
#include <string.h>

#include "logging.h"
#include "util.h"
#include "z80.h"

/*
Initialize a MMU object. This must be called before using the MMU.

Return true if initialization was successful, or false if the required
amount of memory could not be allocated.
*/
bool mmu_init(MMU *mmu)
void mmu_init(MMU *mmu)
{
mmu->system_ram = malloc(sizeof(uint8_t) * MMU_SYSTEM_RAM_SIZE);
if (!mmu->system_ram)
return false;
mmu->system_ram = cr_malloc(sizeof(uint8_t) * MMU_SYSTEM_RAM_SIZE);

for (size_t slot = 0; slot < MMU_NUM_SLOTS; slot++)
mmu->map_slots[slot] = NULL;

for (size_t bank = 0; bank < MMU_NUM_ROM_BANKS; bank++)
mmu->rom_banks[bank] = NULL;

return true;
}

/*


+ 1
- 1
src/mmu.h Прегледај датотеку

@@ -22,7 +22,7 @@ typedef struct {

/* Functions */

bool mmu_init(MMU*);
void mmu_init(MMU*);
void mmu_free(MMU*);
void mmu_load_rom(MMU*, const uint8_t*, size_t);
void mmu_power(MMU*);


+ 3
- 6
src/rom.c Прегледај датотеку

@@ -169,8 +169,7 @@ const char* rom_open(ROM **rom_ptr, const char *path)
return (st.st_mode & S_IFDIR) ? rom_err_isdir : rom_err_notfile;
}

if (!(rom = malloc(sizeof(ROM))))
OUT_OF_MEMORY()
rom = cr_malloc(sizeof(ROM));

// Set defaults:
rom->name = NULL;
@@ -183,8 +182,7 @@ const char* rom_open(ROM **rom_ptr, const char *path)
rom->region_code = 0;

// Set rom->name:
if (!(rom->name = malloc(sizeof(char) * (strlen(path) + 1))))
OUT_OF_MEMORY()
rom->name = cr_malloc(sizeof(char) * (strlen(path) + 1));
strcpy(rom->name, path);
DEBUG("Loading ROM %s:", rom->name)

@@ -198,8 +196,7 @@ const char* rom_open(ROM **rom_ptr, const char *path)
rom->size = st.st_size;

// Set rom->data:
if (!(rom->data = malloc(sizeof(uint8_t) * st.st_size)))
OUT_OF_MEMORY()
rom->data = cr_malloc(sizeof(uint8_t) * st.st_size);
if (!(fread(rom->data, st.st_size, 1, fp))) {
rom_close(rom);
fclose(fp);


+ 2
- 0
src/util.h Прегледај датотеку

@@ -6,6 +6,8 @@
#include <stdbool.h>
#include <stdint.h>

#include "util_alloc.h"

#define INVALID_SIZE_CODE 0x8

/* Functions */


+ 40
- 0
src/util_alloc.h Прегледај датотеку

@@ -0,0 +1,40 @@
/* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
Released under the terms of the MIT License. See LICENSE for details. */

#pragma once

#include <stddef.h>
#include <stdlib.h>
#include <string.h>

#include "logging.h"

#define OUT_OF_MEMORY() FATAL("couldn't allocate enough memory")

#define OOM_GUARD_(type, call) \
type ptr = call; \
if (!ptr) \
OUT_OF_MEMORY() \
return ptr;

#define OOM_GUARD_FUNC_1_(ret_type, func, arg_type) \
static inline ret_type cr_##func(arg_type arg1) { \
OOM_GUARD_(ret_type, func(arg1)) \
}

#define OOM_GUARD_FUNC_2_(ret_type, func, arg1_type, arg2_type) \
static inline ret_type cr_##func(arg1_type arg1, arg2_type arg2) { \
OOM_GUARD_(ret_type, func(arg1, arg2)) \
}

/* Functions */

OOM_GUARD_FUNC_1_(void*, malloc, size_t) // cr_malloc
OOM_GUARD_FUNC_2_(void*, calloc, size_t, size_t) // cr_calloc
OOM_GUARD_FUNC_2_(void*, realloc, void*, size_t) // cr_realloc
OOM_GUARD_FUNC_1_(char*, strdup, const char*) // cr_strdup
OOM_GUARD_FUNC_2_(char*, strndup, const char*, size_t) // cr_strndup

#undef OOM_GUARD_FUNC2_
#undef OOM_GUARD_FUNC1_
#undef OOM_GUARD_

Loading…
Откажи
Сачувај