Merge branch 'devel'

Fully ported from quicksilver to raylib.
This commit is contained in:
Stephen Seo 2023-02-22 14:34:17 +09:00
commit e0eff5df8e
23 changed files with 4215 additions and 3100 deletions

2436
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,11 +2,23 @@
name = "ld45_one_and_all"
version = "0.1.1"
authors = ["Stephen Seo <seo.disparate@gmail.com>"]
edition = "2018"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
quicksilver = { version = "0.3.19", default-features = false, features = [ "fonts", "saving", "sounds" ] }
#quicksilver = { version = "0.3.19", default-features = false, features = [ "fonts", "saving", "sounds" ] }
rand = "0.7.2"
serde = { version = "1.0.101", features = ["derive"] }
[build-dependencies]
bindgen = "0.64"
[lib]
name = "ld45_lib"
path = "src/lib.rs"
crate-type = ["rlib", "staticlib"]
[[bin]]
name = "ld45_bin"
path = "src/bin.rs"

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 Stephen Seo
Copyright (c) 2019,2023 Stephen Seo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,17 +1,12 @@
# Disclaimer
The hosted WASM versions of this game still work, but I am not entirely sure if
the security issues with this game apply to the WASM versions. **USE AT YOUR OWN
RISK**.
This game was created in a manner such that it was tightly coupled with the
`quicksilver` dependency. However, `quicksilver` is no longer (actively)
maintained, and its old dependencies no longer allow this game to run properly
(crashes on native, cannot serve WASM version).
This game has been ported away from quicksilver. A WASM version of the new
version will be made soon.
# Entry for LD45 - Start with Nothing
This game is a Rust project using the quicksilver 2D game framework.
This game is a Rust project that originally used the quicksilver 2D game
framework. It now uses Raylib.
It can be run locally by invoking `cargo run` in the project directory.

22
build.rs Normal file
View file

@ -0,0 +1,22 @@
extern crate bindgen;
use std::env;
use std::path::PathBuf;
fn main() {
println!("cargo:rustc-link-lib=raylib");
println!("cargo:rustc-link-lib=OpenGL");
println!("cargo:rerun-if-changed=raylib/raylib.h");
let bindings = bindgen::Builder::default()
.header("raylib/raylib.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.clang_arg("-fvisibility=default")
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("raylib_bindings.rs"))
.expect("Couldn't write bindings!");
}

1568
raylib/raylib.h Normal file

File diff suppressed because it is too large Load diff

119
src/agnostic_interface.rs Normal file
View file

@ -0,0 +1,119 @@
pub mod raylib_impl;
use std::path::Path;
use crate::faux_quicksilver::{Circle, Color, Rectangle, Transform, Vector};
pub trait ImageInterface {
fn draw(&mut self, x: f32, y: f32, color: Color) -> Result<(), String>;
fn draw_sub(
&mut self,
sub_rect: Rectangle,
dest_rect: Rectangle,
color: Color,
) -> Result<(), String>;
fn draw_transform(
&mut self,
x: f32,
y: f32,
color: Color,
transform: Transform,
origin: Vector,
) -> Result<(), String>;
fn draw_sub_transform(
&mut self,
sub_rect: Rectangle,
dest_rect: Rectangle,
color: Color,
transform: Transform,
origin: Vector,
) -> Result<(), String>;
fn get_w(&self) -> usize;
fn get_h(&self) -> usize;
fn get_wh_rect(&self) -> Rectangle;
}
pub trait FontInterface {
fn draw(&mut self, s: &str, size: u32, x: f32, y: f32, color: Color) -> Result<(), String>;
}
pub trait SoundInterface {
fn play(&mut self, vol: f32) -> Result<(), String>;
}
pub trait MusicInterface {
fn play(&mut self, vol: f32) -> Result<(), String>;
fn pause(&mut self) -> Result<(), String>;
fn stop(&mut self) -> Result<(), String>;
fn set_loop(&mut self, loop_enable: bool) -> Result<(), String>;
fn update(&mut self) -> Result<(), String>;
}
pub trait ShaderInterface {
fn set_transform_attrib(&mut self, transform: Transform) -> Result<(), String>;
fn set_origin_attrib(&mut self, origin: Vector) -> Result<(), String>;
fn set_camera_attrib(&mut self, camera: Vector) -> Result<(), String>;
fn begin_draw_shader(&self) -> Result<(), String>;
fn end_draw_shader(&self) -> Result<(), String>;
}
pub trait CameraInterface {
fn get_view_xy(&self) -> Result<(f32, f32), String>;
fn set_view_xy(&mut self, x: f32, y: f32) -> Result<(), String>;
}
pub trait GameInterface {
fn get_dimensions(&self) -> Result<(f32, f32), String>;
fn get_key_pressed(&mut self, key: char) -> Result<bool, String>;
fn get_mouse_pressed(&mut self) -> Result<Option<(f32, f32)>, String>;
fn get_mouse_released(&mut self) -> Result<bool, String>;
fn get_mouse_down(&mut self) -> Result<Option<(f32, f32)>, String>;
fn get_mouse_xy(&self) -> Result<(f32, f32), String>;
fn get_mouse_xy_vec(&self) -> Result<Vector, String>;
fn xy_to_world(&self, x: f32, y: f32) -> Result<Vector, String>;
fn vec_to_world(&self, vec: Vector) -> Result<Vector, String>;
fn get_delta_time(&self) -> f32;
fn clear_window(&mut self, color: Color) -> Result<(), String>;
fn begin_drawing(&mut self) -> Result<(), String>;
fn end_drawing(&mut self) -> Result<(), String>;
fn draw_circle(&mut self, circle: Circle, color: Color) -> Result<(), String>;
fn draw_circle_transform(
&mut self,
circle: Circle,
color: Color,
transform: Transform,
origin: Vector,
) -> Result<(), String>;
fn draw_rect(&mut self, rect: Rectangle, color: Color) -> Result<(), String>;
fn draw_rect_ex(
&mut self,
rect: Rectangle,
color: Color,
origin: Vector,
rot: f32,
) -> Result<(), String>;
fn draw_rect_transform(
&mut self,
rect: Rectangle,
color: Color,
transform: Transform,
origin: Vector,
) -> Result<(), String>;
fn load_image(&mut self, path: &Path) -> Result<Box<dyn ImageInterface>, String>;
fn load_font(&mut self, path: &Path) -> Result<Box<dyn FontInterface>, String>;
fn load_sound(&mut self, path: &Path) -> Result<Box<dyn SoundInterface>, String>;
fn load_music(&mut self, path: &Path) -> Result<Box<dyn MusicInterface>, String>;
fn load_shader(
&mut self,
name: String,
vs: &Path,
fs: &Path,
) -> Result<Box<dyn ShaderInterface>, String>;
fn get_camera(&mut self) -> Result<Box<dyn CameraInterface>, String>;
fn get_default_camera(&mut self) -> Result<Box<dyn CameraInterface>, String>;
fn set_camera(&mut self, camera: &Box<dyn CameraInterface>) -> Result<(), String>;
fn set_camera_xy(&mut self, x: f32, y: f32) -> Result<(), String>;
}

File diff suppressed because it is too large Load diff

17
src/bin.rs Normal file
View file

@ -0,0 +1,17 @@
use ld45_lib::agnostic_interface;
fn will_close() -> bool {
unsafe { agnostic_interface::raylib_impl::ffi::WindowShouldClose() }
}
fn main() {
let state_ptr = ld45_lib::ld45_initialize();
while !will_close() {
ld45_lib::ld45_iterate(state_ptr);
}
unsafe {
drop(Box::from_raw(state_ptr as *mut ld45_lib::WasmState));
}
}

352
src/faux_quicksilver.rs Normal file
View file

@ -0,0 +1,352 @@
use std::collections::HashMap;
use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};
use std::path::Path;
use serde::{Deserialize, Serialize};
use crate::agnostic_interface::{
FontInterface, GameInterface, ImageInterface, MusicInterface, SoundInterface,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl Color {
pub const WHITE: Self = Self {
r: 255,
g: 255,
b: 255,
a: 255,
};
pub const BLACK: Self = Self {
r: 0,
g: 0,
b: 0,
a: 255,
};
pub const GREEN: Self = Self {
r: 0,
g: 255,
b: 0,
a: 255,
};
pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Rectangle {
pub x: f32,
pub y: f32,
pub w: f32,
pub h: f32,
}
impl Rectangle {
pub fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
Self { x, y, w, h }
}
pub fn pos_add_vec(&mut self, v: Vector) {
self.x += v.x;
self.y += v.y;
}
}
impl Default for Rectangle {
fn default() -> Self {
Self {
x: 0.0,
y: 0.0,
w: 1.0,
h: 1.0,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Circle {
pub x: f32,
pub y: f32,
pub r: f32,
}
impl Circle {
pub fn new(x: f32, y: f32, r: f32) -> Self {
Self { x, y, r }
}
pub fn pos_add_vec(&mut self, v: Vector) {
self.x += v.x;
self.y += v.y;
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Vector {
pub x: f32,
pub y: f32,
}
impl Add<Vector> for Vector {
type Output = Vector;
fn add(self, rhs: Vector) -> Self::Output {
Vector {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl Sub<Vector> for Vector {
type Output = Vector;
fn sub(self, rhs: Vector) -> Self::Output {
Vector {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl AddAssign<Vector> for Vector {
fn add_assign(&mut self, rhs: Vector) {
self.x += rhs.x;
self.y += rhs.y;
}
}
impl SubAssign<Vector> for Vector {
fn sub_assign(&mut self, rhs: Vector) {
self.x -= rhs.x;
self.y -= rhs.y;
}
}
impl Mul<f32> for Vector {
type Output = Vector;
fn mul(self, rhs: f32) -> Self::Output {
Vector {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl Vector {
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
impl From<(f32, f32)> for Vector {
fn from(t: (f32, f32)) -> Self {
Self { x: t.0, y: t.1 }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Transform {
pub mat: [f32; 9],
}
impl Default for Transform {
fn default() -> Self {
Self {
mat: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
}
}
}
impl Mul<Vector> for Transform {
type Output = Vector;
fn mul(self, rhs: Vector) -> Self::Output {
Vector {
x: rhs.x * self.mat[0] + rhs.y * self.mat[1] + self.mat[2],
y: rhs.x * self.mat[3] + rhs.y * self.mat[4] + self.mat[5],
}
}
}
impl Mul<Transform> for Transform {
type Output = Transform;
fn mul(self, rhs: Transform) -> Self::Output {
Self {
mat: [
self.mat[0] * rhs.mat[0] + self.mat[1] * rhs.mat[3] + self.mat[2] * rhs.mat[6],
self.mat[0] * rhs.mat[1] + self.mat[1] * rhs.mat[4] + self.mat[2] * rhs.mat[7],
self.mat[0] * rhs.mat[2] + self.mat[1] * rhs.mat[5] + self.mat[2] * rhs.mat[8],
self.mat[3] * rhs.mat[0] + self.mat[4] * rhs.mat[3] + self.mat[5] * rhs.mat[6],
self.mat[3] * rhs.mat[1] + self.mat[4] * rhs.mat[4] + self.mat[5] * rhs.mat[7],
self.mat[3] * rhs.mat[2] + self.mat[4] * rhs.mat[5] + self.mat[5] * rhs.mat[8],
self.mat[6] * rhs.mat[0] + self.mat[7] * rhs.mat[3] + self.mat[8] * rhs.mat[6],
self.mat[6] * rhs.mat[1] + self.mat[7] * rhs.mat[4] + self.mat[8] * rhs.mat[7],
self.mat[6] * rhs.mat[2] + self.mat[7] * rhs.mat[5] + self.mat[8] * rhs.mat[8],
],
}
}
}
impl Transform {
pub const IDENTITY: Self = Self {
mat: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
};
pub fn rotate(rot: f32) -> Self {
Self {
mat: [
rot.cos(),
-rot.sin(),
0.0,
rot.sin(),
rot.cos(),
0.0,
0.0,
0.0,
1.0,
],
}
}
pub fn translate(x: f32, y: f32) -> Self {
Self {
mat: [1.0, 0.0, x, 0.0, 1.0, y, 0.0, 0.0, 1.0],
}
}
}
pub struct Window {
gi: Box<dyn GameInterface>,
images: HashMap<String, Box<dyn ImageInterface>>,
fonts: HashMap<String, Box<dyn FontInterface>>,
sounds: HashMap<String, Box<dyn SoundInterface>>,
music: HashMap<String, Box<dyn MusicInterface>>,
}
impl Window {
pub fn new(gi: Box<dyn GameInterface>) -> Self {
Self {
gi,
images: HashMap::new(),
fonts: HashMap::new(),
sounds: HashMap::new(),
music: HashMap::new(),
}
}
pub fn get_gi(&self) -> &dyn GameInterface {
self.gi.as_ref()
}
pub fn get_gi_mut(&mut self) -> &mut dyn GameInterface {
self.gi.as_mut()
}
pub fn load_image(&mut self, path: &Path, name: String) -> Result<(), String> {
self.images.insert(name, self.gi.load_image(path)?);
Ok(())
}
pub fn load_font(&mut self, path: &Path, name: String) -> Result<(), String> {
self.fonts.insert(name, self.gi.load_font(path)?);
Ok(())
}
pub fn load_sound(&mut self, path: &Path, name: String) -> Result<(), String> {
self.sounds.insert(name, self.gi.load_sound(path)?);
Ok(())
}
pub fn load_music(&mut self, path: &Path, name: String) -> Result<(), String> {
self.music.insert(name, self.gi.load_music(path)?);
Ok(())
}
pub fn get_image(&self, name: &str) -> Result<&dyn ImageInterface, String> {
Ok(self
.images
.get(name)
.ok_or_else(|| format!("Image \"{name}\" not found"))?
.as_ref())
}
pub fn get_image_mut(&mut self, name: &str) -> Result<&mut dyn ImageInterface, String> {
Ok(self
.images
.get_mut(name)
.ok_or_else(|| format!("Image \"{name}\" not found"))?
.as_mut())
}
pub fn get_font(&self, name: &str) -> Result<&dyn FontInterface, String> {
Ok(self
.fonts
.get(name)
.ok_or_else(|| format!("Font \"{name}\" not found"))?
.as_ref())
}
pub fn get_font_mut(&mut self, name: &str) -> Result<&mut dyn FontInterface, String> {
Ok(self
.fonts
.get_mut(name)
.ok_or_else(|| format!("Font \"{name}\" not found"))?
.as_mut())
}
pub fn get_sound(&self, name: &str) -> Result<&dyn SoundInterface, String> {
Ok(self
.sounds
.get(name)
.ok_or_else(|| format!("Sound \"{name}\" not found"))?
.as_ref())
}
pub fn get_sound_mut(&mut self, name: &str) -> Result<&mut dyn SoundInterface, String> {
Ok(self
.sounds
.get_mut(name)
.ok_or_else(|| format!("Sound \"{name}\" not found"))?
.as_mut())
}
pub fn get_music(&self, name: &str) -> Result<&dyn MusicInterface, String> {
Ok(self
.music
.get(name)
.ok_or_else(|| format!("Music \"{name}\" not found"))?
.as_ref())
}
pub fn get_music_mut(&mut self, name: &str) -> Result<&mut dyn MusicInterface, String> {
Ok(self
.music
.get_mut(name)
.ok_or_else(|| format!("Music \"{name}\" not found"))?
.as_mut())
}
pub fn update_music(&mut self) -> Result<(), String> {
for (_, music) in &mut self.music {
music.update()?;
}
Ok(())
}
}

48
src/lib.rs Normal file
View file

@ -0,0 +1,48 @@
pub mod agnostic_interface;
mod faux_quicksilver;
mod original_impl;
mod shaders;
use agnostic_interface::raylib_impl::RaylibGame;
use faux_quicksilver::Window;
use original_impl::GameState;
pub struct WasmState {
pub window: Window,
pub game_state: GameState,
}
impl WasmState {
pub fn get_window_mut(&mut self) -> &mut Window {
&mut self.window
}
pub fn get_state_mut(&mut self) -> &mut GameState {
&mut self.game_state
}
}
#[no_mangle]
pub extern "C" fn ld45_initialize() -> *mut ::std::os::raw::c_void {
let game_interface = RaylibGame::new_boxed(800, 600);
let mut window = Window::new(game_interface);
let game_state = GameState::new(&mut window).unwrap();
Box::into_raw(Box::new(WasmState { window, game_state })) as *mut ::std::os::raw::c_void
}
#[no_mangle]
pub extern "C" fn ld45_iterate(context: *mut ::std::os::raw::c_void) {
let state_ptr = context as *mut WasmState;
unsafe {
(*state_ptr).get_window_mut().update_music().unwrap();
(*state_ptr)
.get_state_mut()
.update(&mut (*state_ptr).get_window_mut())
.unwrap();
(*state_ptr)
.get_state_mut()
.draw(&mut (*state_ptr).get_window_mut())
.unwrap();
}
}

File diff suppressed because it is too large Load diff

47
src/shaders.rs Normal file
View file

@ -0,0 +1,47 @@
use crate::{
agnostic_interface::raylib_impl::RaylibShader,
faux_quicksilver::{Transform, Vector},
};
use std::ffi::CStr;
extern "C" {
pub fn glVertexAttrib2f(index: ::std::os::raw::c_uint, x: f32, y: f32);
}
extern "C" {
pub fn glVertexAttrib3f(index: ::std::os::raw::c_uint, x: f32, y: f32, z: f32);
}
extern "C" {
pub fn glVertexAttrib4f(index: ::std::os::raw::c_uint, x: f32, y: f32, z: f32, w: f32);
}
extern "C" {
pub fn glGetAttribLocation(
program: ::std::os::raw::c_uint,
name: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int;
}
pub fn get_attrib_location(raylib_shader: &RaylibShader, name: &CStr) -> ::std::os::raw::c_uint {
unsafe {
glGetAttribLocation(raylib_shader.get_shader_id(), name.as_ptr()) as ::std::os::raw::c_uint
}
}
pub fn set_transform_3f(index: ::std::os::raw::c_uint, transform: Transform) {
// OpenGL stores matrix indices in column major order.
for (i, idx) in (index..(index + 3)).enumerate() {
unsafe {
glVertexAttrib3f(
idx,
transform.mat[0 + i as usize],
transform.mat[3 + i as usize],
transform.mat[6 + i as usize],
);
}
}
}
pub fn set_attr_2f(index: ::std::os::raw::c_uint, origin: Vector) {
unsafe {
glVertexAttrib2f(index, origin.x, origin.y);
}
}

23
static/camera.vs Normal file
View file

@ -0,0 +1,23 @@
#version 100
// Input vertex attributes
attribute vec3 vertexPosition;
attribute vec2 vertexTexCoord;
attribute vec4 vertexColor;
// Input uniform values
uniform mat4 mvp;
// Output vertex attributes (to fragment shader)
varying vec2 fragTexCoord;
varying vec4 fragColor;
// custom
attribute vec2 camera;
void main() {
fragTexCoord = vertexTexCoord;
fragColor = vertexColor;
gl_Position = mvp * vec4(vertexPosition - vec3(camera, 0.0), 1.0);
}

20
static/simple.fs Normal file
View file

@ -0,0 +1,20 @@
#version 100
// Default fragment shader used by Raylib.
precision mediump float;
// Input vertex attributes (from vertex shader)
varying vec2 fragTexCoord;
varying vec4 fragColor;
// Input uniform values
uniform sampler2D texture0;
uniform vec4 colDiffuse;
void main() {
// Texel color fetching from texture sampler
vec4 texelColor = texture2D(texture0, fragTexCoord);
gl_FragColor = texelColor*colDiffuse*fragColor;
}

26
static/transform.vs Normal file
View file

@ -0,0 +1,26 @@
#version 100
// Input vertex attributes
attribute vec3 vertexPosition;
attribute vec2 vertexTexCoord;
attribute vec4 vertexColor;
// Input uniform values
uniform mat4 mvp;
// Output vertex attributes (to fragment shader)
varying vec2 fragTexCoord;
varying vec4 fragColor;
// custom
attribute vec2 camera;
attribute vec2 origin;
attribute mat3 transform;
void main() {
fragTexCoord = vertexTexCoord;
fragColor = vertexColor;
vec3 pos = transform * (vertexPosition - vec3(origin, 0.0)) + vec3(origin, 0.0);
gl_Position = mvp * vec4(pos - vec3(camera, 0.0), 1.0);
}

5
wasm/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
/ld45.html
/ld45.js
/ld45.data
/ld45.wasm
/index.html

32
wasm/Makefile Normal file
View file

@ -0,0 +1,32 @@
CC = source "${HOME}/git/emsdk/emsdk_env.sh" && emcc
all: ld45.html
ld45.html: src/main.c ../target/wasm32-unknown-emscripten/release/libld45_lib.a
${CC} -o ld45.html -s USE_GLFW=3 -Iinclude \
-Llib -lraylib \
-L../target/wasm32-unknown-emscripten/release -lld45_lib \
-sTOTAL_MEMORY=1024MB \
-sALLOW_MEMORY_GROWTH=1 \
-O2 \
-fsanitize=address \
-sEXPORTED_FUNCTIONS="['_malloc', '_main']" \
-sEXPORTED_RUNTIME_METHODS=ccall,cwrap \
--preload-file ../static src/main.c
ln -sf ld45.html index.html
#-fsanitize=address \
#-sWARN_UNALIGNED=1 \
../target/wasm32-unknown-emscripten/release/libld45_lib.a: ../src/lib.rs
cd ..; source "${HOME}/git/emsdk/emsdk_env.sh"; cargo build --lib --release --target wasm32-unknown-emscripten
.PHONY: clean
clean:
rm -f ld45.html
rm -f ld45.js
rm -f ld45.wasm
rm -f ld45.data
rm -f index.html
cd ..; cargo clean

1
wasm/emsdk_version Normal file
View file

@ -0,0 +1 @@
3.1.32

8
wasm/include/ld45_lib.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef LD45_ONE_AND_ALL_LIB_H_
#define LD45_ONE_AND_ALL_LIB_H_
extern void *ld45_initialize();
extern void ld45_iterate(void *context);
#endif

1
wasm/include/raylib.h Symbolic link
View file

@ -0,0 +1 @@
../../raylib/raylib.h

BIN
wasm/lib/libraylib.a Normal file

Binary file not shown.

18
wasm/src/main.c Normal file
View file

@ -0,0 +1,18 @@
#include <emscripten.h>
#include <emscripten/html5.h>
#include <ld45_lib.h>
#include <stdio.h>
void main_loop(void *ud) {
ld45_iterate(ud);
}
int main(void) {
void *ld45_context = ld45_initialize();
emscripten_set_main_loop_arg(main_loop, ld45_context, 0, 1);
return 0;
}