From 8e855fa2c12fdadcb43c298c83762ea7ee681e3e Mon Sep 17 00:00:00 2001 From: Raimon Zamora Date: Sat, 31 May 2025 14:28:20 +0200 Subject: [PATCH] =?UTF-8?q?-=20[FIX]=20No=20permetia=20carregar=20GIFs=20d?= =?UTF-8?q?e=20mes=20de=20256=20pixels=20de=20ample=20o=20alt=20-=20[FIX]?= =?UTF-8?q?=20No=20tornava=20be=20la=20ruta=20al=20arxiu=20de=20configurac?= =?UTF-8?q?i=C3=B3=20-=20[NEW]=20Ara=20guarda=20en=20"~/.config/jailgames/?= =?UTF-8?q?loquesiga/"=20-=20[FIX]=20El=20mapa=20de=20tiles=20ha=20de=20pi?= =?UTF-8?q?llar=20els=20tiles=20de=20la=20surface=20source=20-=20[NEW]=20A?= =?UTF-8?q?ctualitzada=20la=20llibreria=20de=20autocompletar=20per=20a=20v?= =?UTF-8?q?scode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/main.lua | 104 +------------ gif.c | 4 +- jfile.cpp | 25 ++- lua.cpp | 8 +- vscode/library.lua | 376 ++++++++++++++++++++++----------------------- 5 files changed, 219 insertions(+), 298 deletions(-) diff --git a/data/main.lua b/data/main.lua index a10fe78..9db6bba 100644 --- a/data/main.lua +++ b/data/main.lua @@ -3,104 +3,14 @@ other = require "other" x=0 function mini.init() - text=other.peiv() - keyRight = tonumber(config.getKey("keyright")) or key.RIGHT - keyLeft = tonumber(config.getKey("keyleft")) or key.LEFT - mini.update=normal_update - --turbo(false) - local perico = "péricòñ" - print(utf8.len(perico)) - ants = 0xc936; - s = surface.load("tiles01.gif") - --surface.source(s) - p = palette.load("tiles01.gif") - palette.set(p) - palette.setTransparent(255) - print(#p) - surface.save(s, "data/copy.gif") - s = surface.load("copy.gif") - --draw.source(s) - system.setBeat(4) - + s = surf.load("sprites.gif") + surf.source(s) + p = pal.load("sprites.gif") + pal.set(p) + pal.trans(255) end function mini.update() - if keyboard.keyPressed(key.ESCAPE) then - system.quit() - end - - if system.isBeat() then - ants = (ants >> 12) | ((ants<<4)&0xffff) - end - surface.cls(5) - draw.surface(s, 0, 0, 64, 64, 10, 10) - draw.rect(10, 10, 73, 73, 8) - draw.setPattern(ants) - draw.rect(10, 10, 73, 73, 0) - - draw.setPattern(0xffff) - --draw.text(#p,0,0,2) + surf.cls(1) + draw.surf(0, 0, 64, 64, 10, 10) end - -function normal_update() - if keyboard.keyPressed(key.RIGHT) then x=x+1 end - if keyboard.keyPressed(key.LEFT) then x=x-1 end - if keyboard.keyPressed(key.SPACE) then - redefinekeys.init() - end - if keyboard.keyPressed(key.ESCAPE) then - system.quit() - end - - if keyboard.keyPressed(key.F2) or mouse.buttonPressed(1) then - local val = window.getZoom() + 2 - if val >= 10 then val = 2 end - window.setZoom(val) - elseif keyboard.keyPressed(key.F3) then - window.setFullscreen(not window.getFullscreen()) - end - - if x>160 then x=-utf8.len(text)*4 end - - viewport.resetClipping() - viewport.setOrigin(0,0) - surface.cls(20) - viewport.setClipping(10,10,140,100) - surface.cls(3) - draw.text("HOLA",0,0,5) - viewport.setOrigin(-70,-50) - draw.text("ORÍGIN",0,0,5) - draw.text(text,x,10,5) - draw.circFill(20,20,10,15); - draw.setPattern(0x5a5a); - draw.circFill(20,20,10,10); - draw.setPattern(0xffff); -end - -redefinekeys = { - state = 0, - init = function () - redefinekeys.state=0 - _update=redefinekeys.update - end, - update = function() - surface.cls(20) - if redefinekeys.state == 0 then - draw.text("PULSA TECLA PER A DRETA...",0,0,10) - local key = keyboard.keyPressed(); - if key ~= 0 then - redefinekeys.state = 1 - keyRight=key - config.setKey("keyright", keyRight) - end - elseif redefinekeys.state == 1 then - draw.text("PULSA TECLA PER A ESQUERRA...",0,0,10) - local key = keyboard.keyPressed(); - if key ~= 0 then - keyLeft=key - config.setKey("keyleft", keyLeft) - _update=normal_update - end - end - end -} diff --git a/gif.c b/gif.c index 631ae40..e7dc897 100644 --- a/gif.c +++ b/gif.c @@ -197,8 +197,8 @@ static uint8_t* LoadGif(uint8_t *buffer, uint16_t* w, uint16_t* h) rgb *global_color_table = NULL; buffer += 6; // Ignore header - *w = (uint16_t)*buffer; buffer+=2; - *h = (uint16_t)*buffer; buffer+=2; + *w = *((uint16_t*)buffer); buffer+=2; + *h = *((uint16_t*)buffer); buffer+=2; const uint8_t fields = *buffer; buffer+=3; const int color_resolution_bits = ( ( fields & 0x70 ) >> 4 ) + 1; diff --git a/jfile.cpp b/jfile.cpp index d2c75f4..4fc7adf 100644 --- a/jfile.cpp +++ b/jfile.cpp @@ -151,6 +151,29 @@ void file_setconfigfolder(const char *foldername) struct passwd *pw = getpwuid(getuid()); const char *homedir = pw->pw_dir; config_folder = std::string(homedir) + "/." + foldername; + config_folder = std::string(homedir) + "/.config/jailgames/" + foldername; + + { + // Intenta crear ".config", per si no existeix + std::string config_base_folder = std::string(homedir) + "/.config"; + int ret = mkdir(config_base_folder.c_str(), S_IRWXU); + if (ret == -1 && errno != EEXIST) + { + printf("ERROR CREATING CONFIG BASE FOLDER."); + exit(EXIT_FAILURE); + } + } + { + // Intenta crear ".config/jailgames", per si no existeix + std::string config_base_folder = std::string(homedir) + "/.config/jailgames"; + int ret = mkdir(config_base_folder.c_str(), S_IRWXU); + if (ret == -1 && errno != EEXIST) + { + printf("ERROR CREATING CONFIG BASE FOLDER."); + exit(EXIT_FAILURE); + } + } + #endif struct stat st = {0}; @@ -171,7 +194,7 @@ void file_setconfigfolder(const char *foldername) } const char *file_getconfigfolder() { - std::string folder = config_folder + "/"; + static std::string folder = config_folder + "/"; return folder.c_str(); } diff --git a/lua.cpp b/lua.cpp index 33e55d7..ae14991 100644 --- a/lua.cpp +++ b/lua.cpp @@ -163,8 +163,8 @@ extern "C" { uint8_t celw = luaL_checknumber(L, 5); uint8_t celh = luaL_checknumber(L, 6); uint8_t layer = luaL_optinteger(L, 7, 0);*/ - uint8_t surface = luaL_checkinteger(L, 1); - setsource(surface); + //uint8_t surface = luaL_checkinteger(L, 1); + //setsource(surface); map(); //celx, cely, sx, sy, celw, celh, layer); return 0; } @@ -902,6 +902,10 @@ void push_lua_funcs() { lua_pushcfunction(L,cpp_mouse_wheel); lua_setfield(L, -2, "wheel"); lua_pushcfunction(L,cpp_mouse_down); lua_setfield(L, -2, "down"); lua_pushcfunction(L,cpp_mouse_press); lua_setfield(L, -2, "press"); + + lua_pushinteger(L, 1); lua_setfield(L, -2, "LEFT"); + lua_pushinteger(L, 2); lua_setfield(L, -2, "MIDDLE"); + lua_pushinteger(L, 3); lua_setfield(L, -2, "RIGHT"); lua_setglobal(L, "mouse"); lua_newtable(L); diff --git a/vscode/library.lua b/vscode/library.lua index c296cd5..72218d4 100644 --- a/vscode/library.lua +++ b/vscode/library.lua @@ -3,187 +3,170 @@ ---@class mini mini = {} ----@class surface -surface = {} +---@class surf +surf = {} ---@param w number ---@param h number ---@return number surface ---Create new surface specifying width and height -function surface.new(w, h) end +function surf.new(w, h) end ---@param filename string ---@return number surface ---Load GIF file and return surface -function surface.load(filename) end +function surf.load(filename) end ---@param surface number ---@param filename string ---@optional palette table ---Save surface as GIF file, with optional palette -function surface.save(surface, filename, palette) end +function surf.save(surface, filename, palette) end ---@param surface number ---Free the specified surface -function surface.free(surface) end +function surf.free(surface) end ---@param surface number ---@return number w, number h ---Retrieve width and height of surface -function surface.getSize(surface) end +function surf.size(surface) end ---@param surface number ---Set surface as target -function surface.setTarget(surface) end +function surf.target(surface) end + +---@param surface number +---Set surface as source +function surf.source(surface) end + +---Erase the current target surface with color 0. +function surf.cls() end ---@param color number ---Erase the current target surface with 'color' -function surface.cls(color) end - ----@param surface number ----@param x number ----@param y number ----@param color number ----Set the color for pixel (x,y) in the specified surface. -function surface.setPixel(surface, x, y, color) end - ----@param surface number ----@param x number ----@param y number ----@return number color ----Get color of pixel (x,y) in the specified surface. -function surface.getPixel(surface, x, y) end - ----@param x number ----@param y number ----@param color number ----Set the color for pixel (x,y) in the current target surface. -function surface.setPixel(x, y, color) end +function surf.cls(color) end ---@param x number ---@param y number ---@return number color ----Get color of pixel (x,y) in the current target -function surface.getPixel(x, y) end +---Get color of pixel (x,y) on the source surface. +function surf.pixel(x, y) end ----@class tilemap -tilemap = {} +---@param x number +---@param y number +---@param color number +---Set the color for pixel (x,y) on the target surface. +function surf.pixel(x, y, color) end + +---@class map +map = {} ---@param w number ---@param h number ---Create new map specifying width and height -function tilemap.new(w, h) end +function map.new(w, h) end ---@param filename string ----@return number surface ---Load a tilemap from a file and set it as current tilemap -function tilemap.load(filename) end +function map.load(filename) end ---@param filename string ---Save the current tilemap in a file -function tilemap.save(filename) end +function map.save(filename) end ---@param surface number ---Set surface as the current tilemap -function tilemap.set(surface) end +function map.set(surface) end ----@param surface number ----Draw the tilemap, using the provided surface as tile graphics source -function tilemap.draw(surface) end +---Draw the tilemap, using the source surface as tile graphics source +function map.draw() end ---@param x number ---@param y number ----@return number color +---@return number tile ---Get tile at the position (x,y) in the current tilemap -function tilemap.getTile(x, y) end +function map.tile(x, y) end ---@param x number ---@param y number ---@param tile number ---Set the tile at the position (x,y) in the current tilemap -function tilemap.setTile(x, y, tile) end +function map.tile(x, y, tile) end ----@class palette -palette = {} +---@class pal +pal = {} ---@param filename string ---@return table pal ---Load a palette from a GIF file and return it -function palette.load(filename) end +function pal.load(filename) end ---@param pal table ---Set a specified palette as the current palette -function palette.set(pal) end +function pal.set(pal) end ---@param index number ---@return number r, number g, number b ---Retrieve (r,g,b) color for the index specified in the current palette -function palette.getColor(index) end +function pal.color(index) end ---@param index number ---@param r number ---@param g number ---@param b number ---Set (r,g,b) color for the specified index in the current palette -function palette.setColor(index, r, g, b) end +function pal.color(index, r, g, b) end ---@param index number ---Set the index specified as transparent color -function palette.setTransparent(index) end - ----@class subpalette -subpalette = {} +function pal.trans(index) end ---Reset all the subpalette indices to their default palette index -function subpalette.resetAll() end +function pal.subpal() end + +---@param index number +---Reset the specified subpalette index to its default palette index +function pal.subpal(index) end ---@param index number ---@param color number ---Set the specified subpalette index to the specified palette index -function subpalette.set(index, color) end - ----@param index number ----Reset the specified subpalette index to its default palette index -function subpalette.reset(index) end +function pal.subpal(index, color) end ---@param index1 number ---@param index2 number ---@param color number ---Set the specified subpalette range to the specified palette index -function subpalette.setRange(index1, index2, color) end +function pal.subpal(index1, index2, color) end ----@param index1 number ----@param index2 number ----Reset the specified subpalette range to its default palette index -function subpalette.resetRange(index1, index2) end +---@class view +view = {} ----@class viewport -viewport = {} +---reset the current clipping region to the entire window +function view.clip() end ---@param x number ---@param y number ---@param w number ---@param h number ---Set the current clipping region -function viewport.setClipping(x, y, w, h) end +function view.clip(x, y, w, h) end ----reset the current clipping region to the entire window -function viewport.resetClipping() end +---@return number x, number y +---Get the current origin position +function view.origin() end ---@param x number ---@param y number ---Set the current origin position -function viewport.setOrigin(x, y) end - ----@return number x, number y ----Get the current origin position -function viewport.getOrigin() end +function view.origin(x, y) end ---@param x number ---@param y number ---@return number x, number y ---Convert screen position to viewport position -function viewport.toLocal(x, y) end +function view.tolocal(x, y) end ---@class draw draw = {} @@ -224,7 +207,7 @@ function draw.rect(x1, y1, x2, y2, color) end ---@param y2 number ---@param color number ---Draw a filled rectangle from (x1,y1) to (x2,y2) with the given color -function draw.rectFill(x1, y1, x2, y2, color) end +function draw.rectf(x1, y1, x2, y2, color) end ---@param x number ---@param y number @@ -238,7 +221,7 @@ function draw.circ(x, y, r, color) end ---@param r number ---@param color number ---Draw a filled cicle at position(x,y) with radius r and the given color -function draw.circFill(x, y, r, color) end +function draw.circf(x, y, r, color) end ---@param x1 number ---@param y1 number @@ -254,13 +237,12 @@ function draw.oval(x1, y1, x2, y2, color) end ---@param y2 number ---@param color number ---Draw a filled oval enclosed in (x1,y1)-(x2,y2) and the given color -function draw.ovalFill(x1, y1, x2, y2, color) end +function draw.ovalf(x1, y1, x2, y2, color) end ---@param pattern number ---Specify a pattern for the drawing functions -function draw.setPattern(pattern) end +function draw.pattern(pattern) end ----@param surface number ---@param sx number ---@param sy number ---@param sw number @@ -272,12 +254,11 @@ function draw.setPattern(pattern) end ---@optional boolean flip_x ---@optional boolean flip_y ---@optional boolean invert ----Blit the region starting at (sx,sy) and size (sw, sh) from the specified surface +---Blit the region starting at (sx,sy) and size (sw, sh) from the source surface ---to the position (dx, dy) (and optionally of size (dw, dh)) to the target surface, ---optionally flipping it horizontally or vertically, or inverting x and y axes -function draw.surface(surface, sx, sy, sw, sh, dx, dy, dw, dh, flip_x, flip_y, invert) end +function draw.surf(sx, sy, sw, sh, dx, dy, dw, dh, flip_x, flip_y, invert) end ----@param surface number ---@param sx number ---@param sy number ---@param sw number @@ -285,9 +266,9 @@ function draw.surface(surface, sx, sy, sw, sh, dx, dy, dw, dh, flip_x, flip_y, i ---@param x number ---@param y number ---@param a number ----Blit the region starting at (sx,sy) and size (sw, sh) from the specified surface +---Blit the region starting at (sx,sy) and size (sw, sh) from the source surface ---to the position (dx, dy) of the target surface, rotating it by a degrees -function draw.surfaceRotated(surface, sx, sy, sw, sh, x, y, a) end +function draw.surfrot(sx, sy, sw, sh, x, y, a) end ---@param text string ---@param x number @@ -312,13 +293,13 @@ function music.resume() end ---Stop the currently playing song function music.stop() end ----@param pos number ----Set the playing position of the currently loaded song -function music.setPosition(pos) end - ---@return number pos ---Get the playing position of the currently loaded song -function music.getPosition() end +function music.pos() end + +---@param pos number +---Set the playing position of the currently loaded song +function music.pos(pos) end ---@class sound sound = {} @@ -341,68 +322,66 @@ function sound.play(snd) end ---Stop the channel specified function sound.stop(chl) end ----@class system -system = {} +---@class sys +sys = {} ---Get current system timer in seconds (with decimals) -function system.getTime() end - ----@param bts number ----Set number of frames between beats -function system.setBeat(bts) end +function sys.time() end ---@return boolean ---Query if a beat has already passed -function system.isBeat() end +function sys.beat() end ----The game will call mini.update as fast as possible -function system.updateAtFullSpeed() end - ----The game will call mini.update only when a keyboard, mouse or pad event fires -function system.updateOnlyOnEvents() end +---@param bts number +---Set number of frames between beats +function sys.beat(bts) end +---@param mode number ---@param ms number ----The game will call mini.update on events or after the milliseconds specified passed -function system.updateOnEventsAndTimeout(ms) end +---Sets the update mode. +---UPDATE_ALWAYS: The game will call mini.update as fast as possible +---UPDATE_EVENTS: The game will call mini.update only when a keyboard, mouse or pad event fires +---UPDATE_TIMEOUT: The game will call mini.update on events or after the milliseconds specified passed +function sys.update(mode, ms) end ---@return table ---Gets a table with the name of each file in the data directory -function system.getFilesInDataDirectory() end +function sys.dir() end ---Exit the game -function system.quit() end +function sys.quit() end ----@class window -window = {} +---@class win +win = {} ---@param value number ---Set the window zoom -function window.setZoom(value) end +function win.zoom(value) end ---@return number value ---Get the window zoom -function window.getZoom() end +function win.zoom() end ---@param value boolean ---Specifies if the window must display at fullscreen or not -function window.setFullscreen(value) end +function win.fullscreen(value) end ---@return boolean value ---Returns if the window is at fullscreen or not -function window.getFullscreen() end +function win.fullscreen() end ---@param value boolean ---Specifies if the cursor must be visible or not -function window.showCursor(value) end +function win.cursor(value) end ---@return number w, number h ---Returns the current window size -function window.getResolution() end +function win.res() end ---@param w number ---@param h number ---Sets the window size -function window.setResolution(w, h) end +function win.res(w, h) end ---@class config @@ -411,81 +390,135 @@ config = {} ---@param key string ---@param value any ---Sets the value of a key in the configuration file -function config.setKey(key, value) end +function config.key(key, value) end ---@param key string ---@return string value ---Gets the value of a key in the configuration file -function config.getKey(key) end +function config.key(key) end ---@return string value ---Returns the folder in which the configuration file resides -function config.getConfigFolder() end +function config.folder() end ---@class mouse +---@field mouse.LEFT number +---@field mouse.MIDDLE number +---@field mouse.RIGHT number + mouse = {} ---@return number x, number y ---Returns the current position of the mouse -function mouse.getPos() end +function mouse.pos() end ---@return number value ---Returns the value of the mouse wheel -function mouse.getWheel() end +function mouse.wheel() end ---@param btn number ---@return boolean ---Returns whether the specified mouse button is down -function mouse.buttonDown(btn) end +function mouse.down(btn) end ---@param btn number ---@return boolean ---Returns whether the specified mouse button has just been pressed -function mouse.buttonPressed(btn) end +function mouse.press(btn) end ----@class keyboard -keyboard = {} +mouse.LEFT = 1 +mouse.MIDDLE = 2 +mouse.RIGHT = 3 + +---@class key +key = {} ---@param key number ---@return boolean ---Returns whether the specified keyboard key is down -function keyboard.keyDown(key) end +function key.down(key) end ---@return number ---Returns which keyboard key has just been pressed -function keyboard.keyPressed() end +function key.press() end ---@param key number ---@return boolean ---Returns whether the specified keyboard key has just been pressed -function keyboard.keyPressed(key) end +function key.press(key) end ---@return boolean ---Returns whether any keyboard key has just been pressed -function keyboard.anyKeyPressed() end +function key.any() end ----@class gamepad -gamepad = {} +---@class pad +---@field pad.INVALID number +---@field pad.A number +---@field pad.B number +---@field pad.X number +---@field pad.Y number +---@field pad.BACK number +---@field pad.GUIDE number +---@field pad.START number +---@field pad.LEFTSTICK number +---@field pad.RIGHTSTICK number +---@field pad.LEFTSHOULDER number +---@field pad.RIGHTSHOULDER number +---@field pad.UP number +---@field pad.DOWN number +---@field pad.LEFT number +---@field pad.RIGHT number +---@field pad.MISC1 number +---@field pad.PADDLE1 number +---@field pad.PADDLE2 number +---@field pad.PADDLE3 number +---@field pad.PADDLE4 number +---@field pad.TOUCHPAD number + +pad = {} ---@param btn number ---@return boolean ---Returns whether the specified gamepad button is down -function gamepad.buttonDown(btn) end +function pad.down(btn) end ---@return number ---Returns which gamepad button has just been pressed -function gamepad.buttonPressed() end +function pad.press() end ---@param btn number ---@return boolean ---Returns whether the specified gamepad button has just been pressed -function gamepad.buttonPressed(btn) end +function pad.press(btn) end ---@return boolean ---Returns whether any gamepad button has just been pressed -function gamepad.anybuttonPressed() end +function pad.any() end + +pad.INVALID = -1 +pad.A = 0 +pad.B = 1 +pad.X = 2 +pad.Y = 3 +pad.BACK = 4 +pad.GUIDE = 5 +pad.START = 6 +pad.LEFTSTICK = 7 +pad.RIGHTSTICK = 8 +pad.LEFTSHOULDER = 9 +pad.RIGHTSHOULDER = 10 +pad.UP = 11 +pad.DOWN = 12 +pad.LEFT = 13 +pad.RIGHT = 14 +pad.MISC1 = 15 +pad.PADDLE1 = 16 +pad.PADDLE2 = 17 +pad.PADDLE3 = 18 +pad.PADDLE4 = 19 +pad.TOUCHPAD = 20 ---@class key ---@field key.UNKNOWN number @@ -515,16 +548,16 @@ function gamepad.anybuttonPressed() end ---@field key.X number ---@field key.Y number ---@field key.Z number ----@field key.1 number ----@field key.2 number ----@field key.3 number ----@field key.4 number ----@field key.5 number ----@field key.6 number ----@field key.7 number ----@field key.8 number ----@field key.9 number ----@field key.0 number +---@field key.N1 number +---@field key.N2 number +---@field key.N3 number +---@field key.N4 number +---@field key.N5 number +---@field key.N6 number +---@field key.N7 number +---@field key.N8 number +---@field key.N9 number +---@field key.N0 number ---@field key.RETURN number ---@field key.ESCAPE number ---@field key.BACKSPACE number @@ -595,7 +628,7 @@ function gamepad.anybuttonPressed() end ---@field key.RSHIFT number ---@field key.RALT number ---@field key.RGUI number -key = {} + key.UNKNOWN = 0 key.A = 4 key.B = 5 @@ -703,52 +736,3 @@ key.RCTRL = 228 key.RSHIFT = 229 key.RALT = 230 key.RGUI = 231 - - ----@class button ----@field button.INVALID number ----@field button.A number ----@field button.B number ----@field button.X number ----@field button.Y number ----@field button.BACK number ----@field button.GUIDE number ----@field button.START number ----@field button.LEFTSTICK number ----@field button.RIGHTSTICK number ----@field button.LEFTSHOULDER number ----@field button.RIGHTSHOULDER number ----@field button.UP number ----@field button.DOWN number ----@field button.LEFT number ----@field button.RIGHT number ----@field button.MISC1 number ----@field button.PADDLE1 number ----@field button.PADDLE2 number ----@field button.PADDLE3 number ----@field button.PADDLE4 number ----@field button.TOUCHPAD number - -button = {} -button.INVALID = -1 -button.A = 0 -button.B = 1 -button.X = 2 -button.Y = 3 -button.BACK = 4 -button.GUIDE = 5 -button.START = 6 -button.LEFTSTICK = 7 -button.RIGHTSTICK = 8 -button.LEFTSHOULDER = 9 -button.RIGHTSHOULDER = 10 -button.UP = 11 -button.DOWN = 12 -button.LEFT = 13 -button.RIGHT = 14 -button.MISC1 = 15 -button.PADDLE1 = 16 -button.PADDLE2 = 17 -button.PADDLE3 = 18 -button.PADDLE4 = 19 -button.TOUCHPAD = 20 \ No newline at end of file