Browse Source

Merge branch 'master' of https://github.com/odin-lang/Odin

gingerBill 3 years ago
parent
commit
b0904d6598

+ 56 - 1
core/compress/common.odin

@@ -294,6 +294,24 @@ peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid
 	}
 	}
 }
 }
 
 
+@(optimization_mode="speed")
+peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
+	size :: size_of(T)
+
+	#no_bounds_check {
+		if len(z.input_data) >= size + offset {
+			buf := z.input_data[offset:][:size]
+			return (^T)(&buf[0])^, .None
+		}
+	}
+
+	if len(z.input_data) == 0 {
+		return T{}, .EOF
+	} else {
+		return T{}, .Short_Buffer
+	}
+}
+
 @(optimization_mode="speed")
 @(optimization_mode="speed")
 peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
 peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
 	size :: size_of(T)
 	size :: size_of(T)
@@ -321,7 +339,44 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
 	return res, .None
 	return res, .None
 }
 }
 
 
-peek_data :: proc{peek_data_from_memory, peek_data_from_stream}
+@(optimization_mode="speed")
+peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
+	size :: size_of(T)
+
+	// Get current position to return to.
+	cur_pos, e1 := z.input->impl_seek(0, .Current)
+	if e1 != .None {
+		return T{}, e1
+	}
+
+	// Seek to offset.
+	pos, e2 := z.input->impl_seek(offset, .Start)
+	if e2 != .None {
+		return T{}, e2
+	}
+
+	r, e3 := io.to_reader_at(z.input)
+	if !e3 {
+		return T{}, .Empty
+	}
+	when size <= 128 {
+		b: [size]u8
+	} else {
+		b := make([]u8, size, context.temp_allocator)
+	}
+	_, e4 := io.read_at(r, b[:], pos)
+	if e4 != .None {
+		return T{}, .Empty
+	}
+
+	// Return read head to original position.
+	z.input->impl_seek(cur_pos, .Start)
+
+	res = (^T)(&b[0])^
+	return res, .None
+}
+
+peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_offset_from_memory, peek_data_at_offset_from_stream}
 
 
 
 
 
 

+ 19 - 0
core/container/queue/queue.odin

@@ -73,11 +73,18 @@ get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
 front :: proc(q: ^$Q/Queue($T)) -> T {
 front :: proc(q: ^$Q/Queue($T)) -> T {
 	return q.data[q.offset]
 	return q.data[q.offset]
 }
 }
+front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
+	return &q.data[q.offset]
+}
 
 
 back :: proc(q: ^$Q/Queue($T)) -> T {
 back :: proc(q: ^$Q/Queue($T)) -> T {
 	idx := (q.offset+uint(q.len))%builtin.len(q.data)
 	idx := (q.offset+uint(q.len))%builtin.len(q.data)
 	return q.data[idx]
 	return q.data[idx]
 }
 }
+back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
+	idx := (q.offset+uint(q.len))%builtin.len(q.data)
+	return &q.data[idx]
+}
 
 
 set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
 set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
 	runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
 	runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
@@ -92,6 +99,18 @@ get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^
 	return &q.data[idx]
 	return &q.data[idx]
 }
 }
 
 
+peek_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
+	runtime.bounds_check_error_loc(loc, 0, builtin.len(q.data))
+	idx := q.offset%builtin.len(q.data)
+	return &q.data[idx]
+}
+
+peek_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
+	runtime.bounds_check_error_loc(loc, int(q.len - 1), builtin.len(q.data))
+	idx := (uint(q.len - 1)+q.offset)%builtin.len(q.data)
+	return &q.data[idx]
+}
+
 // Push an element to the back of the queue
 // Push an element to the back of the queue
 push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
 push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
 	if space(q^) == 0 {
 	if space(q^) == 0 {

+ 1 - 0
core/encoding/json/marshal.odin

@@ -85,6 +85,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
 		case i16:     u = u128(i)
 		case i16:     u = u128(i)
 		case i32:     u = u128(i)
 		case i32:     u = u128(i)
 		case i64:     u = u128(i)
 		case i64:     u = u128(i)
+		case i128:    u = u128(i)
 		case int:     u = u128(i)
 		case int:     u = u128(i)
 		case u8:      u = u128(i)
 		case u8:      u = u128(i)
 		case u16:     u = u128(i)
 		case u16:     u = u128(i)

+ 60 - 2
core/image/common.odin

@@ -46,7 +46,7 @@ Image :: struct {
 	height:        int,
 	height:        int,
 	channels:      int,
 	channels:      int,
 	depth:         int, // Channel depth in bits, typically 8 or 16
 	depth:         int, // Channel depth in bits, typically 8 or 16
-	pixels:        bytes.Buffer,
+	pixels:        bytes.Buffer `fmt:"-"`,
 	/*
 	/*
 		Some image loaders/writers can return/take an optional background color.
 		Some image loaders/writers can return/take an optional background color.
 		For convenience, we return them as u16 so we don't need to switch on the type
 		For convenience, we return them as u16 so we don't need to switch on the type
@@ -61,6 +61,7 @@ Image_Metadata :: union #shared_nil {
 	^Netpbm_Info,
 	^Netpbm_Info,
 	^PNG_Info,
 	^PNG_Info,
 	^QOI_Info,
 	^QOI_Info,
+	^TGA_Info,
 }
 }
 
 
 
 
@@ -168,6 +169,7 @@ Error :: union #shared_nil {
 
 
 General_Image_Error :: enum {
 General_Image_Error :: enum {
 	None = 0,
 	None = 0,
+	Unsupported_Option,
 	// File I/O
 	// File I/O
 	Unable_To_Read_File,
 	Unable_To_Read_File,
 	Unable_To_Write_File,
 	Unable_To_Write_File,
@@ -376,10 +378,20 @@ QOI_Info :: struct {
 	header: QOI_Header,
 	header: QOI_Header,
 }
 }
 
 
+TGA_Data_Type :: enum u8  {
+	No_Image_Data             = 0,
+	Uncompressed_Color_Mapped = 1,
+	Uncompressed_RGB          = 2,
+	Uncompressed_Black_White  = 3,
+	Compressed_Color_Mapped   = 9,
+	Compressed_RGB            = 10,
+	Compressed_Black_White    = 11,
+}
+
 TGA_Header :: struct #packed {
 TGA_Header :: struct #packed {
 	id_length:        u8,
 	id_length:        u8,
 	color_map_type:   u8,
 	color_map_type:   u8,
-	data_type_code:   u8,
+	data_type_code:   TGA_Data_Type,
 	color_map_origin: u16le,
 	color_map_origin: u16le,
 	color_map_length: u16le,
 	color_map_length: u16le,
 	color_map_depth:  u8,
 	color_map_depth:  u8,
@@ -390,6 +402,52 @@ TGA_Header :: struct #packed {
 }
 }
 #assert(size_of(TGA_Header) == 18)
 #assert(size_of(TGA_Header) == 18)
 
 
+New_TGA_Signature :: "TRUEVISION-XFILE.\x00"
+
+TGA_Footer :: struct #packed {
+	extension_area_offset:      u32le,
+	developer_directory_offset: u32le,
+	signature:                  [18]u8 `fmt:"s,0"`, // Should match signature if New TGA.
+}
+#assert(size_of(TGA_Footer) == 26)
+
+TGA_Extension :: struct #packed {
+	extension_size:          u16le,               // Size of this struct. If not 495 bytes it means it's an unsupported version.
+	author_name:             [41]u8  `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
+	author_comments:         [324]u8 `fmt:"s,0"`, // Author comments, formatted as 4 lines of 80 character lines, each zero terminated.
+	datetime:                struct {month, day, year, hour, minute, second: u16le},
+	job_name:                [41]u8  `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
+	job_time:                struct {hour, minute, second: u16le},
+	software_id:             [41]u8  `fmt:"s,0"`, // Software ID name, ASCII. Zero-terminated
+	software_version: struct #packed {
+		number: u16le, // Version number * 100
+		letter: u8 `fmt:"r"`,   // " " if not used
+	},
+	key_color:               [4]u8,    // ARGB key color used at time of production
+	aspect_ratio:            [2]u16le, // Numerator / Denominator
+	gamma:                   [2]u16le, // Numerator / Denominator, range should be 0.0..10.0
+	color_correction_offset: u32le,    // 0 if no color correction information
+	postage_stamp_offset:    u32le,    // 0 if no thumbnail
+	scanline_offset:         u32le,    // 0 if no scanline table
+	attributes:              TGA_Alpha_Kind,
+}
+#assert(size_of(TGA_Extension) == 495)
+
+TGA_Alpha_Kind :: enum u8 {
+	None,
+	Undefined_Ignore,
+	Undefined_Retain,
+	Useful,
+	Premultiplied,
+}
+
+TGA_Info :: struct {
+	header:    TGA_Header,
+	image_id:  string,
+	footer:    Maybe(TGA_Footer),
+	extension: Maybe(TGA_Extension),
+}
+
 // Function to help with image buffer calculations
 // Function to help with image buffer calculations
 compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
 compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
 	size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
 	size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height

+ 341 - 2
core/image/tga/tga.odin

@@ -4,6 +4,7 @@
 
 
 	List of contributors:
 	List of contributors:
 		Jeroen van Rijn: Initial implementation.
 		Jeroen van Rijn: Initial implementation.
+		Benoit Jacquier: tga loader
 */
 */
 
 
 
 
@@ -14,11 +15,16 @@ import "core:mem"
 import "core:image"
 import "core:image"
 import "core:bytes"
 import "core:bytes"
 import "core:os"
 import "core:os"
+import "core:compress"
+import "core:strings"
+
+// TODO: alpha_premultiply support
 
 
 Error   :: image.Error
 Error   :: image.Error
 Image   :: image.Image
 Image   :: image.Image
 Options :: image.Options
 Options :: image.Options
 
 
+GA_Pixel   :: image.GA_Pixel
 RGB_Pixel  :: image.RGB_Pixel
 RGB_Pixel  :: image.RGB_Pixel
 RGBA_Pixel :: image.RGBA_Pixel
 RGBA_Pixel :: image.RGBA_Pixel
 
 
@@ -57,7 +63,7 @@ save_to_memory  :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
 	}
 	}
 
 
 	header := image.TGA_Header{
 	header := image.TGA_Header{
-		data_type_code   = 0x02, // Color, uncompressed.
+		data_type_code   = .Uncompressed_RGB,
 		dimensions       = {u16le(img.width), u16le(img.height)},
 		dimensions       = {u16le(img.width), u16le(img.height)},
 		bits_per_pixel   = u8(img.depth * img.channels),
 		bits_per_pixel   = u8(img.depth * img.channels),
 		image_descriptor = 1 << 5, // Origin is top left.
 		image_descriptor = 1 << 5, // Origin is top left.
@@ -98,4 +104,337 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato
 	return nil if write_ok else .Unable_To_Write_File
 	return nil if write_ok else .Unable_To_Write_File
 }
 }
 
 
-save :: proc{save_to_memory, save_to_file}
+save :: proc{save_to_memory, save_to_file}
+
+load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+	context.allocator = allocator
+	options := options
+
+	if .alpha_premultiply in options {
+		return nil, .Unsupported_Option
+	}
+
+	if .info in options {
+		options |= {.return_metadata, .do_not_decompress_image}
+		options -= {.info}
+	}
+
+	if .return_header in options && .return_metadata in options {
+		options -= {.return_header}
+	}
+
+	// First check for a footer.
+	filesize := compress.input_size(ctx) or_return
+
+	footer: image.TGA_Footer
+	have_valid_footer := false
+
+	extension: image.TGA_Extension
+	have_valid_extension := false
+
+	if filesize >= size_of(image.TGA_Header) + size_of(image.TGA_Footer) {
+		if f, f_err := compress.peek_data(ctx, image.TGA_Footer, filesize - i64(size_of(image.TGA_Footer))); f_err == .None {
+			if string(f.signature[:]) == image.New_TGA_Signature {
+				have_valid_footer = true
+				footer = f
+
+				if i64(footer.extension_area_offset) + i64(size_of(image.TGA_Extension)) < filesize {
+					if e, e_err := compress.peek_data(ctx, image.TGA_Extension, footer.extension_area_offset); e_err == .None {
+						if e.extension_size == size_of(image.TGA_Extension) {
+							have_valid_extension = true
+							extension = e
+						}
+					}
+				}
+			}
+		}
+	}
+
+	header := image.read_data(ctx, image.TGA_Header) or_return
+
+	// Header checks
+	rle_encoding  := false
+	color_mapped  := false
+	black_white   := false
+	src_channels  := 0
+	dest_depth    := header.bits_per_pixel
+	dest_channels := 0
+
+	#partial switch header.data_type_code {
+	// Supported formats: RGB(A), RGB(A) RLE
+	case .Compressed_RGB:
+		rle_encoding = true
+	case .Uncompressed_RGB:
+		// Intentionally blank
+	case .Uncompressed_Black_White:
+		black_white  = true
+		dest_depth   = 24
+	case .Uncompressed_Color_Mapped:
+		color_mapped = true
+	case .Compressed_Color_Mapped:
+		color_mapped = true
+		rle_encoding = true
+	case .Compressed_Black_White:
+		black_white  = true
+		rle_encoding = true
+		dest_depth   = 24
+
+	case:
+		return nil, .Unsupported_Format
+	}
+
+	if color_mapped {
+		if header.color_map_type != 1 {
+			return nil, .Unsupported_Format
+		}
+		dest_depth = header.color_map_depth
+
+		// Expect LUT entry index to be 8 bits
+		if header.bits_per_pixel != 8 || header.color_map_origin != 0 || header.color_map_length > 256 {
+			return nil, .Unsupported_Format
+		}
+	}
+
+	switch dest_depth {
+	case 15: // B5G5R5
+		src_channels  = 2
+		dest_channels = 3
+		if color_mapped {
+			src_channels = 1
+		}
+	case 16: // B5G5R5A1
+		src_channels  = 2
+		dest_channels = 3 // Alpha bit is dodgy in TGA, so we ignore it.
+		if color_mapped {
+			src_channels = 1
+		}
+	case 24: // RGB8
+		src_channels  = 1 if (color_mapped || black_white) else 3
+		dest_channels = 3
+	case 32: // RGBA8
+		src_channels  = 4 if !color_mapped else 1
+		dest_channels = 4
+
+	case:
+		return nil, .Unsupported_Format
+	}
+
+	if header.image_descriptor & IMAGE_DESCRIPTOR_INTERLEAVING_MASK != 0 {
+		return nil, .Unsupported_Format
+	}
+
+	if int(header.dimensions[0]) * int(header.dimensions[1]) > image.MAX_DIMENSIONS {
+		return nil, .Image_Dimensions_Too_Large
+	}
+
+	if img == nil {
+		img = new(Image)
+	}
+
+	defer if err != nil {
+		destroy(img)
+	}
+
+	img.which = .TGA
+	img.channels = 4 if .alpha_add_if_missing  in options else dest_channels
+	img.channels = 3 if .alpha_drop_if_present in options else img.channels
+
+	img.depth  = 8
+	img.width  = int(header.dimensions[0])
+	img.height = int(header.dimensions[1])
+
+	// Read Image ID if present
+	image_id := ""
+	if _id, e := compress.read_slice(ctx, int(header.id_length)); e != .None {
+		return img, .Corrupt
+	} else {
+		if .return_metadata in options {
+			id := strings.trim_right_null(string(_id))
+			image_id = strings.clone(id)
+		}
+	}
+
+	color_map := make([]RGBA_Pixel, header.color_map_length)
+	defer delete(color_map)
+
+	if color_mapped {
+		switch header.color_map_depth {
+		case 16:
+			for i in 0..<header.color_map_length {
+				if lut, lut_err := compress.read_data(ctx, GA_Pixel); lut_err != .None {
+					return img, .Corrupt
+				} else {
+					color_map[i].rg = lut
+					color_map[i].ba = 255
+				}
+			}
+
+		case 24:
+			for i in 0..<header.color_map_length {
+				if lut, lut_err := compress.read_data(ctx, RGB_Pixel); lut_err != .None {
+					return img, .Corrupt
+				} else {
+					color_map[i].rgb = lut
+					color_map[i].a   = 255
+				}
+			}
+
+		case 32:
+			for i in 0..<header.color_map_length {
+				if lut, lut_err := compress.read_data(ctx, RGBA_Pixel); lut_err != .None {
+					return img, .Corrupt
+				} else {
+					color_map[i] = lut
+				}
+			}
+		}
+	}
+
+	if .return_metadata in options {
+		info := new(image.TGA_Info)
+		info.header   = header
+		info.image_id = image_id
+		if have_valid_footer {
+			info.footer = footer
+		}
+		if have_valid_extension {
+			info.extension = extension
+		}
+		img.metadata = info
+	}
+
+	if .do_not_decompress_image in options {
+		return img, nil
+	}
+
+	if !resize(&img.pixels.buf, dest_channels * img.width * img.height) {
+		return img, .Unable_To_Allocate_Or_Resize
+	}
+
+	origin_is_top        := header.image_descriptor & IMAGE_DESCRIPTOR_TOP_MASK   != 0
+	origin_is_left       := header.image_descriptor & IMAGE_DESCRIPTOR_RIGHT_MASK == 0
+	rle_repetition_count := 0
+	read_pixel           := true
+	is_packet_rle        := false
+
+	pixel: RGBA_Pixel
+
+	stride := img.width * dest_channels
+	line   := 0 if origin_is_top else img.height - 1
+
+	for _ in 0..<img.height {
+		offset := line * stride + (0 if origin_is_left else (stride - dest_channels))
+		for _ in 0..<img.width {
+			// handle RLE decoding
+			if rle_encoding {
+				if rle_repetition_count == 0 {
+					rle_cmd, err := compress.read_u8(ctx)
+					if err != .None {
+						return img, .Corrupt
+					}
+					is_packet_rle = (rle_cmd >> 7) != 0
+					rle_repetition_count = 1 + int(rle_cmd & 0x7F)
+					read_pixel = true
+				} else if !is_packet_rle {
+					read_pixel = rle_repetition_count > 0
+				} else {
+					read_pixel = false
+				}
+			}
+			// Read pixel
+			if read_pixel {
+				src, src_err := compress.read_slice(ctx, src_channels)
+				if src_err != .None {
+					return img, .Corrupt
+				}
+				switch src_channels {
+				case 1:
+					// Color-mapped or Black & White
+					if black_white {
+						pixel = {src[0], src[0], src[0], 255}
+					} else if header.color_map_depth == 24 {
+						pixel = color_map[src[0]].bgra
+					} else if header.color_map_depth == 16 {
+						lut := color_map[src[0]]
+						v := u16(lut.r) | u16(lut.g) << 8
+						b := u8( v        & 31) << 3
+						g := u8((v >>  5) & 31) << 3
+						r := u8((v >> 10) & 31) << 3
+						pixel = {r, g, b, 255}
+					}
+
+				case 2:
+					v := u16(src[0]) | u16(src[1]) << 8
+					b := u8( v        & 31) << 3
+					g := u8((v >>  5) & 31) << 3
+					r := u8((v >> 10) & 31) << 3
+					pixel = {r, g, b, 255}
+
+				case 3:
+					pixel = {src[2], src[1], src[0], 255}
+				case 4:
+					pixel = {src[2], src[1], src[0], src[3]}
+				case:
+					return img, .Corrupt
+				}
+			}
+
+			// Write pixel
+			copy(img.pixels.buf[offset:], pixel[:dest_channels])
+			offset += dest_channels if origin_is_left else -dest_channels
+			rle_repetition_count -= 1
+		}
+		line += 1 if origin_is_top else -1
+	}
+	return img, nil
+}
+
+load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+	ctx := &compress.Context_Memory_Input{
+		input_data = data,
+	}
+
+	img, err = load_from_context(ctx, options, allocator)
+	return img, err
+}
+
+load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+	context.allocator = allocator
+
+	data, ok := os.read_entire_file(filename)
+	defer delete(data)
+
+	if ok {
+		return load_from_bytes(data, options)
+	} else {
+		return nil, .Unable_To_Read_File
+	}
+}
+
+load :: proc{load_from_file, load_from_bytes, load_from_context}
+
+destroy :: proc(img: ^Image) {
+	if img == nil || img.width == 0 || img.height == 0 {
+		return
+	}
+
+	bytes.buffer_destroy(&img.pixels)
+	if v, ok := img.metadata.(^image.TGA_Info); ok {
+		delete(v.image_id)
+		free(v)
+	}
+
+	// Make destroy idempotent
+	img.width  = 0
+	img.height = 0
+	free(img)
+}
+
+IMAGE_DESCRIPTOR_INTERLEAVING_MASK :: (1<<6) | (1<<7)
+IMAGE_DESCRIPTOR_RIGHT_MASK :: 1<<4
+IMAGE_DESCRIPTOR_TOP_MASK   :: 1<<5
+
+@(init, private)
+_register :: proc() {
+	image.register(.TGA, load_from_bytes, destroy)
+}

+ 151 - 0
core/sys/windows/user32.odin

@@ -190,6 +190,14 @@ foreign user32 {
 	SetWindowTextW :: proc(hWnd: HWND, lpString: LPCWSTR) -> BOOL ---
 	SetWindowTextW :: proc(hWnd: HWND, lpString: LPCWSTR) -> BOOL ---
 	CallWindowProcW :: proc(lpPrevWndFunc: WNDPROC, hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
 	CallWindowProcW :: proc(lpPrevWndFunc: WNDPROC, hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
 	EnableWindow :: proc(hWnd: HWND, bEnable: BOOL) -> BOOL ---
 	EnableWindow :: proc(hWnd: HWND, bEnable: BOOL) -> BOOL ---
+
+	DefRawInputProc :: proc(paRawInput: ^PRAWINPUT, nInput: INT, cbSizeHeader: UINT) -> LRESULT ---
+	GetRawInputBuffer :: proc(pRawInput: PRAWINPUT, pcbSize: PUINT, cbSizeHeader: UINT) -> UINT ---
+	GetRawInputData :: proc(hRawInput: HRAWINPUT, uiCommand: UINT, pData: LPVOID, pcbSize: PUINT, cbSizeHeader: UINT) -> UINT ---
+	GetRawInputDeviceInfoW :: proc(hDevice: HANDLE, uiCommand: UINT, pData: LPVOID, pcbSize: PUINT) -> UINT ---
+	GetRawInputDeviceList :: proc(pRawInputDeviceList: PRAWINPUTDEVICELIST, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
+	GetRegisteredRawInputDevices :: proc(pRawInputDevices: PRAWINPUTDEVICE, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
+	RegisterRawInputDevices :: proc(pRawInputDevices: PCRAWINPUTDEVICE, uiNumDevices: UINT, cbSize: UINT) -> BOOL ---
 }
 }
 
 
 CreateWindowW :: #force_inline proc "stdcall" (
 CreateWindowW :: #force_inline proc "stdcall" (
@@ -277,3 +285,146 @@ DPI_AWARENESS_CONTEXT_SYSTEM_AWARE         :: DPI_AWARENESS_CONTEXT(~uintptr(1))
 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE    :: DPI_AWARENESS_CONTEXT(~uintptr(2)) // -3
 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE    :: DPI_AWARENESS_CONTEXT(~uintptr(2)) // -3
 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 :: DPI_AWARENESS_CONTEXT(~uintptr(3)) // -4
 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 :: DPI_AWARENESS_CONTEXT(~uintptr(3)) // -4
 DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED    :: DPI_AWARENESS_CONTEXT(~uintptr(4)) // -5
 DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED    :: DPI_AWARENESS_CONTEXT(~uintptr(4)) // -5
+
+RAWINPUTHEADER :: struct {
+	dwType: DWORD,
+	dwSize: DWORD,
+	hDevice: HANDLE,
+	wParam: WPARAM,
+}
+
+RAWHID :: struct {
+	dwSizeHid: DWORD,
+	dwCount: DWORD,
+	bRawData: [1]BYTE,
+}
+
+RAWMOUSE :: struct {
+	usFlags: USHORT,
+	DUMMYUNIONNAME: struct #raw_union {
+		ulButtons: ULONG,
+		DUMMYSTRUCTNAME: struct {
+			usButtonFlags: USHORT,
+			usButtonData: USHORT,
+		},
+	},
+	ulRawButtons: ULONG,
+	lLastX: LONG,
+	lLastY: LONG,
+	ulExtraInformation: ULONG,
+}
+
+RAWKEYBOARD :: struct {
+	MakeCode: USHORT,
+	Flags: USHORT,
+	Rserved: USHORT,
+	VKey: USHORT,
+	Message: UINT,
+	ExtraInformation: ULONG,
+}
+
+RAWINPUT :: struct {
+	header: RAWINPUTHEADER,
+	data: struct #raw_union {
+		mouse: RAWMOUSE,
+		keyboard: RAWKEYBOARD,
+		hid: RAWHID,
+	},
+}
+
+PRAWINPUT :: ^RAWINPUT
+HRAWINPUT :: distinct LPARAM
+
+RAWINPUTDEVICE :: struct {
+	usUsagePage: USHORT,
+	usUsage: USHORT,
+	dwFlags: DWORD,
+	hwndTarget: HWND,
+}
+
+PRAWINPUTDEVICE :: ^RAWINPUTDEVICE
+PCRAWINPUTDEVICE :: PRAWINPUTDEVICE
+
+RAWINPUTDEVICELIST :: struct {
+	hDevice: HANDLE,
+	dwType: DWORD,
+}
+
+PRAWINPUTDEVICELIST :: ^RAWINPUTDEVICELIST
+
+RID_DEVICE_INFO_HID :: struct {
+	dwVendorId: DWORD,
+	dwProductId: DWORD,
+	dwVersionNumber: DWORD,
+	usUsagePage: USHORT,
+	usUsage: USHORT,
+}
+
+RID_DEVICE_INFO_KEYBOARD :: struct {
+	dwType: DWORD,
+	dwSubType: DWORD,
+	dwKeyboardMode: DWORD,
+	dwNumberOfFunctionKeys: DWORD,
+	dwNumberOfIndicators: DWORD,
+	dwNumberOfKeysTotal: DWORD,
+}
+
+RID_DEVICE_INFO_MOUSE :: struct {
+	dwId: DWORD,
+	dwNumberOfButtons: DWORD,
+	dwSampleRate: DWORD,
+	fHasHorizontalWheel: BOOL,
+}
+
+RID_DEVICE_INFO :: struct {
+	cbSize: DWORD,
+	dwType: DWORD,
+	DUMMYUNIONNAME: struct #raw_union {
+		mouse: RID_DEVICE_INFO_MOUSE,
+		keyboard: RID_DEVICE_INFO_KEYBOARD,
+		hid: RID_DEVICE_INFO_HID,
+	},
+}
+
+RIDEV_REMOVE :: 0x00000001
+RIDEV_EXCLUDE :: 0x00000010
+RIDEV_PAGEONLY :: 0x00000020
+RIDEV_NOLEGACY :: 0x00000030
+RIDEV_INPUTSINK :: 0x00000100
+RIDEV_CAPTUREMOUSE :: 0x00000200
+RIDEV_NOHOTKEYS :: 0x00000200
+RIDEV_APPKEYS :: 0x00000400
+RIDEV_EXINPUTSINK :: 0x00001000
+RIDEV_DEVNOTIFY :: 0x00002000
+
+RID_HEADER :: 0x10000005
+RID_INPUT :: 0x10000003
+
+RIM_TYPEMOUSE :: 0
+RIM_TYPEKEYBOARD :: 1
+RIM_TYPEHID :: 2
+
+MOUSE_MOVE_RELATIVE :: 0x00
+MOUSE_MOVE_ABSOLUTE :: 0x01
+MOUSE_VIRTUAL_DESKTOP :: 0x02
+MOUSE_ATTRIUBTTES_CHANGED :: 0x04
+MOUSE_MOVE_NOCOALESCE :: 0x08
+
+RI_MOUSE_BUTTON_1_DOWN :: 0x0001
+RI_MOUSE_LEFT_BUTTON_DOWNS :: RI_MOUSE_BUTTON_1_DOWN
+RI_MOUSE_BUTTON_1_UP :: 0x0002
+RI_MOUSE_LEFT_BUTTON_UP :: RI_MOUSE_BUTTON_1_UP
+RI_MOUSE_BUTTON_2_DOWN :: 0x0004
+RI_MOUSE_RIGHT_BUTTON_DOWN :: RI_MOUSE_BUTTON_2_DOWN
+RI_MOUSE_BUTTON_2_UP :: 0x0008
+RI_MOUSE_RIGHT_BUTTON_UP :: RI_MOUSE_BUTTON_2_UP
+RI_MOUSE_BUTTON_3_DOWN :: 0x0010
+RI_MOUSE_MIDDLE_BUTTON_DOWN :: RI_MOUSE_BUTTON_3_DOWN
+RI_MOUSE_BUTTON_3_UP :: 0x0020
+RI_MOUSE_MIDDLE_BUTTON_UP :: RI_MOUSE_BUTTON_3_UP
+RI_MOUSE_BUTTON_4_DOWN :: 0x0040
+RI_MOUSE_BUTTON_4_UP :: 0x0080
+RI_MOUSE_BUTTON_5_DOWN :: 0x0100
+RI_MOUSE_BUTTON_5_UP :: 0x0200
+RI_MOUSE_WHEEL :: 0x0400
+RI_MOUSE_HWHEEL :: 0x0800