Browse Source

Merge branch 'master' into windows-llvm-13.0.0

gingerBill 3 years ago
parent
commit
660aa7d78e
87 changed files with 5007 additions and 656 deletions
  1. 5 3
      build.bat
  2. 13 1
      build_odin.sh
  3. 56 1
      core/compress/common.odin
  4. 19 0
      core/container/queue/queue.odin
  5. 1 0
      core/encoding/json/marshal.odin
  6. 18 3
      core/fmt/fmt.odin
  7. 60 2
      core/image/common.odin
  8. 341 2
      core/image/tga/tga.odin
  9. 2 0
      core/intrinsics/intrinsics.odin
  10. 1 1
      core/math/ease/ease.odin
  11. 6 0
      core/math/math.odin
  12. 1 42
      core/mem/mem.odin
  13. 3 3
      core/mem/virtual/growing_arena.odin
  14. 1 1
      core/mem/virtual/virtual_linux.odin
  15. 1 1
      core/os/os_darwin.odin
  16. 1 1
      core/os/os_linux.odin
  17. 1 1
      core/os/os_openbsd.odin
  18. 45 24
      core/reflect/reflect.odin
  19. 21 13
      core/runtime/core_builtin.odin
  20. 2 2
      core/slice/slice.odin
  21. 62 0
      core/slice/sort.odin
  22. 177 0
      core/slice/sort_private.odin
  23. 31 18
      core/strconv/strconv.odin
  24. 5 5
      core/strings/strings.odin
  25. 4 3
      core/sync/futex_darwin.odin
  26. 26 0
      core/sys/info/cpu_arm.odin
  27. 37 2
      core/sys/info/cpu_intel.odin
  28. 78 0
      core/sys/info/doc.odin
  29. 510 0
      core/sys/info/platform_darwin.odin
  30. 76 0
      core/sys/info/platform_freebsd.odin
  31. 136 0
      core/sys/info/platform_linux.odin
  32. 69 0
      core/sys/info/platform_openbsd.odin
  33. 375 0
      core/sys/info/platform_windows.odin
  34. 45 0
      core/sys/info/sysinfo.odin
  35. 7 0
      core/sys/unix/syscalls_freebsd.odin
  36. 3 3
      core/sys/unix/syscalls_linux.odin
  37. 6 0
      core/sys/unix/syscalls_openbsd.odin
  38. 45 0
      core/sys/unix/sysctl_darwin.odin
  39. 44 0
      core/sys/unix/sysctl_freebsd.odin
  40. 49 0
      core/sys/unix/sysctl_openbsd.odin
  41. 2 2
      core/sys/valgrind/callgrind.odin
  42. 178 0
      core/sys/valgrind/helgrind.odin
  43. 2 2
      core/sys/valgrind/memcheck.odin
  44. 4 4
      core/sys/valgrind/valgrind.odin
  45. 36 10
      core/sys/windows/kernel32.odin
  46. 171 0
      core/sys/windows/types.odin
  47. 151 0
      core/sys/windows/user32.odin
  48. 3 3
      core/sys/windows/util.odin
  49. 2 0
      core/sys/windows/winerror.odin
  50. 3 0
      examples/all/all_main.odin
  51. 639 313
      src/bug_report.cpp
  52. 112 63
      src/build_settings.cpp
  53. 50 5
      src/check_builtin.cpp
  54. 18 2
      src/check_expr.cpp
  55. 49 6
      src/check_stmt.cpp
  56. 4 2
      src/check_type.cpp
  57. 1 0
      src/checker.cpp
  58. 3 0
      src/checker_builtin_procs.hpp
  59. 4 0
      src/common.cpp
  60. 1 1
      src/llvm_abi.cpp
  61. 2 1
      src/llvm_backend.cpp
  62. 4 0
      src/llvm_backend_const.cpp
  63. 2 1
      src/llvm_backend_debug.cpp
  64. 22 12
      src/llvm_backend_expr.cpp
  65. 11 23
      src/llvm_backend_general.cpp
  66. 5 3
      src/llvm_backend_proc.cpp
  67. 13 3
      src/llvm_backend_stmt.cpp
  68. 14 0
      src/llvm_backend_utility.cpp
  69. 43 43
      src/main.cpp
  70. 50 1
      src/parser.cpp
  71. 3 4
      src/types.cpp
  72. 6 1
      tests/core/build.bat
  73. 182 0
      tests/core/slice/test_core_slice.odin
  74. 1 1
      tools/odinfmt/flag/flag.odin
  75. 1 1
      tools/odinfmt/main.odin
  76. 2 0
      vendor/OpenGL/constants.odin
  77. 1 1
      vendor/OpenGL/helpers.odin
  78. 1 1
      vendor/OpenGL/wrappers.odin
  79. 8 1
      vendor/README.md
  80. 170 0
      vendor/commonmark/LICENSE
  81. 4 0
      vendor/commonmark/build.bat
  82. 526 0
      vendor/commonmark/cmark.odin
  83. BIN
      vendor/commonmark/cmark_static.lib
  84. 117 0
      vendor/commonmark/doc.odin
  85. 1 12
      vendor/darwin/Foundation/NSWindow.odin
  86. 1 1
      vendor/sdl2/sdl2.odin
  87. 1 1
      vendor/wasm/js/runtime.js

+ 5 - 3
build.bat

@@ -72,10 +72,12 @@ del *.ilk > NUL 2> NUL
 cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
 cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
 if %errorlevel% neq 0 goto end_of_build
 if %errorlevel% neq 0 goto end_of_build
 
 
-call build_vendor.bat
-if %errorlevel% neq 0 goto end_of_build
+rem call build_vendor.bat
+rem if %errorlevel% neq 0 goto end_of_build
 
 
-if %release_mode% EQU 0 odin run examples/demo
+odin run examples/demo -vet -strict-style
+rem odin check examples/all
+rem odin run examples/bug
 
 
 del *.obj > NUL 2> NUL
 del *.obj > NUL 2> NUL
 
 

+ 13 - 1
build_odin.sh

@@ -50,7 +50,19 @@ config_darwin() {
 }
 }
 
 
 config_freebsd() {
 config_freebsd() {
-	: ${LLVM_CONFIG=/usr/local/bin/llvm-config11}
+	: ${LLVM_CONFIG=}
+
+	if [ ! "$LLVM_CONFIG" ]; then
+		if which llvm-config11 > /dev/null 2>&1; then
+			LLVM_CONFIG=llvm-config11
+		elif which llvm-config12 > /dev/null 2>&1; then
+			LLVM_CONFIG=llvm-config12
+		elif which llvm-config13 > /dev/null 2>&1; then
+			LLVM_CONFIG=llvm-config13
+		else
+			panic "Unable to find LLVM-config"
+		fi
+	fi
 
 
 	CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
 	CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
 	LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
 	LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"

+ 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)

+ 18 - 3
core/fmt/fmt.odin

@@ -2059,18 +2059,33 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 			ed         := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
 			ed         := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
 			entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
 			entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
 			entry_size := ed.elem_size
 			entry_size := ed.elem_size
-
+			/*
+				NOTE: The layout of a `map` is as follows:
+
+					map[Key]Value
+
+				## Internal Layout
+				struct {
+					hashes: []int,
+					entries: [dynamic]struct{
+						hash:  uintptr,
+						next:  int,
+						key:   Key,
+						value: Value,
+					},
+				}
+			*/
 			for i in 0..<entries.len {
 			for i in 0..<entries.len {
 				if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
 				if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
 
 
 				data := uintptr(entries.data) + uintptr(i*entry_size)
 				data := uintptr(entries.data) + uintptr(i*entry_size)
 
 
-				key := data + entry_type.offsets[2]
+				key := data + entry_type.offsets[2] // key: Key
 				fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
 				fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
 
 
 				io.write_string(fi.writer, "=", &fi.n)
 				io.write_string(fi.writer, "=", &fi.n)
 
 
-				value := data + entry_type.offsets[3]
+				value := data + entry_type.offsets[3] // value: Value
 				fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
 				fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
 			}
 			}
 		}
 		}

+ 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)
+}

+ 2 - 0
core/intrinsics/intrinsics.odin

@@ -188,6 +188,8 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
 type_equal_proc  :: proc($T: typeid) -> (equal:  proc "contextless" (rawptr, rawptr) -> bool)                 where type_is_comparable(T) ---
 type_equal_proc  :: proc($T: typeid) -> (equal:  proc "contextless" (rawptr, rawptr) -> bool)                 where type_is_comparable(T) ---
 type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
 type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
 
 
+type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
+
 constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
 constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
 
 
 // SIMD related
 // SIMD related

+ 1 - 1
core/math/ease/ease.odin

@@ -356,7 +356,7 @@ Flux_Tween :: struct($T: typeid) {
 flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
 flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
 	return {
 	return {
 		values = make(map[^T]Flux_Tween(T), value_capacity),
 		values = make(map[^T]Flux_Tween(T), value_capacity),
-		keys_to_be_deleted = make([dynamic]^T, 0, value_capacity)
+		keys_to_be_deleted = make([dynamic]^T, 0, value_capacity),
 	}
 	}
 }
 }
 
 

+ 6 - 0
core/math/math.odin

@@ -1,6 +1,7 @@
 package math
 package math
 
 
 import "core:intrinsics"
 import "core:intrinsics"
+import "core:builtin"
 _ :: intrinsics
 _ :: intrinsics
 
 
 Float_Class :: enum {
 Float_Class :: enum {
@@ -36,6 +37,11 @@ MAX_F16_PRECISION ::  4 // Maximum number of meaningful digits after the decimal
 RAD_PER_DEG :: TAU/360.0
 RAD_PER_DEG :: TAU/360.0
 DEG_PER_RAD :: 360.0/TAU
 DEG_PER_RAD :: 360.0/TAU
 
 
+abs :: builtin.abs
+min :: builtin.min
+max :: builtin.max
+clamp :: builtin.clamp
+
 sqrt_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(sqrt_f16(f16(x))) }
 sqrt_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(sqrt_f16(f16(x))) }
 sqrt_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(sqrt_f16(f16(x))) }
 sqrt_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(sqrt_f16(f16(x))) }
 sqrt_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(sqrt_f32(f32(x))) }
 sqrt_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(sqrt_f32(f32(x))) }

+ 1 - 42
core/mem/mem.odin

@@ -54,48 +54,7 @@ compare :: proc "contextless" (a, b: []byte) -> int {
 }
 }
 
 
 compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
 compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
-	switch {
-	case a == b:
-		return 0
-	case a == nil:
-		return -1
-	case b == nil:
-		return -1
-	case n == 0:
-		return 0
-	}
-
-	x := slice_ptr(a, n)
-	y := slice_ptr(b, n)
-
-	SU :: size_of(uintptr)
-	fast := n/SU + 1
-	offset := (fast-1)*SU
-	curr_block := 0
-	if n < SU {
-		fast = 0
-	}
-
-	la := slice_ptr((^uintptr)(a), fast)
-	lb := slice_ptr((^uintptr)(b), fast)
-
-	for /**/; curr_block < fast; curr_block += 1 {
-		if la[curr_block] ~ lb[curr_block] != 0 {
-			for pos := curr_block*SU; pos < n; pos += 1 {
-				if x[pos] ~ y[pos] != 0 {
-					return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1
-				}
-			}
-		}
-	}
-
-	for /**/; offset < n; offset += 1 {
-		if x[offset] ~ y[offset] != 0 {
-			return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1
-		}
-	}
-
-	return 0
+	return runtime.memory_compare(a, b, n)
 }
 }
 
 
 check_zero :: proc(data: []byte) -> bool {
 check_zero :: proc(data: []byte) -> bool {

+ 3 - 3
core/mem/virtual/growing_arena.odin

@@ -13,10 +13,10 @@ Growing_Arena :: struct {
 
 
 DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
 DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
 
 
-growing_arena_init :: proc(arena: ^Static_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
-	arena.block = memory_block_alloc(0, reserved, {}) or_return
+growing_arena_init :: proc(arena: ^Growing_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
+	arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
 	arena.total_used = 0
 	arena.total_used = 0
-	arena.total_reserved = arena.block.reserved
+	arena.total_reserved = arena.curr_block.reserved
 	return
 	return
 }
 }
 
 

+ 1 - 1
core/mem/virtual/virtual_linux.odin

@@ -48,7 +48,7 @@ munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
 }
 }
 
 
 mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int {
 mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int {
-	res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
+	res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot))
 	return c.int(res)
 	return c.int(res)
 }
 }
 
 

+ 1 - 1
core/os/os_darwin.odin

@@ -596,7 +596,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
 		return "", Errno(get_last_error())
 		return "", Errno(get_last_error())
 	}
 	}
 
 
-	path := strings.clone_from_cstring(cstring(&buf[0]), context.temp_allocator)
+	path := strings.clone_from_cstring(cstring(&buf[0]))
 	return path, ERROR_NONE
 	return path, ERROR_NONE
 }
 }
 
 

+ 1 - 1
core/os/os_linux.odin

@@ -311,7 +311,7 @@ _unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
 		low := uintptr(offset & 0xFFFFFFFF)
 		low := uintptr(offset & 0xFFFFFFFF)
 		high := uintptr(offset >> 32)
 		high := uintptr(offset >> 32)
 		result: i64
 		result: i64
-		res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence)))
+		res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
 		return -1 if res < 0 else result
 		return -1 if res < 0 else result
 	}
 	}
 }
 }

+ 1 - 1
core/os/os_openbsd.odin

@@ -705,4 +705,4 @@ _alloc_command_line_arguments :: proc() -> []string {
 		res[i] = string(arg)
 		res[i] = string(arg)
 	}
 	}
 	return res
 	return res
-}
+}

+ 45 - 24
core/reflect/reflect.odin

@@ -1,8 +1,9 @@
 package reflect
 package reflect
 
 
 import "core:runtime"
 import "core:runtime"
-import "core:mem"
 import "core:intrinsics"
 import "core:intrinsics"
+import "core:mem"
+_ :: mem
 _ :: intrinsics
 _ :: intrinsics
 
 
 Type_Info :: runtime.Type_Info
 Type_Info :: runtime.Type_Info
@@ -224,7 +225,7 @@ align_of_typeid :: proc(T: typeid) -> int {
 as_bytes :: proc(v: any) -> []byte {
 as_bytes :: proc(v: any) -> []byte {
 	if v != nil {
 	if v != nil {
 		sz := size_of_typeid(v.id)
 		sz := size_of_typeid(v.id)
-		return mem.slice_ptr((^byte)(v.data), sz)
+		return ([^]byte)(v.data)[:sz]
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -266,19 +267,19 @@ length :: proc(val: any) -> int {
 		return a.count
 		return a.count
 
 
 	case Type_Info_Slice:
 	case Type_Info_Slice:
-		return (^mem.Raw_Slice)(val.data).len
+		return (^runtime.Raw_Slice)(val.data).len
 
 
 	case Type_Info_Dynamic_Array:
 	case Type_Info_Dynamic_Array:
-		return (^mem.Raw_Dynamic_Array)(val.data).len
+		return (^runtime.Raw_Dynamic_Array)(val.data).len
 
 
 	case Type_Info_Map:
 	case Type_Info_Map:
-		return (^mem.Raw_Map)(val.data).entries.len
+		return (^runtime.Raw_Map)(val.data).entries.len
 
 
 	case Type_Info_String:
 	case Type_Info_String:
 		if a.is_cstring {
 		if a.is_cstring {
 			return len((^cstring)(val.data)^)
 			return len((^cstring)(val.data)^)
 		} else {
 		} else {
-			return (^mem.Raw_String)(val.data).len
+			return (^runtime.Raw_String)(val.data).len
 		}
 		}
 	}
 	}
 	return 0
 	return 0
@@ -301,10 +302,10 @@ capacity :: proc(val: any) -> int {
 		return a.count
 		return a.count
 
 
 	case Type_Info_Dynamic_Array:
 	case Type_Info_Dynamic_Array:
-		return (^mem.Raw_Dynamic_Array)(val.data).cap
+		return (^runtime.Raw_Dynamic_Array)(val.data).cap
 
 
 	case Type_Info_Map:
 	case Type_Info_Map:
-		return (^mem.Raw_Map)(val.data).entries.cap
+		return (^runtime.Raw_Map)(val.data).entries.cap
 	}
 	}
 	return 0
 	return 0
 }
 }
@@ -344,14 +345,14 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
 		return any{data, a.elem.id}
 		return any{data, a.elem.id}
 
 
 	case Type_Info_Slice:
 	case Type_Info_Slice:
-		raw := (^mem.Raw_Slice)(val.data)
+		raw := (^runtime.Raw_Slice)(val.data)
 		runtime.bounds_check_error_loc(loc, i, raw.len)
 		runtime.bounds_check_error_loc(loc, i, raw.len)
 		offset := uintptr(a.elem.size * i)
 		offset := uintptr(a.elem.size * i)
 		data := rawptr(uintptr(raw.data) + offset)
 		data := rawptr(uintptr(raw.data) + offset)
 		return any{data, a.elem.id}
 		return any{data, a.elem.id}
 
 
 	case Type_Info_Dynamic_Array:
 	case Type_Info_Dynamic_Array:
-		raw := (^mem.Raw_Dynamic_Array)(val.data)
+		raw := (^runtime.Raw_Dynamic_Array)(val.data)
 		runtime.bounds_check_error_loc(loc, i, raw.len)
 		runtime.bounds_check_error_loc(loc, i, raw.len)
 		offset := uintptr(a.elem.size * i)
 		offset := uintptr(a.elem.size * i)
 		data := rawptr(uintptr(raw.data) + offset)
 		data := rawptr(uintptr(raw.data) + offset)
@@ -360,7 +361,7 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
 	case Type_Info_String:
 	case Type_Info_String:
 		if a.is_cstring { return nil }
 		if a.is_cstring { return nil }
 
 
-		raw := (^mem.Raw_String)(val.data)
+		raw := (^runtime.Raw_String)(val.data)
 		runtime.bounds_check_error_loc(loc, i, raw.len)
 		runtime.bounds_check_error_loc(loc, i, raw.len)
 		offset := uintptr(size_of(u8) * i)
 		offset := uintptr(size_of(u8) * i)
 		data := rawptr(uintptr(raw.data) + offset)
 		data := rawptr(uintptr(raw.data) + offset)
@@ -729,6 +730,26 @@ get_union_variant_raw_tag :: proc(a: any) -> i64 {
 	panic("expected a union to reflect.get_union_variant_raw_tag")
 	panic("expected a union to reflect.get_union_variant_raw_tag")
 }
 }
 
 
+get_union_variant :: proc(a: any) -> any {
+	if a == nil {
+		return nil
+	}
+	id := union_variant_typeid(a)
+	if id == nil {
+		return nil
+	}
+	return any{a.data, id}
+}
+
+get_union_as_ptr_variants :: proc(val: ^$T) -> (res: intrinsics.type_convert_variants_to_pointers(T)) where intrinsics.type_is_union(T) {
+	ptr := rawptr(val)
+	tag := get_union_variant_raw_tag(val^)
+	mem.copy(&res, &ptr, size_of(ptr))
+	set_union_variant_raw_tag(res, tag)
+	return
+}
+
+
 
 
 set_union_variant_raw_tag :: proc(a: any, tag: i64) {
 set_union_variant_raw_tag :: proc(a: any, tag: i64) {
 	if a == nil { return }
 	if a == nil { return }
@@ -826,17 +847,17 @@ set_union_value :: proc(dst: any, value: any) -> bool {
 	ti := runtime.type_info_base(type_info_of(dst.id))
 	ti := runtime.type_info_base(type_info_of(dst.id))
 	if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
 	if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
 		if value.id == nil {
 		if value.id == nil {
-			mem.zero(dst.data, ti.size)
+			intrinsics.mem_zero(dst.data, ti.size)
 			return true
 			return true
 		}
 		}
 		if ti.id == runtime.typeid_base(value.id) {
 		if ti.id == runtime.typeid_base(value.id) {
-			mem.copy(dst.data, value.data, ti.size)
+			intrinsics.mem_copy(dst.data, value.data, ti.size)
 			return true
 			return true
 		}
 		}
 		
 		
 		if type_info_union_is_pure_maybe(info) {
 		if type_info_union_is_pure_maybe(info) {
 			if variant := info.variants[0]; variant.id == value.id {
 			if variant := info.variants[0]; variant.id == value.id {
-				mem.copy(dst.data, value.data, variant.size)
+				intrinsics.mem_copy(dst.data, value.data, variant.size)
 				return true
 				return true
 			}
 			}
 			return false
 			return false
@@ -848,7 +869,7 @@ set_union_value :: proc(dst: any, value: any) -> bool {
 				if !info.no_nil {
 				if !info.no_nil {
 					tag += 1
 					tag += 1
 				}
 				}
-				mem.copy(dst.data, value.data, variant.size)
+				intrinsics.mem_copy(dst.data, value.data, variant.size)
 				set_union_variant_raw_tag(dst, tag)
 				set_union_variant_raw_tag(dst, tag)
 				return true
 				return true
 			}
 			}
@@ -1341,11 +1362,11 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
 
 
 	case Type_Info_Slice:
 	case Type_Info_Slice:
 		valid = true
 		valid = true
-		value = (^mem.Raw_Slice)(a.data).data
+		value = (^runtime.Raw_Slice)(a.data).data
 
 
 	case Type_Info_Dynamic_Array:
 	case Type_Info_Dynamic_Array:
 		valid = true
 		valid = true
-		value = (^mem.Raw_Dynamic_Array)(a.data).data
+		value = (^runtime.Raw_Dynamic_Array)(a.data).data
 	}
 	}
 
 
 	return
 	return
@@ -1387,7 +1408,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
 	}
 	}
 
 
 	if .Simple_Compare in t.flags {
 	if .Simple_Compare in t.flags {
-		return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
+		return runtime.memory_compare(a.data, b.data, t.size) == 0
 	}
 	}
 	
 	
 	t = runtime.type_info_core(t)
 	t = runtime.type_info_core(t)
@@ -1425,7 +1446,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
 		Type_Info_Relative_Pointer,
 		Type_Info_Relative_Pointer,
 		Type_Info_Soa_Pointer,
 		Type_Info_Soa_Pointer,
 		Type_Info_Matrix:
 		Type_Info_Matrix:
-		return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
+		return runtime.memory_compare(a.data, b.data, t.size) == 0
 		
 		
 	case Type_Info_String:
 	case Type_Info_String:
 		if v.is_cstring {
 		if v.is_cstring {
@@ -1479,8 +1500,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
 		if !including_indirect_array_recursion {
 		if !including_indirect_array_recursion {
 			return false
 			return false
 		}
 		}
-		array_a := (^mem.Raw_Slice)(a.data)
-		array_b := (^mem.Raw_Slice)(b.data)
+		array_a := (^runtime.Raw_Slice)(a.data)
+		array_b := (^runtime.Raw_Slice)(b.data)
 		if array_a.len != array_b.len {
 		if array_a.len != array_b.len {
 			return false
 			return false
 		}
 		}
@@ -1499,8 +1520,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
 		if !including_indirect_array_recursion {
 		if !including_indirect_array_recursion {
 			return false
 			return false
 		}
 		}
-		array_a := (^mem.Raw_Dynamic_Array)(a.data)
-		array_b := (^mem.Raw_Dynamic_Array)(b.data)
+		array_a := (^runtime.Raw_Dynamic_Array)(a.data)
+		array_b := (^runtime.Raw_Dynamic_Array)(b.data)
 		if array_a.len != array_b.len {
 		if array_a.len != array_b.len {
 			return false
 			return false
 		}
 		}
@@ -1508,7 +1529,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
 			return true
 			return true
 		}
 		}
 		if .Simple_Compare in v.elem.flags {
 		if .Simple_Compare in v.elem.flags {
-			return mem.compare_byte_ptrs((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0
+			return runtime.memory_compare((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0
 		}
 		}
 		
 		
 		for i in 0..<array_a.len {
 		for i in 0..<array_a.len {

+ 21 - 13
core/runtime/core_builtin.odin

@@ -342,12 +342,13 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
 
 
 
 
 @builtin
 @builtin
-append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location)  {
+append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> int {
 	if array == nil {
 	if array == nil {
-		return
+		return 0
 	}
 	}
 	when size_of(E) == 0 {
 	when size_of(E) == 0 {
-		a.len += 1
+		array.len += 1
+		return 1
 	} else {
 	} else {
 		if cap(array) < len(array)+1 {
 		if cap(array) < len(array)+1 {
 			cap := 2 * cap(array) + max(8, 1)
 			cap := 2 * cap(array) + max(8, 1)
@@ -361,23 +362,26 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location)  {
 				data[a.len] = arg
 				data[a.len] = arg
 			}
 			}
 			a.len += 1
 			a.len += 1
+			return 1
 		}
 		}
+		return 0
 	}
 	}
 }
 }
 
 
 @builtin
 @builtin
-append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)  {
+append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int {
 	if array == nil {
 	if array == nil {
-		return
+		return 0
 	}
 	}
 
 
 	arg_len := len(args)
 	arg_len := len(args)
 	if arg_len <= 0 {
 	if arg_len <= 0 {
-		return
+		return 0
 	}
 	}
 
 
 	when size_of(E) == 0 {
 	when size_of(E) == 0 {
-		a.len += arg_len
+		array.len += arg_len
+		return arg_len
 	} else {
 	} else {
 		if cap(array) < len(array)+arg_len {
 		if cap(array) < len(array)+arg_len {
 			cap := 2 * cap(array) + max(8, arg_len)
 			cap := 2 * cap(array) + max(8, arg_len)
@@ -393,23 +397,25 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
 			}
 			}
 			a.len += arg_len
 			a.len += arg_len
 		}
 		}
+		return arg_len
 	}
 	}
 }
 }
 
 
 // The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
 // The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
 @builtin
 @builtin
-append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) {
+append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> int {
 	args := transmute([]E)arg
 	args := transmute([]E)arg
-	append_elems(array=array, args=args, loc=loc)
+	return append_elems(array=array, args=args, loc=loc)
 }
 }
 
 
 
 
 // The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
 // The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
 @builtin
 @builtin
-append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) {
+append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int) {
 	for arg in args {
 	for arg in args {
-		append(array = array, args = transmute([]E)(arg), loc = loc)
+		n += append(array = array, args = transmute([]E)(arg), loc = loc)
 	}
 	}
+	return
 }
 }
 
 
 // The append built-in procedure appends elements to the end of a dynamic array
 // The append built-in procedure appends elements to the end of a dynamic array
@@ -417,11 +423,13 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
 
 
 
 
 @builtin
 @builtin
-append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
+append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> int {
 	if array == nil {
 	if array == nil {
-		return
+		return 0
 	}
 	}
+	prev_len := len(array)
 	resize(array, len(array)+1)
 	resize(array, len(array)+1)
+	return len(array)-prev_len
 }
 }
 
 
 
 

+ 2 - 2
core/slice/slice.odin

@@ -14,14 +14,14 @@ _ :: mem
 	Turn a pointer and a length into a slice.
 	Turn a pointer and a length into a slice.
 */
 */
 from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T {
 from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T {
-    return ([^]T)(ptr)[:count]
+	return ([^]T)(ptr)[:count]
 }
 }
 
 
 /*
 /*
 	Turn a pointer and a length into a byte slice.
 	Turn a pointer and a length into a byte slice.
 */
 */
 bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte {
 bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte {
-    return ([^]byte)(ptr)[:byte_count]
+	return ([^]byte)(ptr)[:byte_count]
 }
 }
 
 
 /*
 /*

+ 62 - 0
core/slice/sort.odin

@@ -38,6 +38,52 @@ sort :: proc(data: $T/[]$E) where ORD(E) {
 	}
 	}
 }
 }
 
 
+
+sort_by_indices :: proc{ sort_by_indices_allocate, _sort_by_indices}
+
+sort_by_indices_allocate :: proc(data: $T/[]$E, indices: []int, allocator := context.allocator) -> (sorted: T) {
+	assert(len(data) == len(indices))
+	sorted = make(T, len(data), allocator)
+	for v, i in indices {
+		sorted[i] = data[v]
+	}
+	return
+}
+
+_sort_by_indices :: proc(data, sorted: $T/[]$E, indices: []int) {
+	assert(len(data) == len(indices))
+	assert(len(data) == len(sorted))
+	for v, i in indices {
+		sorted[i] = data[v]
+	}
+}
+
+sort_by_indices_overwrite :: proc(data: $T/[]$E, indices: []int) {
+	assert(len(data) == len(indices))
+	temp := make([]int, len(data), context.allocator)
+	defer delete(temp)
+	for v, i in indices {
+		temp[i] = data[v]
+	}
+	swap_with_slice(data, temp)
+}
+
+// sort sorts a slice and returns a slice of the original indices
+// This sort is not guaranteed to be stable
+sort_with_indices :: proc(data: $T/[]$E, allocator := context.allocator) -> (indices: []int) where ORD(E) {
+	indices = make([]int, len(data), allocator)
+	when size_of(E) != 0 {
+		if n := len(data); n > 1 {
+			for _, idx in indices {
+				indices[idx] = idx
+			}
+			_quick_sort_general_with_indices(data, indices, 0, n, _max_depth(n), struct{}{}, .Ordered)
+		}
+		return indices
+	}
+	return indices
+}
+
 // sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
 // sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
 // This sort is not guaranteed to be stable
 // This sort is not guaranteed to be stable
 sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
 sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
@@ -48,6 +94,22 @@ sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
 	}
 	}
 }
 }
 
 
+// sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
+// This sort is not guaranteed to be stable
+sort_by_with_indices :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool, allocator := context.allocator) -> (indices : []int) {
+	indices = make([]int, len(data), allocator)
+	when size_of(E) != 0 {
+		if n := len(data); n > 1 {
+			for _, idx in indices {
+				indices[idx] = idx
+			}
+			_quick_sort_general_with_indices(data, indices, 0, n, _max_depth(n), less, .Less)
+			return indices
+		}
+	}
+	return indices
+}
+
 sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
 sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
 	when size_of(E) != 0 {
 	when size_of(E) != 0 {
 		if n := len(data); n > 1 {
 		if n := len(data); n > 1 {

+ 177 - 0
core/slice/sort_private.odin

@@ -198,3 +198,180 @@ _stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (O
 		}
 		}
 	}
 	}
 }
 }
+
+_quick_sort_general_with_indices :: proc(data: $T/[]$E, indices: []int, a, b, max_depth: int, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check {
+	less :: #force_inline proc(a, b: E, call: P) -> bool {
+		when KIND == .Ordered {
+			return a < b
+		} else when KIND == .Less {
+			return call(a, b)
+		} else when KIND == .Cmp {
+			return call(a, b) == .Less
+		} else {
+			#panic("unhandled Sort_Kind")
+		}
+	}
+
+	insertion_sort :: proc(data: $T/[]$E, indices: []int, a, b: int, call: P) #no_bounds_check {
+		for i in a+1..<b {
+			for j := i; j > a && less(data[j], data[j-1], call); j -= 1 {
+				swap(data, j, j-1)
+				swap(indices, j, j-1)
+			}
+		}
+	}
+
+	heap_sort :: proc(data: $T/[]$E, indices: []int, a, b: int, call: P) #no_bounds_check {
+		sift_down :: proc(data: T, indices: []int, lo, hi, first: int, call: P) #no_bounds_check {
+			root := lo
+			for {
+				child := 2*root + 1
+				if child >= hi {
+					break
+				}
+				if child+1 < hi && less(data[first+child], data[first+child+1], call) {
+					child += 1
+				}
+				if !less(data[first+root], data[first+child], call) {
+					return
+				}
+				swap(data, first+root, first+child)
+				swap(indices, first+root, first+child)
+				root = child
+			}
+		}
+
+
+		first, lo, hi := a, 0, b-a
+
+		for i := (hi-1)/2; i >= 0; i -= 1 {
+			sift_down(data, indices, i, hi, first, call)
+		}
+
+		for i := hi-1; i >= 0; i -= 1 {
+			swap(data, first, first+i)
+			swap(indices, first, first+i)
+			sift_down(data, indices, lo, i, first, call)
+		}
+	}
+
+	median3 :: proc(data: T, indices: []int, m1, m0, m2: int, call: P) #no_bounds_check {
+		if less(data[m1], data[m0], call) {
+			swap(data, m1, m0)
+			swap(indices, m1, m0)
+		}
+		if less(data[m2], data[m1], call) {
+			swap(data, m2, m1)
+			swap(indices, m2, m1)
+			if less(data[m1], data[m0], call) {
+				swap(data, m1, m0)
+				swap(indices, m1, m0)
+			}
+		}
+	}
+
+	do_pivot :: proc(data: T, indices: []int, lo, hi: int, call: P) -> (midlo, midhi: int) #no_bounds_check {
+		m := int(uint(lo+hi)>>1)
+		if hi-lo > 40 {
+			s := (hi-lo)/8
+			median3(data, indices, lo, lo+s, lo+s*2, call)
+			median3(data, indices, m, m-s, m+s, call)
+			median3(data, indices, hi-1, hi-1-s, hi-1-s*2, call)
+		}
+		median3(data, indices, lo, m, hi-1, call)
+
+		pivot := lo
+		a, c := lo+1, hi-1
+
+
+		for ; a < c && less(data[a], data[pivot], call); a += 1 {
+		}
+		b := a
+
+		for {
+			for ; b < c && !less(data[pivot], data[b], call); b += 1 { // data[b] <= pivot
+			}
+			for ; b < c && less(data[pivot], data[c-1], call); c -=1 { // data[c-1] > pivot
+			}
+			if b >= c {
+				break
+			}
+
+			swap(data, b, c-1)
+			swap(indices, b, c-1)
+			b += 1
+			c -= 1
+		}
+
+		protect := hi-c < 5
+		if !protect && hi-c < (hi-lo)/4 {
+			dups := 0
+			if !less(data[pivot], data[hi-1], call) {
+				swap(data, c, hi-1)
+				swap(indices, c, hi-1)
+				c += 1
+				dups += 1
+			}
+			if !less(data[b-1], data[pivot], call) {
+				b -= 1
+				dups += 1
+			}
+
+			if !less(data[m], data[pivot], call) {
+				swap(data, m, b-1)
+				swap(indices, m, b-1)
+				b -= 1
+				dups += 1
+			}
+			protect = dups > 1
+		}
+		if protect {
+			for {
+				for ; a < b && !less(data[b-1], data[pivot], call); b -= 1 {
+				}
+				for ; a < b && less(data[a], data[pivot], call); a += 1 {
+				}
+				if a >= b {
+					break
+				}
+				swap(data, a, b-1)
+				swap(indices, a, b-1)
+				a += 1
+				b -= 1
+			}
+		}
+		swap(data, pivot, b-1)
+		swap(indices, pivot, b-1)
+		return b-1, c
+	}
+
+	assert(len(data) == len(indices))
+
+	a, b, max_depth := a, b, max_depth
+
+	for b-a > 12 { // only use shell sort for lengths <= 12
+		if max_depth == 0 {
+			heap_sort(data, indices, a, b, call)
+			return
+		}
+		max_depth -= 1
+		mlo, mhi := do_pivot(data, indices, a, b, call)
+		if mlo-a < b-mhi {
+			_quick_sort_general_with_indices(data, indices, a, mlo, max_depth, call, KIND)
+			a = mhi
+		} else {
+			_quick_sort_general_with_indices(data, indices, mhi, b, max_depth, call, KIND)
+			b = mlo
+		}
+	}
+	if b-a > 1 {
+		// Shell short with gap 6
+		for i in a+6..<b {
+			if less(data[i], data[i-6], call) {
+				swap(data, i, i-6)
+				swap(indices, i, i-6)
+			}
+		}
+		insertion_sort(data, indices, a, b, call)
+	}
+}

+ 31 - 18
core/strconv/strconv.odin

@@ -2,11 +2,13 @@ package strconv
 
 
 import "core:unicode/utf8"
 import "core:unicode/utf8"
 
 
-parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) {
+parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) {
 	switch s {
 	switch s {
 	case "1", "t", "T", "true", "TRUE", "True":
 	case "1", "t", "T", "true", "TRUE", "True":
+		if n != nil { n^ = len(s) }
 		return true, true
 		return true, true
 	case "0", "f", "F", "false", "FALSE", "False":
 	case "0", "f", "F", "false", "FALSE", "False":
+		if n != nil { n^ = len(s) }
 		return false, true
 		return false, true
 	}
 	}
 	return
 	return
@@ -32,10 +34,13 @@ _digit_value :: proc(r: rune) -> int {
 // n, ok := strconv.parse_i64_of_base("-1234eeee", 10);
 // n, ok := strconv.parse_i64_of_base("-1234eeee", 10);
 // assert(n == -1234 && ok);
 // assert(n == -1234 && ok);
 // ```
 // ```
-parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) {
+parse_i64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i64, ok: bool) {
 	assert(base <= 16, "base must be 1-16")
 	assert(base <= 16, "base must be 1-16")
 
 
 	s := str
 	s := str
+
+	defer if n != nil { n^ = len(str)-len(s) }
+
 	if s == "" {
 	if s == "" {
 		return
 		return
 	}
 	}
@@ -87,8 +92,9 @@ parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) {
 // n, ok = strconv.parse_i64_maybe_prefixed("0xeeee");
 // n, ok = strconv.parse_i64_maybe_prefixed("0xeeee");
 // assert(n == 0xeeee && ok);
 // assert(n == 0xeeee && ok);
 // ```
 // ```
-parse_i64_maybe_prefixed :: proc(str: string) -> (value: i64, ok: bool) {
+parse_i64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i64, ok: bool) {
 	s := str
 	s := str
+	defer if n != nil { n^ = len(str)-len(s) }
 	if s == "" {
 	if s == "" {
 		return
 		return
 	}
 	}
@@ -155,9 +161,10 @@ parse_i64 :: proc{parse_i64_maybe_prefixed, parse_i64_of_base}
 // n, ok = strconv.parse_u64_of_base("5678eeee", 16);
 // n, ok = strconv.parse_u64_of_base("5678eeee", 16);
 // assert(n == 0x5678eeee && ok);
 // assert(n == 0x5678eeee && ok);
 // ```
 // ```
-parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) {
+parse_u64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u64, ok: bool) {
 	assert(base <= 16, "base must be 1-16")
 	assert(base <= 16, "base must be 1-16")
 	s := str
 	s := str
+	defer if n != nil { n^ = len(str)-len(s) }
 	if s == "" {
 	if s == "" {
 		return
 		return
 	}
 	}
@@ -198,8 +205,9 @@ parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) {
 // n, ok = strconv.parse_u64_maybe_prefixed("0xeeee");
 // n, ok = strconv.parse_u64_maybe_prefixed("0xeeee");
 // assert(n == 0xeeee && ok);
 // assert(n == 0xeeee && ok);
 // ```
 // ```
-parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, ok: bool) {
+parse_u64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u64, ok: bool) {
 	s := str
 	s := str
+	defer if n != nil { n^ = len(str)-len(s) }
 	if s == "" {
 	if s == "" {
 		return
 		return
 	}
 	}
@@ -259,11 +267,11 @@ parse_u64 :: proc{parse_u64_maybe_prefixed, parse_u64_of_base}
 // n, ok = strconv.parse_int("0xffff"); // with prefix and inferred base
 // n, ok = strconv.parse_int("0xffff"); // with prefix and inferred base
 // assert(n == 0xffff && ok);
 // assert(n == 0xffff && ok);
 // ```
 // ```
-parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) {
+parse_int :: proc(s: string, base := 0, n: ^int = nil) -> (value: int, ok: bool) {
 	v: i64 = ---
 	v: i64 = ---
 	switch base {
 	switch base {
-	case 0:  v, ok = parse_i64_maybe_prefixed(s)
-	case:    v, ok = parse_i64_of_base(s, base)
+	case 0:  v, ok = parse_i64_maybe_prefixed(s, n)
+	case:    v, ok = parse_i64_of_base(s, base, n)
 	}
 	}
 	value = int(v)
 	value = int(v)
 	return
 	return
@@ -289,11 +297,11 @@ parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) {
 // n, ok = strconv.parse_uint("0xffff"); // with prefix and inferred base
 // n, ok = strconv.parse_uint("0xffff"); // with prefix and inferred base
 // assert(n == 0xffff && ok);
 // assert(n == 0xffff && ok);
 // ```
 // ```
-parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) {
+parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: bool) {
 	v: u64 = ---
 	v: u64 = ---
 	switch base {
 	switch base {
-	case 0:  v, ok = parse_u64_maybe_prefixed(s)
-	case:    v, ok = parse_u64_of_base(s, base)
+	case 0:  v, ok = parse_u64_maybe_prefixed(s, n)
+	case:    v, ok = parse_u64_of_base(s, base, n)
 	}
 	}
 	value = uint(v)
 	value = uint(v)
 	return
 	return
@@ -309,10 +317,11 @@ parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) {
 // n, ok := strconv.parse_i128_of_base("-1234eeee", 10);
 // n, ok := strconv.parse_i128_of_base("-1234eeee", 10);
 // assert(n == -1234 && ok);
 // assert(n == -1234 && ok);
 // ```
 // ```
-parse_i128_of_base :: proc(str: string, base: int) -> (value: i128, ok: bool) {
+parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i128, ok: bool) {
 	assert(base <= 16, "base must be 1-16")
 	assert(base <= 16, "base must be 1-16")
 
 
 	s := str
 	s := str
+	defer if n != nil { n^ = len(str)-len(s) }
 	if s == "" {
 	if s == "" {
 		return
 		return
 	}
 	}
@@ -364,8 +373,9 @@ parse_i128_of_base :: proc(str: string, base: int) -> (value: i128, ok: bool) {
 // n, ok = strconv.parse_i128_maybe_prefixed("0xeeee");
 // n, ok = strconv.parse_i128_maybe_prefixed("0xeeee");
 // assert(n == 0xeeee && ok);
 // assert(n == 0xeeee && ok);
 // ```
 // ```
-parse_i128_maybe_prefixed :: proc(str: string) -> (value: i128, ok: bool) {
+parse_i128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i128, ok: bool) {
 	s := str
 	s := str
+	defer if n != nil { n^ = len(str)-len(s) }
 	if s == "" {
 	if s == "" {
 		return
 		return
 	}
 	}
@@ -432,9 +442,10 @@ parse_i128 :: proc{parse_i128_maybe_prefixed, parse_i128_of_base}
 // n, ok = strconv.parse_u128_of_base("5678eeee", 16);
 // n, ok = strconv.parse_u128_of_base("5678eeee", 16);
 // assert(n == 0x5678eeee && ok);
 // assert(n == 0x5678eeee && ok);
 // ```
 // ```
-parse_u128_of_base :: proc(str: string, base: int) -> (value: u128, ok: bool) {
+parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u128, ok: bool) {
 	assert(base <= 16, "base must be 1-16")
 	assert(base <= 16, "base must be 1-16")
 	s := str
 	s := str
+	defer if n != nil { n^ = len(str)-len(s) }
 	if s == "" {
 	if s == "" {
 		return
 		return
 	}
 	}
@@ -475,8 +486,9 @@ parse_u128_of_base :: proc(str: string, base: int) -> (value: u128, ok: bool) {
 // n, ok = strconv.parse_u128_maybe_prefixed("0xeeee");
 // n, ok = strconv.parse_u128_maybe_prefixed("0xeeee");
 // assert(n == 0xeeee && ok);
 // assert(n == 0xeeee && ok);
 // ```
 // ```
-parse_u128_maybe_prefixed :: proc(str: string) -> (value: u128, ok: bool) {
+parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, ok: bool) {
 	s := str
 	s := str
+	defer if n != nil { n^ = len(str)-len(s) }
 	if s == "" {
 	if s == "" {
 		return
 		return
 	}
 	}
@@ -535,9 +547,9 @@ parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
 // n, ok = strconv.parse_f32("12.34");
 // n, ok = strconv.parse_f32("12.34");
 // assert(n == 12.34 && ok);
 // assert(n == 12.34 && ok);
 // ```
 // ```
-parse_f32 :: proc(s: string) -> (value: f32, ok: bool) {
+parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
 	v: f64 = ---
 	v: f64 = ---
-	v, ok = parse_f64(s)
+	v, ok = parse_f64(s, n)
 	return f32(v), ok
 	return f32(v), ok
 }
 }
 
 
@@ -553,8 +565,9 @@ parse_f32 :: proc(s: string) -> (value: f32, ok: bool) {
 // n, ok = strconv.parse_f32("12.34");
 // n, ok = strconv.parse_f32("12.34");
 // assert(n == 12.34 && ok);
 // assert(n == 12.34 && ok);
 // ```
 // ```
-parse_f64 :: proc(str: string) -> (value: f64, ok: bool) {
+parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
 	s := str
 	s := str
+	defer if n != nil { n^ = len(str)-len(s) }
 	if s == "" {
 	if s == "" {
 		return
 		return
 	}
 	}

+ 5 - 5
core/strings/strings.odin

@@ -1686,7 +1686,7 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
 		return clone(str, allocator)
 		return clone(str, allocator)
 	}
 	}
 
 
-	remains := length-1
+	remains := length-n
 	pad_len := rune_count(pad)
 	pad_len := rune_count(pad)
 
 
 	b: Builder
 	b: Builder
@@ -1702,14 +1702,14 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
 	return to_string(b)
 	return to_string(b)
 }
 }
 
 
-// left_justify returns a string with a pad string at left side if the str's rune length is smaller than length
+// left_justify returns a string with a pad string at right side if the str's rune length is smaller than length
 left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
 left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
 	n := rune_count(str)
 	n := rune_count(str)
 	if n >= length || pad == "" {
 	if n >= length || pad == "" {
 		return clone(str, allocator)
 		return clone(str, allocator)
 	}
 	}
 
 
-	remains := length-1
+	remains := length-n
 	pad_len := rune_count(pad)
 	pad_len := rune_count(pad)
 
 
 	b: Builder
 	b: Builder
@@ -1724,14 +1724,14 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
 	return to_string(b)
 	return to_string(b)
 }
 }
 
 
-// right_justify returns a string with a pad string at right side if the str's rune length is smaller than length
+// right_justify returns a string with a pad string at left side if the str's rune length is smaller than length
 right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
 right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
 	n := rune_count(str)
 	n := rune_count(str)
 	if n >= length || pad == "" {
 	if n >= length || pad == "" {
 		return clone(str, allocator)
 		return clone(str, allocator)
 	}
 	}
 
 
-	remains := length-1
+	remains := length-n
 	pad_len := rune_count(pad)
 	pad_len := rune_count(pad)
 
 
 	b: Builder
 	b: Builder

+ 4 - 3
core/sync/futex_darwin.odin

@@ -8,8 +8,9 @@ import "core:time"
 foreign import System "System.framework"
 foreign import System "System.framework"
 
 
 foreign System {
 foreign System {
+	// __ulock_wait is not available on 10.15
+	// See https://github.com/odin-lang/Odin/issues/1959
 	__ulock_wait  :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_us: u32) -> c.int ---
 	__ulock_wait  :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_us: u32) -> c.int ---
-	__ulock_wait2 :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ns: u64, value2: u64) -> c.int ---
 	__ulock_wake  :: proc "c" (operation: u32, addr: rawptr, wake_value: u64) -> c.int ---
 	__ulock_wake  :: proc "c" (operation: u32, addr: rawptr, wake_value: u64) -> c.int ---
 }
 }
 
 
@@ -28,9 +29,9 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
 }
 }
 
 
 _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
 _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
-	timeout_ns := u64(duration)
+	timeout_ns := u32(duration) * 1000
 	
 	
-	s := __ulock_wait2(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns, 0)
+	s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns)
 	if s >= 0 {
 	if s >= 0 {
 		return true
 		return true
 	}
 	}

+ 26 - 0
core/sys/info/cpu_arm.odin

@@ -0,0 +1,26 @@
+//+build arm32, arm64
+package sysinfo
+
+// TODO: Set up an enum with the ARM equivalent of the above.
+CPU_Feature :: enum u64 {}
+
+cpu_features: Maybe(CPU_Feature)
+cpu_name:     Maybe(string)
+
+@(init, private)
+init_cpu_features :: proc "c" () {
+}
+
+@(private)
+_cpu_name_buf: [72]u8
+
+@(init, private)
+init_cpu_name :: proc "c" () {
+	when ODIN_ARCH == .arm32 {
+		copy(_cpu_name_buf[:], "ARM")
+		cpu_name = string(_cpu_name_buf[:3])
+	} else {
+		copy(_cpu_name_buf[:], "ARM64")
+		cpu_name = string(_cpu_name_buf[:5])
+	}
+}

+ 37 - 2
core/simd/x86/cpu.odin → core/sys/info/cpu_intel.odin

@@ -1,5 +1,5 @@
 //+build i386, amd64
 //+build i386, amd64
-package simd_x86
+package sysinfo
 
 
 import "core:intrinsics"
 import "core:intrinsics"
 
 
@@ -9,7 +9,6 @@ cpuid :: intrinsics.x86_cpuid
 // xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
 // xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
 xgetbv :: intrinsics.x86_xgetbv
 xgetbv :: intrinsics.x86_xgetbv
 
 
-
 CPU_Feature :: enum u64 {
 CPU_Feature :: enum u64 {
 	aes,       // AES hardware implementation (AES NI)
 	aes,       // AES hardware implementation (AES NI)
 	adx,       // Multi-precision add-carry instruction extensions
 	adx,       // Multi-precision add-carry instruction extensions
@@ -34,6 +33,7 @@ CPU_Feature :: enum u64 {
 CPU_Features :: distinct bit_set[CPU_Feature; u64]
 CPU_Features :: distinct bit_set[CPU_Feature; u64]
 
 
 cpu_features: Maybe(CPU_Features)
 cpu_features: Maybe(CPU_Features)
+cpu_name:     Maybe(string)
 
 
 @(init, private)
 @(init, private)
 init_cpu_features :: proc "c" () {
 init_cpu_features :: proc "c" () {
@@ -67,6 +67,13 @@ init_cpu_features :: proc "c" () {
 	try_set(&set, .os_xsave,  27, ecx1)
 	try_set(&set, .os_xsave,  27, ecx1)
 	try_set(&set, .rdrand,    30, ecx1)
 	try_set(&set, .rdrand,    30, ecx1)
 
 
+	when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
+		// xgetbv is an illegal instruction under FreeBSD 13 & OpenBSD 7.1
+		// return before probing further
+		cpu_features = set
+		return
+	}
+
 	os_supports_avx := false
 	os_supports_avx := false
 	if .os_xsave in set {
 	if .os_xsave in set {
 		eax, _ := xgetbv(0)
 		eax, _ := xgetbv(0)
@@ -92,3 +99,31 @@ init_cpu_features :: proc "c" () {
 
 
 	cpu_features = set
 	cpu_features = set
 }
 }
+
+@(private)
+_cpu_name_buf: [72]u8
+
+@(init, private)
+init_cpu_name :: proc "c" () {
+	number_of_extended_ids, _, _, _ := cpuid(0x8000_0000, 0)
+	if number_of_extended_ids < 0x8000_0004 {
+		return
+	}
+
+	_buf := transmute(^[0x12]u32)&_cpu_name_buf
+	_buf[ 0], _buf[ 1], _buf[ 2], _buf[ 3] = cpuid(0x8000_0002, 0)
+	_buf[ 4], _buf[ 5], _buf[ 6], _buf[ 7] = cpuid(0x8000_0003, 0)
+	_buf[ 8], _buf[ 9], _buf[10], _buf[11] = cpuid(0x8000_0004, 0)
+
+	// Some CPUs like may include leading or trailing spaces. Trim them.
+	// e.g. `      Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz`
+
+	brand := string(_cpu_name_buf[:])
+	for len(brand) > 0 && brand[0] == 0 || brand[0] == ' ' {
+		brand = brand[1:]
+	}
+	for len(brand) > 0 && brand[len(brand) - 1] == 0 || brand[len(brand) - 1] == ' ' {
+		brand = brand[:len(brand) - 1]
+	}
+	cpu_name = brand
+}

+ 78 - 0
core/sys/info/doc.odin

@@ -0,0 +1,78 @@
+/*
+	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Made available under Odin's BSD-3 license.
+
+	Package `core:sys/info` gathers system information on:
+	Windows, Linux, macOS, FreeBSD & OpenBSD.
+
+	Simply import the package and you'll have access to the OS version, RAM amount
+	and CPU information.
+
+	On Windows, GPUs will also be enumerated using the registry.
+
+	CPU feature flags can be tested against `cpu_features`, where applicable, e.g.
+	`if .aes in si.aes { ... }`
+*/
+// +ignore
+package sysinfo
+
+import "core:fmt"
+import si "core:sys/info"
+
+main :: proc() {
+	fmt.printf("Odin:  %v\n",     ODIN_VERSION)
+	fmt.printf("OS:    %v\n",     si.os_version.as_string)
+	fmt.printf("OS:    %#v\n",    si.os_version)
+	fmt.printf("CPU:   %v\n",     si.cpu_name)
+	fmt.printf("RAM:   %v MiB\n", si.ram.total_ram / 1024 / 1024)
+
+	fmt.println()
+	for gpu, i in si.gpus {
+		fmt.printf("GPU #%v:\n", i)
+		fmt.printf("\tVendor: %v\n",     gpu.vendor_name)
+		fmt.printf("\tModel:  %v\n",     gpu.model_name)
+		fmt.printf("\tVRAM:   %v MiB\n", gpu.total_ram / 1024 / 1024)
+	}
+}
+
+/*
+	Example Windows output:
+		Odin:  dev-2022-09
+		OS:    Windows 10 Professional (version: 20H2), build: 19042.1466
+		OS:    OS_Version{
+			platform = "Windows",
+			major = 10,
+			minor = 0,
+			patch = 0,
+			build = [
+				19042,
+				1466,
+			],
+			version = "20H2",
+			as_string = "Windows 10 Professional (version: 20H2), build: 19042.1466",
+		}
+		CPU:   AMD Ryzen 7 1800X Eight-Core Processor
+		RAM:   65469 MiB
+
+		GPU #0:
+			Vendor: Advanced Micro Devices, Inc.
+			Model:  Radeon RX Vega
+			VRAM:   8176 MiB
+
+	Example macOS output:
+		ODIN: dev-2022-09
+		OS:   OS_Version{
+		        platform = "MacOS",
+		        major = 21,
+		        minor = 5,
+		        patch = 0,
+		        build = [
+		                0,
+		                0,
+		        ],
+		        version = "21F79",
+		        as_string = "macOS Monterey 12.4 (build 21F79, kernel 21.5.0)",
+		}
+		CPU:  Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
+		RAM:  8192 MiB
+*/

+ 510 - 0
core/sys/info/platform_darwin.odin

@@ -0,0 +1,510 @@
+// +build darwin
+package sysinfo
+
+import sys "core:sys/unix"
+import "core:strconv"
+import "core:strings"
+
+@(private)
+version_string_buf: [1024]u8
+
+@(init, private)
+init_os_version :: proc () {
+	os_version.platform = .MacOS
+
+	// Start building display version
+	b := strings.builder_from_bytes(version_string_buf[:])
+
+	mib := []i32{sys.CTL_KERN, sys.KERN_OSVERSION}
+	build_buf: [12]u8
+
+	ok := sys.sysctl(mib, &build_buf)
+	if !ok {
+		strings.write_string(&b, "macOS Unknown")
+		os_version.as_string = strings.to_string(b)
+		return
+	}
+
+	build := string(cstring(&build_buf[0]))
+
+	// Do we have an exact match?
+	match: Darwin_Match
+	rel, exact := macos_release_map[build]
+
+	if exact {
+		match = .Exact
+	} else {
+		// Match on XNU kernel version
+		mib = []i32{sys.CTL_KERN, sys.KERN_OSRELEASE}
+		version_bits: [12]u8 // enough for 999.999.999\x00
+		have_kernel_version := sys.sysctl(mib, &version_bits)
+
+		major_ok, minor_ok, patch_ok: bool
+
+		triplet := strings.split(string(cstring(&version_bits[0])), ".", context.temp_allocator)
+		if len(triplet) != 3 {
+			have_kernel_version = false
+		} else {
+			rel.darwin.x, major_ok = strconv.parse_int(triplet[0])
+			rel.darwin.y, minor_ok = strconv.parse_int(triplet[1])
+			rel.darwin.z, patch_ok = strconv.parse_int(triplet[2])
+
+			if !(major_ok && minor_ok && patch_ok) {
+				have_kernel_version = false
+			}
+		}
+
+		if !have_kernel_version {
+			// We don't know the kernel version, but we do know the build
+			strings.write_string(&b, "macOS Unknown (build ")
+			l := strings.builder_len(b)
+			strings.write_string(&b, build)
+			os_version.version = strings.to_string(b)[l:]
+			strings.write_rune(&b, ')')
+			os_version.as_string = strings.to_string(b)
+			return
+		}
+		rel, match = map_darwin_kernel_version_to_macos_release(build, rel.darwin)
+	}
+
+	os_version.major = rel.darwin.x
+	os_version.minor = rel.darwin.y
+	os_version.patch = rel.darwin.z
+
+	strings.write_string(&b, rel.os_name)
+	if match == .Exact || match == .Nearest {
+		strings.write_rune(&b, ' ')
+		strings.write_string(&b, rel.release.name)
+		strings.write_rune(&b, ' ')
+		strings.write_int(&b, rel.release.version.x)
+		if rel.release.version.y > 0 || rel.release.version.z > 0 {
+			strings.write_rune(&b, '.')
+			strings.write_int(&b, rel.release.version.y)
+		}
+		if rel.release.version.z > 0 {
+			strings.write_rune(&b, '.')
+			strings.write_int(&b, rel.release.version.z)
+		}
+		if match == .Nearest {
+			strings.write_rune(&b, '?')
+		}
+	} else {
+		strings.write_string(&b, " Unknown")
+	}
+
+	strings.write_string(&b, " (build ")
+	l := strings.builder_len(b)
+	strings.write_string(&b, build)
+	os_version.version = strings.to_string(b)[l:]
+
+	strings.write_string(&b, ", kernel ")
+	strings.write_int(&b, rel.darwin.x)
+	strings.write_rune(&b, '.')
+	strings.write_int(&b, rel.darwin.y)
+	strings.write_rune(&b, '.')
+	strings.write_int(&b, rel.darwin.z)
+	strings.write_rune(&b, ')')
+
+	os_version.as_string = strings.to_string(b)
+}
+
+@(init)
+init_ram :: proc() {
+	// Retrieve RAM info using `sysctl`
+
+	mib := []i32{sys.CTL_HW, sys.HW_MEMSIZE}
+	mem_size: u64
+	if sys.sysctl(mib, &mem_size) {
+		ram.total_ram = int(mem_size)
+	}
+}
+
+@(private)
+Darwin_To_Release :: struct {
+	darwin:      [3]int, // Darwin kernel triplet
+	os_name:     string, // OS X, MacOS
+	release:     struct {
+		name:    string, // Monterey, Mojave, etc.
+		version: [3]int, // 12.4, etc.
+	},
+}
+
+// Important: Order from lowest to highest kernel version
+@(private)
+macos_release_map: map[string]Darwin_To_Release = {
+	// MacOS Tiger
+	"8A428"   = {{8, 0, 0},   "macOS", {"Tiger",         {10,  4, 0}}},
+	"8A432"   = {{8, 0, 0},   "macOS", {"Tiger",         {10,  4, 0}}},
+	"8B15"    = {{8, 1, 0},   "macOS", {"Tiger",         {10,  4, 1}}},
+	"8B17"    = {{8, 1, 0},   "macOS", {"Tiger",         {10,  4, 1}}},
+	"8C46"    = {{8, 2, 0},   "macOS", {"Tiger",         {10,  4, 2}}},
+	"8C47"    = {{8, 2, 0},   "macOS", {"Tiger",         {10,  4, 2}}},
+	"8E102"   = {{8, 2, 0},   "macOS", {"Tiger",         {10,  4, 2}}},
+	"8E45"    = {{8, 2, 0},   "macOS", {"Tiger",         {10,  4, 2}}},
+	"8E90"    = {{8, 2, 0},   "macOS", {"Tiger",         {10,  4, 2}}},
+	"8F46"    = {{8, 3, 0},   "macOS", {"Tiger",         {10,  4, 3}}},
+	"8G32"    = {{8, 4, 0},   "macOS", {"Tiger",         {10,  4, 4}}},
+	"8G1165"  = {{8, 4, 0},   "macOS", {"Tiger",         {10,  4, 4}}},
+	"8H14"    = {{8, 5, 0},   "macOS", {"Tiger",         {10,  4, 5}}},
+	"8G1454"  = {{8, 5, 0},   "macOS", {"Tiger",         {10,  4, 5}}},
+	"8I127"   = {{8, 6, 0},   "macOS", {"Tiger",         {10,  4, 6}}},
+	"8I1119"  = {{8, 6, 0},   "macOS", {"Tiger",         {10,  4, 6}}},
+	"8J135"   = {{8, 7, 0},   "macOS", {"Tiger",         {10,  4, 7}}},
+	"8J2135a" = {{8, 7, 0},   "macOS", {"Tiger",         {10,  4, 7}}},
+	"8K1079"  = {{8, 7, 0},   "macOS", {"Tiger",         {10,  4, 7}}},
+	"8N5107"  = {{8, 7, 0},   "macOS", {"Tiger",         {10,  4, 7}}},
+	"8L127"   = {{8, 8, 0},   "macOS", {"Tiger",         {10,  4, 8}}},
+	"8L2127"  = {{8, 8, 0},   "macOS", {"Tiger",         {10,  4, 8}}},
+	"8P135"   = {{8, 9, 0},   "macOS", {"Tiger",         {10,  4, 9}}},
+	"8P2137"  = {{8, 9, 0},   "macOS", {"Tiger",         {10,  4, 9}}},
+	"8R218"   = {{8, 10, 0},  "macOS", {"Tiger",         {10,  4, 10}}},
+	"8R2218"  = {{8, 10, 0},  "macOS", {"Tiger",         {10,  4, 10}}},
+	"8R2232"  = {{8, 10, 0},  "macOS", {"Tiger",         {10,  4, 10}}},
+	"8S165"   = {{8, 11, 0},  "macOS", {"Tiger",         {10,  4, 11}}},
+	"8S2167"  = {{8, 11, 0},  "macOS", {"Tiger",         {10,  4, 11}}},
+
+	// MacOS Leopard
+	"9A581"   = {{9, 0, 0},   "macOS", {"Leopard",       {10,  5, 0}}},
+	"9B18"    = {{9, 1, 0},   "macOS", {"Leopard",       {10,  5, 1}}},
+	"9B2117"  = {{9, 1, 1},   "macOS", {"Leopard",       {10,  5, 1}}},
+	"9C31"    = {{9, 2, 0},   "macOS", {"Leopard",       {10,  5, 2}}},
+	"9C7010"  = {{9, 2, 0},   "macOS", {"Leopard",       {10,  5, 2}}},
+	"9D34"    = {{9, 3, 0},   "macOS", {"Leopard",       {10,  5, 3}}},
+	"9E17"    = {{9, 4, 0},   "macOS", {"Leopard",       {10,  5, 4}}},
+	"9F33"    = {{9, 5, 0},   "macOS", {"Leopard",       {10,  5, 5}}},
+	"9G55"    = {{9, 6, 0},   "macOS", {"Leopard",       {10,  5, 6}}},
+	"9G66"    = {{9, 6, 0},   "macOS", {"Leopard",       {10,  5, 6}}},
+	"9G71"    = {{9, 6, 0},   "macOS", {"Leopard",       {10,  5, 6}}},
+	"9J61"    = {{9, 7, 0},   "macOS", {"Leopard",       {10,  5, 7}}},
+	"9L30"    = {{9, 8, 0},   "macOS", {"Leopard",       {10,  5, 8}}},
+	"9L34"    = {{9, 8, 0},   "macOS", {"Leopard",       {10,  5, 8}}},
+
+	// MacOS Snow Leopard
+	"10A432"  = {{10, 0, 0},  "macOS", {"Snow Leopard",  {10,  6, 0}}},
+	"10A433"  = {{10, 0, 0},  "macOS", {"Snow Leopard",  {10,  6, 0}}},
+	"10B504"  = {{10, 1, 0},  "macOS", {"Snow Leopard",  {10,  6, 1}}},
+	"10C540"  = {{10, 2, 0},  "macOS", {"Snow Leopard",  {10,  6, 2}}},
+	"10D573"  = {{10, 3, 0},  "macOS", {"Snow Leopard",  {10,  6, 3}}},
+	"10D575"  = {{10, 3, 0},  "macOS", {"Snow Leopard",  {10,  6, 3}}},
+	"10D578"  = {{10, 3, 0},  "macOS", {"Snow Leopard",  {10,  6, 3}}},
+	"10F569"  = {{10, 4, 0},  "macOS", {"Snow Leopard",  {10,  6, 4}}},
+	"10H574"  = {{10, 5, 0},  "macOS", {"Snow Leopard",  {10,  6, 5}}},
+	"10J567"  = {{10, 6, 0},  "macOS", {"Snow Leopard",  {10,  6, 6}}},
+	"10J869"  = {{10, 7, 0},  "macOS", {"Snow Leopard",  {10,  6, 7}}},
+	"10J3250" = {{10, 7, 0},  "macOS", {"Snow Leopard",  {10,  6, 7}}},
+	"10J4138" = {{10, 7, 0},  "macOS", {"Snow Leopard",  {10,  6, 7}}},
+	"10K540"  = {{10, 8, 0},  "macOS", {"Snow Leopard",  {10,  6, 8}}},
+	"10K549"  = {{10, 8, 0},  "macOS", {"Snow Leopard",  {10,  6, 8}}},
+
+	// MacOS Lion
+	"11A511"  = {{11, 0, 0},  "macOS", {"Lion",          {10,  7, 0}}},
+	"11A511s" = {{11, 0, 0},  "macOS", {"Lion",          {10,  7, 0}}},
+	"11A2061" = {{11, 0, 2},  "macOS", {"Lion",          {10,  7, 0}}},
+	"11A2063" = {{11, 0, 2},  "macOS", {"Lion",          {10,  7, 0}}},
+	"11B26"   = {{11, 1, 0},  "macOS", {"Lion",          {10,  7, 1}}},
+	"11B2118" = {{11, 1, 0},  "macOS", {"Lion",          {10,  7, 1}}},
+	"11C74"   = {{11, 2, 0},  "macOS", {"Lion",          {10,  7, 2}}},
+	"11D50"   = {{11, 3, 0},  "macOS", {"Lion",          {10,  7, 3}}},
+	"11E53"   = {{11, 4, 0},  "macOS", {"Lion",          {10,  7, 4}}},
+	"11G56"   = {{11, 4, 2},  "macOS", {"Lion",          {10,  7, 5}}},
+	"11G63"   = {{11, 4, 2},  "macOS", {"Lion",          {10,  7, 5}}},
+
+	// MacOS Mountain Lion
+	"12A269"  = {{12, 0, 0},  "macOS", {"Mountain Lion", {10,  8, 0}}},
+	"12B19"   = {{12, 1, 0},  "macOS", {"Mountain Lion", {10,  8, 1}}},
+	"12C54"   = {{12, 2, 0},  "macOS", {"Mountain Lion", {10,  8, 2}}},
+	"12C60"   = {{12, 2, 0},  "macOS", {"Mountain Lion", {10,  8, 2}}},
+	"12C2034" = {{12, 2, 0},  "macOS", {"Mountain Lion", {10,  8, 2}}},
+	"12C3104" = {{12, 2, 0},  "macOS", {"Mountain Lion", {10,  8, 2}}},
+	"12D78"   = {{12, 3, 0},  "macOS", {"Mountain Lion", {10,  8, 3}}},
+	"12E55"   = {{12, 4, 0},  "macOS", {"Mountain Lion", {10,  8, 4}}},
+	"12E3067" = {{12, 4, 0},  "macOS", {"Mountain Lion", {10,  8, 4}}},
+	"12E4022" = {{12, 4, 0},  "macOS", {"Mountain Lion", {10,  8, 4}}},
+	"12F37"   = {{12, 5, 0},  "macOS", {"Mountain Lion", {10,  8, 5}}},
+	"12F45"   = {{12, 5, 0},  "macOS", {"Mountain Lion", {10,  8, 5}}},
+	"12F2501" = {{12, 5, 0},  "macOS", {"Mountain Lion", {10,  8, 5}}},
+	"12F2518" = {{12, 5, 0},  "macOS", {"Mountain Lion", {10,  8, 5}}},
+	"12F2542" = {{12, 5, 0},  "macOS", {"Mountain Lion", {10,  8, 5}}},
+	"12F2560" = {{12, 5, 0},  "macOS", {"Mountain Lion", {10,  8, 5}}},
+
+	// MacOS Mavericks
+	"13A603"  = {{13, 0, 0},  "macOS", {"Mavericks",     {10,  9, 0}}},
+	"13B42"   = {{13, 0, 0},  "macOS", {"Mavericks",     {10,  9, 1}}},
+	"13C64"   = {{13, 1, 0},  "macOS", {"Mavericks",     {10,  9, 2}}},
+	"13C1021" = {{13, 1, 0},  "macOS", {"Mavericks",     {10,  9, 2}}},
+	"13D65"   = {{13, 2, 0},  "macOS", {"Mavericks",     {10,  9, 3}}},
+	"13E28"   = {{13, 3, 0},  "macOS", {"Mavericks",     {10,  9, 4}}},
+	"13F34"   = {{13, 4, 0},  "macOS", {"Mavericks",     {10,  9, 5}}},
+	"13F1066" = {{13, 4, 0},  "macOS", {"Mavericks",     {10,  9, 5}}},
+	"13F1077" = {{13, 4, 0},  "macOS", {"Mavericks",     {10,  9, 5}}},
+	"13F1096" = {{13, 4, 0},  "macOS", {"Mavericks",     {10,  9, 5}}},
+	"13F1112" = {{13, 4, 0},  "macOS", {"Mavericks",     {10,  9, 5}}},
+	"13F1134" = {{13, 4, 0},  "macOS", {"Mavericks",     {10,  9, 5}}},
+	"13F1507" = {{13, 4, 0},  "macOS", {"Mavericks",     {10,  9, 5}}},
+	"13F1603" = {{13, 4, 0},  "macOS", {"Mavericks",     {10,  9, 5}}},
+	"13F1712" = {{13, 4, 0},  "macOS", {"Mavericks",     {10,  9, 5}}},
+	"13F1808" = {{13, 4, 0},  "macOS", {"Mavericks",     {10,  9, 5}}},
+	"13F1911" = {{13, 4, 0},  "macOS", {"Mavericks",     {10,  9, 5}}},
+
+	// MacOS Yosemite
+	"14A389"  = {{14, 0, 0},  "macOS", {"Yosemite",      {10, 10, 0}}},
+	"14B25"   = {{14, 0, 0},  "macOS", {"Yosemite",      {10, 10, 1}}},
+	"14C109"  = {{14, 1, 0},  "macOS", {"Yosemite",      {10, 10, 2}}},
+	"14C1510" = {{14, 1, 0},  "macOS", {"Yosemite",      {10, 10, 2}}},
+	"14C2043" = {{14, 1, 0},  "macOS", {"Yosemite",      {10, 10, 2}}},
+	"14C1514" = {{14, 1, 0},  "macOS", {"Yosemite",      {10, 10, 2}}},
+	"14C2513" = {{14, 1, 0},  "macOS", {"Yosemite",      {10, 10, 2}}},
+	"14D131"  = {{14, 3, 0},  "macOS", {"Yosemite",      {10, 10, 3}}},
+	"14D136"  = {{14, 3, 0},  "macOS", {"Yosemite",      {10, 10, 3}}},
+	"14E46"   = {{14, 4, 0},  "macOS", {"Yosemite",      {10, 10, 4}}},
+	"14F27"   = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F1021" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F1505" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F1509" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F1605" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F1713" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F1808" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F1909" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F1912" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F2009" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F2109" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F2315" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F2411" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+	"14F2511" = {{14, 5, 0},  "macOS", {"Yosemite",      {10, 10, 5}}},
+
+	// MacOS El Capitan
+	"15A284"   = {{15, 0, 0}, "macOS", {"El Capitan",    {10, 11, 0}}},
+	"15B42"    = {{15, 0, 0}, "macOS", {"El Capitan",    {10, 11, 1}}},
+	"15C50"    = {{15, 2, 0}, "macOS", {"El Capitan",    {10, 11, 2}}},
+	"15D21"    = {{15, 3, 0}, "macOS", {"El Capitan",    {10, 11, 3}}},
+	"15E65"    = {{15, 4, 0}, "macOS", {"El Capitan",    {10, 11, 4}}},
+	"15F34"    = {{15, 5, 0}, "macOS", {"El Capitan",    {10, 11, 5}}},
+	"15G31"    = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G1004"  = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G1011"  = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G1108"  = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G1212"  = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G1217"  = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G1421"  = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G1510"  = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G1611"  = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G17023" = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G18013" = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G19009" = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G20015" = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G21013" = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+	"15G22010" = {{15, 6, 0}, "macOS", {"El Capitan",    {10, 11, 6}}},
+
+	// MacOS Sierra
+	"16A323"   = {{16, 0, 0}, "macOS", {"Sierra",        {10, 12, 0}}},
+	"16B2555"  = {{16, 1, 0}, "macOS", {"Sierra",        {10, 12, 1}}},
+	"16B2657"  = {{16, 1, 0}, "macOS", {"Sierra",        {10, 12, 1}}},
+	"16C67"    = {{16, 3, 0}, "macOS", {"Sierra",        {10, 12, 2}}},
+	"16C68"    = {{16, 3, 0}, "macOS", {"Sierra",        {10, 12, 2}}},
+	"16D32"    = {{16, 4, 0}, "macOS", {"Sierra",        {10, 12, 3}}},
+	"16E195"   = {{16, 5, 0}, "macOS", {"Sierra",        {10, 12, 4}}},
+	"16F73"    = {{16, 6, 0}, "macOS", {"Sierra",        {10, 12, 5}}},
+	"16F2073"  = {{16, 6, 0}, "macOS", {"Sierra",        {10, 12, 5}}},
+	"16G29"    = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G1036"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G1114"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G1212"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G1314"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G1408"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G1510"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G1618"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G1710"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G1815"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G1917"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G1918"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G2016"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G2127"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G2128"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+	"16G2136"  = {{16, 7, 0}, "macOS", {"Sierra",        {10, 12, 6}}},
+
+	// MacOS High Sierra
+	"17A365"   = {{17, 0, 0}, "macOS", {"High Sierra",   {10, 13, 0}}},
+	"17A405"   = {{17, 0, 0}, "macOS", {"High Sierra",   {10, 13, 0}}},
+	"17B48"    = {{17, 2, 0}, "macOS", {"High Sierra",   {10, 13, 1}}},
+	"17B1002"  = {{17, 2, 0}, "macOS", {"High Sierra",   {10, 13, 1}}},
+	"17B1003"  = {{17, 2, 0}, "macOS", {"High Sierra",   {10, 13, 1}}},
+	"17C88"    = {{17, 3, 0}, "macOS", {"High Sierra",   {10, 13, 2}}},
+	"17C89"    = {{17, 3, 0}, "macOS", {"High Sierra",   {10, 13, 2}}},
+	"17C205"   = {{17, 3, 0}, "macOS", {"High Sierra",   {10, 13, 2}}},
+	"17C2205"  = {{17, 3, 0}, "macOS", {"High Sierra",   {10, 13, 2}}},
+	"17D47"    = {{17, 4, 0}, "macOS", {"High Sierra",   {10, 13, 3}}},
+	"17D2047"  = {{17, 4, 0}, "macOS", {"High Sierra",   {10, 13, 3}}},
+	"17D102"   = {{17, 4, 0}, "macOS", {"High Sierra",   {10, 13, 3}}},
+	"17D2102"  = {{17, 4, 0}, "macOS", {"High Sierra",   {10, 13, 3}}},
+	"17E199"   = {{17, 5, 0}, "macOS", {"High Sierra",   {10, 13, 4}}},
+	"17E202"   = {{17, 5, 0}, "macOS", {"High Sierra",   {10, 13, 4}}},
+	"17F77"    = {{17, 6, 0}, "macOS", {"High Sierra",   {10, 13, 5}}},
+	"17G65"    = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G2208"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G2307"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G3025"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G4015"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G5019"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G6029"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G6030"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G7024"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G8029"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G8030"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G8037"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G9016"  = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G10021" = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G11023" = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G12034" = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G13033" = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G13035" = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G14019" = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G14033" = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+	"17G14042" = {{17, 7, 0}, "macOS", {"High Sierra",   {10, 13, 6}}},
+
+	// MacOS Mojave
+	"18A391"   = {{18, 0, 0}, "macOS", {"Mojave",        {10, 14, 0}}},
+	"18B75"    = {{18, 2, 0}, "macOS", {"Mojave",        {10, 14, 1}}},
+	"18B2107"  = {{18, 2, 0}, "macOS", {"Mojave",        {10, 14, 1}}},
+	"18B3094"  = {{18, 2, 0}, "macOS", {"Mojave",        {10, 14, 1}}},
+	"18C54"    = {{18, 2, 0}, "macOS", {"Mojave",        {10, 14, 2}}},
+	"18D42"    = {{18, 2, 0}, "macOS", {"Mojave",        {10, 14, 3}}},
+	"18D43"    = {{18, 2, 0}, "macOS", {"Mojave",        {10, 14, 3}}},
+	"18D109"   = {{18, 2, 0}, "macOS", {"Mojave",        {10, 14, 3}}},
+	"18E226"   = {{18, 5, 0}, "macOS", {"Mojave",        {10, 14, 4}}},
+	"18E227"   = {{18, 5, 0}, "macOS", {"Mojave",        {10, 14, 4}}},
+	"18F132"   = {{18, 6, 0}, "macOS", {"Mojave",        {10, 14, 5}}},
+	"18G84"    = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G87"    = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G95"    = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G103"   = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G1012"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G2022"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G3020"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G4032"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G5033"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G6020"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G6032"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G6042"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G7016"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G8012"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G8022"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G9028"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G9216"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+	"18G9323"  = {{18, 7, 0}, "macOS", {"Mojave",        {10, 14, 6}}},
+
+	// MacOS Catalina
+	"19A583"   = {{19, 0, 0}, "macOS", {"Catalina",      {10, 15, 0}}},
+	"19A602"   = {{19, 0, 0}, "macOS", {"Catalina",      {10, 15, 0}}},
+	"19A603"   = {{19, 0, 0}, "macOS", {"Catalina",      {10, 15, 0}}},
+	"19B88"    = {{19, 0, 0}, "macOS", {"Catalina",      {10, 15, 1}}},
+	"19C57"    = {{19, 2, 0}, "macOS", {"Catalina",      {10, 15, 2}}},
+	"19C58"    = {{19, 2, 0}, "macOS", {"Catalina",      {10, 15, 2}}},
+	"19D76"    = {{19, 3, 0}, "macOS", {"Catalina",      {10, 15, 3}}},
+	"19E266"   = {{19, 4, 0}, "macOS", {"Catalina",      {10, 15, 4}}},
+	"19E287"   = {{19, 4, 0}, "macOS", {"Catalina",      {10, 15, 4}}},
+	"19F96"    = {{19, 5, 0}, "macOS", {"Catalina",      {10, 15, 5}}},
+	"19F101"   = {{19, 5, 0}, "macOS", {"Catalina",      {10, 15, 5}}},
+	"19G73"    = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 6}}},
+	"19G2021"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 6}}},
+	"19H2"     = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H4"     = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H15"    = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H114"   = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H512"   = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H524"   = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H1030"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H1217"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H1323"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H1417"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H1419"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H1519"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H1615"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H1713"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H1715"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H1824"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H1922"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+	"19H2026"  = {{19, 6, 0}, "macOS", {"Catalina",      {10, 15, 7}}},
+
+	// MacOS Big Sur
+	"20A2411"  = {{20, 1, 0}, "macOS", {"Big Sur",       {11, 0, 0}}},
+	"20B29"    = {{20, 1, 0}, "macOS", {"Big Sur",       {11, 0, 1}}},
+	"20B50"    = {{20, 1, 0}, "macOS", {"Big Sur",       {11, 0, 1}}},
+	"20C69"    = {{20, 2, 0}, "macOS", {"Big Sur",       {11, 1, 0}}},
+	"20D64"    = {{20, 3, 0}, "macOS", {"Big Sur",       {11, 2, 0}}},
+	"20D74"    = {{20, 3, 0}, "macOS", {"Big Sur",       {11, 2, 1}}},
+	"20D75"    = {{20, 3, 0}, "macOS", {"Big Sur",       {11, 2, 1}}},
+	"20D80"    = {{20, 3, 0}, "macOS", {"Big Sur",       {11, 2, 2}}},
+	"20D91"    = {{20, 3, 0}, "macOS", {"Big Sur",       {11, 2, 3}}},
+	"20E232"   = {{20, 4, 0}, "macOS", {"Big Sur",       {11, 3, 0}}},
+	"20E241"   = {{20, 4, 0}, "macOS", {"Big Sur",       {11, 3, 1}}},
+	"20F71"    = {{20, 5, 0}, "macOS", {"Big Sur",       {11, 4, 0}}},
+	"20G71"    = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 5, 0}}},
+	"20G80"    = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 5, 1}}},
+	"20G95"    = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 5, 2}}},
+	"20G165"   = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 6, 0}}},
+	"20G224"   = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 6, 1}}},
+	"20G314"   = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 6, 2}}},
+	"20G415"   = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 6, 3}}},
+	"20G417"   = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 6, 4}}},
+	"20G527"   = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 6, 5}}},
+	"20G624"   = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 6, 6}}},
+	"20G630"   = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 6, 7}}},
+	"20G730"   = {{20, 6, 0}, "macOS", {"Big Sur",       {11, 6, 8}}},
+
+	// MacOS Monterey
+	"21A344"   = {{21, 0, 1}, "macOS", {"Monterey",      {12, 0, 0}}},
+	"21A559"   = {{21, 1, 0}, "macOS", {"Monterey",      {12, 0, 1}}},
+	"21C52"    = {{21, 2, 0}, "macOS", {"Monterey",      {12, 1, 0}}},
+	"21D49"    = {{21, 3, 0}, "macOS", {"Monterey",      {12, 2, 0}}},
+	"21D62"    = {{21, 3, 0}, "macOS", {"Monterey",      {12, 2, 1}}},
+	"21E230"   = {{21, 4, 0}, "macOS", {"Monterey",      {12, 3, 0}}},
+	"21E258"   = {{21, 4, 0}, "macOS", {"Monterey",      {12, 3, 1}}},
+	"21F79"    = {{21, 5, 0}, "macOS", {"Monterey",      {12, 4, 0}}},
+	"21F2081"  = {{21, 5, 0}, "macOS", {"Monterey",      {12, 4, 0}}},
+	"21F2092"  = {{21, 5, 0}, "macOS", {"Monterey",      {12, 4, 0}}},
+	"21G72"    = {{21, 6, 0}, "macOS", {"Monterey",      {12, 5, 0}}},
+	"21G83"    = {{21, 6, 0}, "macOS", {"Monterey",      {12, 5, 1}}},
+}
+
+@(private)
+Darwin_Match :: enum {
+	Unknown,
+	Exact,
+	Nearest,
+}
+
+@(private)
+map_darwin_kernel_version_to_macos_release :: proc(build: string, darwin: [3]int) -> (res: Darwin_To_Release, match: Darwin_Match) {
+	// Find exact release match if possible.
+	if v, v_ok := macos_release_map[build]; v_ok {
+		return v, .Exact
+	}
+
+	nearest: Darwin_To_Release
+	for _, v in macos_release_map {
+		// Try an exact match on XNU version first.
+		if darwin == v.darwin {
+			return v, .Exact
+		}
+
+		// Major kernel version needs to match exactly,
+		// otherwise the release is considered .Unknown
+		if darwin.x == v.darwin.x {
+			if nearest == {} {
+				nearest = v
+			}
+			if darwin.y >= v.darwin.y && v.darwin != nearest.darwin {
+				nearest = v
+				if darwin.z >= v.darwin.z && v.darwin != nearest.darwin {
+					nearest = v
+				}
+			}
+		}
+	}
+
+	if nearest == {} {
+		return {darwin, "macOS", {"Unknown", {}}}, .Unknown
+	} else {
+		return nearest, .Nearest
+	}
+}

+ 76 - 0
core/sys/info/platform_freebsd.odin

@@ -0,0 +1,76 @@
+// +build freebsd
+package sysinfo
+
+import sys "core:sys/unix"
+import "core:strings"
+import "core:strconv"
+
+@(private)
+version_string_buf: [1024]u8
+
+@(init, private)
+init_os_version :: proc () {
+	os_version.platform = .FreeBSD
+
+	kernel_version_buf: [129]u8
+
+	b := strings.builder_from_bytes(version_string_buf[:])
+	// Retrieve kernel info using `sysctl`, e.g. FreeBSD 13.1-RELEASE-p2 GENERIC
+	mib := []i32{sys.CTL_KERN, sys.KERN_VERSION}
+	if !sys.sysctl(mib, &kernel_version_buf) {
+		return
+	}
+
+	pretty_name := string(cstring(raw_data(kernel_version_buf[:])))
+	pretty_name  = strings.trim(pretty_name, "\n")
+	strings.write_string(&b, pretty_name)
+
+	// l := strings.builder_len(b)
+
+	// Retrieve kernel revision using `sysctl`, e.g. 199506
+	mib = []i32{sys.CTL_KERN, sys.KERN_OSREV}
+	revision: int
+	if !sys.sysctl(mib, &revision) {
+		return
+	}
+	os_version.patch = revision
+
+	strings.write_string(&b, ", revision ")
+	strings.write_int(&b, revision)
+
+	// Finalize pretty name.
+	os_version.as_string = strings.to_string(b)
+
+	// Retrieve kernel release using `sysctl`, e.g. 13.1-RELEASE-p2
+	mib = []i32{sys.CTL_KERN, sys.KERN_OSRELEASE}
+	if !sys.sysctl(mib, &kernel_version_buf) {
+		return
+	}
+
+	// Parse kernel version
+	release := string(cstring(raw_data(kernel_version_buf[:])))
+	version_bits := strings.split_n(release, "-", 2, context.temp_allocator)
+	if len(version_bits) > 1 {
+		// Parse major, minor from KERN_OSRELEASE
+		triplet := strings.split(version_bits[0], ".", context.temp_allocator)
+		if len(triplet) == 2 {
+			major, major_ok := strconv.parse_int(triplet[0])
+			minor, minor_ok := strconv.parse_int(triplet[1])
+
+			if major_ok && minor_ok {
+				os_version.major = major
+				os_version.minor = minor
+			}
+		}
+	}
+}
+
+@(init)
+init_ram :: proc() {
+	// Retrieve RAM info using `sysctl`
+	mib := []i32{sys.CTL_HW, sys.HW_PHYSMEM}
+	mem_size: u64
+	if sys.sysctl(mib, &mem_size) {
+		ram.total_ram = int(mem_size)
+	}
+}

+ 136 - 0
core/sys/info/platform_linux.odin

@@ -0,0 +1,136 @@
+// +build linux
+package sysinfo
+
+import "core:c"
+import sys "core:sys/unix"
+import "core:intrinsics"
+import "core:os"
+import "core:strings"
+import "core:strconv"
+
+@(private)
+version_string_buf: [1024]u8
+
+@(init, private)
+init_os_version :: proc () {
+	os_version.platform = .Linux
+
+	// Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
+	fd, err := os.open("/etc/os-release", os.O_RDONLY, 0)
+	if err != 0 {
+		return
+	}
+	defer os.close(fd)
+
+	os_release_buf: [2048]u8
+	n, read_err := os.read(fd, os_release_buf[:])
+	if read_err != 0 {
+		return
+	}
+	release := string(os_release_buf[:n])
+
+	NEEDLE :: "PRETTY_NAME=\""
+	pretty_start := strings.index(release, NEEDLE)
+
+	b := strings.builder_from_bytes(version_string_buf[:])
+
+	if pretty_start > 0 {
+		for r, i in release[pretty_start + len(NEEDLE):] {
+			if r == '"' {
+				strings.write_string(&b, release[pretty_start + len(NEEDLE):][:i])
+				break
+			} else if r == '\r' || r == '\n' {
+				strings.write_string(&b, "Unknown Linux Distro")
+				break
+			}
+		}
+	}
+
+	NEW_UTS_LEN :: 64
+	UTS_Name :: struct {
+		sys_name:    [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`,
+		node_name:   [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`,
+		release:     [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`,
+		version:     [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`,
+		machine:     [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`,
+		domain_name: [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`,
+	}
+	uts: UTS_Name
+
+	// Grab kernel info using `uname()` syscall, https://linux.die.net/man/2/uname
+	if intrinsics.syscall(sys.SYS_uname, uintptr(&uts)) != 0 {
+		return
+	}
+
+	strings.write_string(&b, ", ")
+	strings.write_string(&b, string(cstring(&uts.sys_name[0])))
+	strings.write_rune(&b, ' ')
+
+	l := strings.builder_len(b)
+	strings.write_string(&b, string(cstring(&uts.release[0])))
+
+	// Parse kernel version, as substrings of the version info in `version_string_buf`
+	version_bits := strings.split_n(strings.to_string(b)[l:], "-", 2, context.temp_allocator)
+	if len(version_bits) > 1 {
+		os_version.version = version_bits[1]
+	}
+
+	// Parse major, minor, patch from release info
+	triplet := strings.split(version_bits[0], ".", context.temp_allocator)
+	if len(triplet) == 3 {
+		major, major_ok := strconv.parse_int(triplet[0])
+		minor, minor_ok := strconv.parse_int(triplet[1])
+		patch, patch_ok := strconv.parse_int(triplet[2])
+
+		if major_ok && minor_ok && patch_ok {
+			os_version.major = major
+			os_version.minor = minor
+			os_version.patch = patch
+		}
+	}
+
+	// Finish the string
+	os_version.as_string = strings.to_string(b)
+}
+
+Sys_Info :: struct {
+	uptime:    c.long,     // Seconds since boot
+	loads:     [3]c.long,  // 1, 5, 15 minute load averages
+	totalram:  c.ulong,    // Total usable main memory size
+	freeram:   c.ulong,    // Available memory size
+	sharedram: c.ulong,    // Amount of shared memory
+	bufferram: c.ulong,    // Memory used by buffers
+	totalswap: c.ulong,    // Total swap space size
+	freeswap:  c.ulong,    // Swap space still available
+	procs:     c.ushort,   // Number of current processes
+	totalhigh: c.ulong,    // Total high memory size
+	freehigh:  c.ulong,    // Available high memory size
+	mem_unit:  c.int,      // Memory unit size in bytes
+	_padding:  [20 - (2 * size_of(c.long)) - size_of(c.int)]u8,
+}
+
+get_sysinfo :: proc "c" () -> (res: Sys_Info, ok: bool) {
+	si: Sys_Info
+	err := intrinsics.syscall(sys.SYS_sysinfo, uintptr(rawptr(&si)))
+	if err != 0 {
+		// Unable to retrieve sysinfo
+		return {}, false
+	}
+	return si, true
+}
+
+@(init)
+init_ram :: proc() {
+	// Retrieve RAM info using `sysinfo`
+	si, ok := get_sysinfo()
+	if !ok {
+		return
+	}
+
+	ram = RAM{
+		total_ram  = int(si.totalram)  * int(si.mem_unit),
+		free_ram   = int(si.freeram)   * int(si.mem_unit),
+		total_swap = int(si.totalswap) * int(si.mem_unit),
+		free_swap  = int(si.freeswap)  * int(si.mem_unit),
+	}
+}

+ 69 - 0
core/sys/info/platform_openbsd.odin

@@ -0,0 +1,69 @@
+// +build openbsd
+package sysinfo
+
+import sys "core:sys/unix"
+import "core:strings"
+import "core:strconv"
+
+@(private)
+version_string_buf: [1024]u8
+
+@(init, private)
+init_os_version :: proc () {
+	os_version.platform = .OpenBSD
+
+	kernel_version_buf: [1024]u8
+
+	b := strings.builder_from_bytes(version_string_buf[:])
+	// Retrieve kernel info using `sysctl`, e.g. OpenBSD
+	mib := []i32{sys.CTL_KERN, sys.KERN_OSTYPE}
+	if !sys.sysctl(mib, &kernel_version_buf) {
+		return
+	}
+	os_type := string(cstring(raw_data(kernel_version_buf[:])))
+	strings.write_string(&b, os_type)
+
+	mib = []i32{sys.CTL_KERN, sys.KERN_OSRELEASE}
+	if !sys.sysctl(mib, &kernel_version_buf) {
+		return
+	}
+
+	strings.write_rune(&b, ' ')
+	version := string(cstring(raw_data(kernel_version_buf[:])))
+	strings.write_string(&b, version)
+
+	// // Parse kernel version
+	triplet := strings.split(version, ".", context.temp_allocator)
+	if len(triplet) == 2 {
+		major, major_ok := strconv.parse_int(triplet[0])
+		minor, minor_ok := strconv.parse_int(triplet[1])
+
+		if major_ok && minor_ok {
+			os_version.major = major
+			os_version.minor = minor
+		}
+	}
+
+	// Retrieve kernel revision using `sysctl`, e.g. 199506
+	mib = []i32{sys.CTL_KERN, sys.KERN_OSREV}
+	revision: int
+	if !sys.sysctl(mib, &revision) {
+		return
+	}
+	os_version.patch = revision
+	strings.write_string(&b, ", build ")
+	strings.write_int(&b, revision)
+
+	// Finalize pretty name.
+	os_version.as_string = strings.to_string(b)
+}
+
+@(init)
+init_ram :: proc() {
+	// Retrieve RAM info using `sysctl`
+	mib := []i32{sys.CTL_HW, sys.HW_PHYSMEM64}
+	mem_size: u64
+	if sys.sysctl(mib, &mem_size) {
+		ram.total_ram = int(mem_size)
+	}
+}

+ 375 - 0
core/sys/info/platform_windows.odin

@@ -0,0 +1,375 @@
+// +build windows
+package sysinfo
+
+import sys "core:sys/windows"
+import "core:intrinsics"
+import "core:strings"
+import "core:unicode/utf16"
+
+import "core:fmt"
+
+@(private)
+version_string_buf: [1024]u8
+
+@(init, private)
+init_os_version :: proc () {
+	/*
+		NOTE(Jeroen):
+			`GetVersionEx`  will return 6.2 for Windows 10 unless the program is manifested for Windows 10.
+			`RtlGetVersion` will return the true version.
+
+			Rather than include the WinDDK, we ask the kernel directly.
+			`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release)
+
+	*/
+	os_version.platform = .Windows
+
+	osvi: sys.OSVERSIONINFOEXW
+	osvi.dwOSVersionInfoSize = size_of(osvi)
+	status := sys.RtlGetVersion(&osvi)
+
+	if status != 0 {
+		return
+	}
+
+	product_type: sys.Windows_Product_Type
+	sys.GetProductInfo(
+		osvi.dwMajorVersion,         osvi.dwMinorVersion,
+		u32(osvi.wServicePackMajor), u32(osvi.wServicePackMinor),
+		&product_type,
+	)
+
+	os_version.major    = int(osvi.dwMajorVersion)
+	os_version.minor    = int(osvi.dwMinorVersion)
+	os_version.build[0] = int(osvi.dwBuildNumber)
+
+	b := strings.builder_from_bytes(version_string_buf[:])
+	strings.write_string(&b, "Windows ")
+
+	switch osvi.dwMajorVersion {
+	case 10:
+		switch osvi.wProductType {
+		case 1: // VER_NT_WORKSTATION:
+			if osvi.dwBuildNumber < 22000 {
+				strings.write_string(&b, "10 ")
+			} else {
+				strings.write_string(&b, "11 ")
+			}
+			format_windows_product_type(&b, product_type)
+
+		case: // Server or Domain Controller
+			switch osvi.dwBuildNumber {
+			case 14393:
+				strings.write_string(&b, "2016 Server")
+			case 17763:
+				strings.write_string(&b, "2019 Server")
+			case 20348:
+				strings.write_string(&b, "2022 Server")
+			case:
+				strings.write_string(&b, "Unknown Server")
+			}
+		}
+
+	case 6:
+		switch osvi.dwMinorVersion {
+		case 0:
+			switch osvi.wProductType {
+			case 1: // VER_NT_WORKSTATION
+				strings.write_string(&b, "Windows Vista ")
+				format_windows_product_type(&b, product_type)
+			case 3:
+				strings.write_string(&b, "Windows Server 2008")
+			}
+
+		case 1:
+			switch osvi.wProductType {
+			case 1: // VER_NT_WORKSTATION:
+				strings.write_string(&b, "Windows 7 ")
+				format_windows_product_type(&b, product_type)
+			case 3:
+				strings.write_string(&b, "Windows Server 2008 R2")
+			}
+
+		case 2:
+			switch osvi.wProductType {
+			case 1: // VER_NT_WORKSTATION:
+				strings.write_string(&b, "Windows 8 ")
+				format_windows_product_type(&b, product_type)
+			case 3:
+				strings.write_string(&b, "Windows Server 2012")
+			}
+
+		case 3:
+			switch osvi.wProductType {
+			case 1: // VER_NT_WORKSTATION:
+				strings.write_string(&b, "Windows 8.1 ")
+				format_windows_product_type(&b, product_type)
+			case 3:
+				strings.write_string(&b, "Windows Server 2012 R2")
+			}
+		}
+
+	case 5:
+		switch osvi.dwMinorVersion {
+		case 0:
+			strings.write_string(&b, "Windows 2000")
+		case 1:
+			strings.write_string(&b, "Windows XP")
+		case 2:
+			strings.write_string(&b, "Windows Server 2003")
+		}
+	}
+
+	// Grab DisplayVersion
+	os_version.version = format_display_version(&b)
+
+	// Grab build number and UBR
+	os_version.build[1]  = format_build_number(&b, int(osvi.dwBuildNumber))
+
+	// Finish the string
+	os_version.as_string = strings.to_string(b)
+
+	format_windows_product_type :: proc (b: ^strings.Builder, prod_type: sys.Windows_Product_Type) {
+		#partial switch prod_type {
+		case .ULTIMATE:
+			strings.write_string(b, "Ultimate")
+
+		case .HOME_BASIC:
+			strings.write_string(b, "Home Basic")
+
+		case .HOME_PREMIUM:
+			strings.write_string(b, "Home Premium")
+
+		case .ENTERPRISE:
+			strings.write_string(b, "Enterprise")
+
+		case .CORE:
+			strings.write_string(b, "Home Basic")
+
+		case .HOME_BASIC_N:
+			strings.write_string(b, "Home Basic N")
+
+		case .EDUCATION:
+			strings.write_string(b, "Education")
+
+		case .EDUCATION_N:
+			strings.write_string(b, "Education N")
+
+		case .BUSINESS:
+			strings.write_string(b, "Business")
+
+		case .STANDARD_SERVER:
+			strings.write_string(b, "Standard Server")
+
+		case .DATACENTER_SERVER:
+			strings.write_string(b, "Datacenter")
+
+		case .SMALLBUSINESS_SERVER:
+			strings.write_string(b, "Windows Small Business Server")
+
+		case .ENTERPRISE_SERVER:
+			strings.write_string(b, "Enterprise Server")
+
+		case .STARTER:
+			strings.write_string(b, "Starter")
+
+		case .DATACENTER_SERVER_CORE:
+			strings.write_string(b, "Datacenter Server Core")
+
+		case .STANDARD_SERVER_CORE:
+			strings.write_string(b, "Server Standard Core")
+
+		case .ENTERPRISE_SERVER_CORE:
+			strings.write_string(b, "Enterprise Server Core")
+
+		case .BUSINESS_N:
+			strings.write_string(b, "Business N")
+
+		case .HOME_SERVER:
+			strings.write_string(b, "Home Server")
+
+		case .SERVER_FOR_SMALLBUSINESS:
+			strings.write_string(b, "Windows Server 2008 for Windows Essential Server Solutions")
+
+		case .SMALLBUSINESS_SERVER_PREMIUM:
+			strings.write_string(b, "Small Business Server Premium")
+
+		case .HOME_PREMIUM_N:
+			strings.write_string(b, "Home Premium N")
+
+		case .ENTERPRISE_N:
+			strings.write_string(b, "Enterprise N")
+
+		case .ULTIMATE_N:
+			strings.write_string(b, "Ultimate N")
+
+		case .HYPERV:
+			strings.write_string(b, "HyperV")
+
+		case .STARTER_N:
+			strings.write_string(b, "Starter N")
+
+		case .PROFESSIONAL:
+			strings.write_string(b, "Professional")
+
+		case .PROFESSIONAL_N:
+			strings.write_string(b, "Professional N")
+
+		case:
+			strings.write_string(b, "Unknown Edition")
+		}
+	}
+
+	// Grab Windows DisplayVersion (like 20H02)
+	format_display_version :: proc (b: ^strings.Builder) -> (version: string) {
+		dv, ok := read_reg(
+			sys.HKEY_LOCAL_MACHINE,
+			"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+			"DisplayVersion",
+			string,
+		)
+		defer delete(dv) // It'll be interned into `version_string_buf`
+
+		if ok {
+			strings.write_string(b, " (version: ")
+			l := strings.builder_len(b^)
+			strings.write_string(b, dv)
+			version = strings.to_string(b^)[l:][:len(dv)]
+			strings.write_rune(b, ')')
+		}
+		return
+	}
+
+	// Grab build number and UBR
+	format_build_number :: proc (b: ^strings.Builder, major_build: int) -> (ubr: int) {
+		res, ok := read_reg(
+			sys.HKEY_LOCAL_MACHINE,
+			"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+			"UBR",
+			i32,
+		)
+
+		if ok {
+			ubr = int(res)
+			strings.write_string(b, ", build: ")
+			strings.write_int(b, major_build)
+			strings.write_rune(b, '.')
+			strings.write_int(b, ubr)
+		}
+		return
+	}
+}
+
+@(init)
+init_ram :: proc() {
+	state: sys.MEMORYSTATUSEX
+
+	state.dwLength = size_of(state)
+	ok := sys.GlobalMemoryStatusEx(&state)
+	if !ok {
+		return
+	}
+	ram = RAM{
+		total_ram  = int(state.ullTotalPhys),
+		free_ram   = int(state.ullAvailPhys),
+		total_swap = int(state.ullTotalPageFil),
+		free_swap  = int(state.ullAvailPageFil),
+	}
+}
+
+@(init, private)
+init_gpu_info :: proc() {
+
+	GPU_INFO_BASE :: "SYSTEM\\ControlSet001\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\"
+
+	gpu_list: [dynamic]GPU
+	gpu_index: int
+
+	for {
+		key := fmt.tprintf("%v\\%04d", GPU_INFO_BASE, gpu_index)
+
+		if vendor, ok := read_reg(sys.HKEY_LOCAL_MACHINE, key, "ProviderName", string); ok {
+			append(&gpu_list, GPU{vendor_name = vendor})
+		} else {
+			break
+		}
+
+		if desc, ok := read_reg(sys.HKEY_LOCAL_MACHINE, key, "DriverDesc", string); ok {
+			gpu_list[gpu_index].model_name = desc
+		}
+
+		if vram, ok := read_reg(sys.HKEY_LOCAL_MACHINE, key, "HardwareInformation.qwMemorySize", i64); ok {
+			gpu_list[gpu_index].total_ram = int(vram)
+		}
+		gpu_index += 1
+	}
+	gpus = gpu_list[:]
+}
+
+@(private)
+read_reg :: proc(hkey: sys.HKEY, subkey, val: string, $T: typeid) -> (res: T, ok: bool) {
+	BUF_SIZE :: 1024
+
+	if len(subkey) == 0 || len(val) == 0 {
+		return {}, false
+	}
+
+	key_name_wide := make([]u16, BUF_SIZE, context.temp_allocator)
+	val_name_wide := make([]u16, BUF_SIZE, context.temp_allocator)
+
+	utf16.encode_string(key_name_wide, subkey)
+	utf16.encode_string(val_name_wide, val)
+
+	when T == string {
+		result_wide := make([]u16, BUF_SIZE, context.temp_allocator)
+		result_size := sys.DWORD(BUF_SIZE * size_of(u16))
+
+		status := sys.RegGetValueW(
+			hkey,
+			&key_name_wide[0],
+			&val_name_wide[0],
+			sys.RRF_RT_REG_SZ,
+			nil,
+			raw_data(result_wide[:]),
+			&result_size,
+		)
+		if status != 0 {
+			// Couldn't retrieve string
+			return
+		}
+
+		// Result string will be allocated for the caller.
+		result_utf8 := make([]u8, BUF_SIZE * 4, context.temp_allocator)
+		utf16.decode_to_utf8(result_utf8, result_wide[:result_size])
+		return strings.clone_from_cstring(cstring(raw_data(result_utf8))), true
+
+	} else when T == i32 {
+		result_size := sys.DWORD(size_of(i32))
+		status := sys.RegGetValueW(
+			hkey,
+			&key_name_wide[0],
+			&val_name_wide[0],
+			sys.RRF_RT_REG_DWORD,
+			nil,
+			&res,
+			&result_size,
+		)
+		return res, status == 0
+
+	} else when T == i64 {
+		result_size := sys.DWORD(size_of(i64))
+		status := sys.RegGetValueW(
+			hkey,
+			&key_name_wide[0],
+			&val_name_wide[0],
+			sys.RRF_RT_REG_QWORD,
+			nil,
+			&res,
+			&result_size,
+		)
+		return res, status == 0
+	} else {
+		#assert(false, "Unhandled type for read_reg")
+	}
+	return
+}

+ 45 - 0
core/sys/info/sysinfo.odin

@@ -0,0 +1,45 @@
+package sysinfo
+
+when !(ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .arm32 || ODIN_ARCH == .arm64) {
+	#assert(false, "This package is unsupported on this architecture.")
+}
+
+os_version: OS_Version
+ram:        RAM
+gpus:       []GPU
+
+OS_Version_Platform :: enum {
+	Unknown,
+	Windows,
+	Linux,
+	MacOS,
+	iOS,
+	FreeBSD,
+	OpenBSD,
+	NetBSD,
+}
+
+OS_Version :: struct {
+	platform: OS_Version_Platform,
+
+	major:     int,
+	minor:     int,
+	patch:     int,
+	build:     [2]int,
+	version:   string,
+
+	as_string: string,
+}
+
+RAM :: struct {
+	total_ram:  int,
+	free_ram:   int,
+	total_swap: int,
+	free_swap:  int,
+}
+
+GPU :: struct {
+	vendor_name: string,
+	model_name:  string,
+	total_ram:   int,
+}

+ 7 - 0
core/sys/unix/syscalls_freebsd.odin

@@ -0,0 +1,7 @@
+package unix
+
+// FreeBSD 13 syscall numbers
+// See: https://alfonsosiciliano.gitlab.io/posts/2021-01-02-freebsd-system-calls-table.html
+
+SYS_uname  : uintptr : 164
+SYS_sysctl : uintptr : 202

+ 3 - 3
core/sys/unix/syscalls_linux.odin

@@ -1568,7 +1568,7 @@ sys_gettid :: proc "contextless" () -> int {
 }
 }
 
 
 sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int {
 sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int {
-	return cast(int)intrinsics.syscall(SYS_getrandom, buf, cast(uintptr)(buflen), cast(uintptr)(flags))
+	return cast(int)intrinsics.syscall(SYS_getrandom, uintptr(buf), uintptr(buflen), uintptr(flags))
 }
 }
 
 
 sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int {
 sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int {
@@ -1622,7 +1622,7 @@ sys_lseek :: proc "contextless" (fd: int, offset: i64, whence: int) -> i64 {
 		low := uintptr(offset & 0xFFFFFFFF)
 		low := uintptr(offset & 0xFFFFFFFF)
 		high := uintptr(offset >> 32)
 		high := uintptr(offset >> 32)
 		result: i64
 		result: i64
-		res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence)))
+		res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
 		return res if res < 0 else result
 		return res if res < 0 else result
 	}
 	}
 }
 }
@@ -1748,7 +1748,7 @@ sys_unlink :: proc "contextless" (path: cstring) -> int {
 }
 }
 
 
 sys_unlinkat :: proc "contextless" (dfd: int, path: cstring, flag: int = 0) -> int {
 sys_unlinkat :: proc "contextless" (dfd: int, path: cstring, flag: int = 0) -> int {
-	return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), flag))
+	return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flag)))
 }
 }
 
 
 sys_rmdir :: proc "contextless" (path: cstring) -> int {
 sys_rmdir :: proc "contextless" (path: cstring) -> int {

+ 6 - 0
core/sys/unix/syscalls_openbsd.odin

@@ -0,0 +1,6 @@
+package unix
+
+// OpenBSD 7.1 syscall numbers
+// See: /usr/include/sys/syscall.h
+
+SYS_sysctl : uintptr : 202

+ 45 - 0
core/sys/unix/sysctl_darwin.odin

@@ -0,0 +1,45 @@
+//+build darwin
+package unix
+
+import "core:sys/darwin"
+import "core:intrinsics"
+
+_ :: darwin
+
+sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) {
+	mib := mib
+	result_size := i64(size_of(T))
+
+	res := intrinsics.syscall(
+		darwin.unix_offset_syscall(.sysctl),
+		uintptr(raw_data(mib)), uintptr(len(mib)),
+		uintptr(val), uintptr(&result_size),
+		uintptr(0), uintptr(0),
+	)
+	return res == 0
+}
+
+// See sysctl.h for darwin for details
+CTL_KERN    :: 1
+	KERN_OSTYPE    :: 1  // Darwin
+	KERN_OSRELEASE :: 2  // 21.5.0 for 12.4 Monterey
+	KERN_OSREV     :: 3  // i32: system revision
+	KERN_VERSION   :: 4  // Darwin Kernel Version 21.5.0: Tue Apr 26 21:08:22 PDT 2022; root:darwin-8020.121.3~4/RELEASE_X86_64
+	KERN_OSRELDATE :: 26 // i32: OS release date
+	KERN_OSVERSION :: 65 // Build number, e.g. 21F79
+CTL_VM      :: 2
+CTL_VFS     :: 3
+CTL_NET     :: 4
+CTL_DEBUG   :: 5
+CTL_HW      :: 6
+	HW_MACHINE      :: 1  // x86_64
+	HW_MODEL        :: 2  // MacbookPro14,1
+	HW_NCPU         :: 3  /* int: number of cpus */
+	HW_BYTEORDER    :: 4  /* int: machine byte order */
+	HW_MACHINE_ARCH :: 12 /* string: machine architecture */
+	HW_VECTORUNIT   :: 13 /* int: has HW vector unit? */
+	HW_MEMSIZE      :: 24 // u64
+	HW_AVAILCPU     :: 25 /* int: number of available CPUs */
+
+CTL_MACHDEP :: 7
+CTL_USER    :: 8

+ 44 - 0
core/sys/unix/sysctl_freebsd.odin

@@ -0,0 +1,44 @@
+//+build freebsd
+package unix
+
+import "core:intrinsics"
+
+sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) {
+	mib := mib
+	result_size := i64(size_of(T))
+
+	res := intrinsics.syscall(SYS_sysctl,
+		uintptr(raw_data(mib)), uintptr(len(mib)),
+		uintptr(val), uintptr(&result_size),
+		uintptr(0), uintptr(0),
+	)
+	return res == 0
+}
+
+// See /usr/include/sys/sysctl.h for details
+CTL_SYSCTL :: 0
+CTL_KERN   :: 1
+	KERN_OSTYPE    :: 1
+	KERN_OSRELEASE :: 2
+	KERN_OSREV     :: 3
+	KERN_VERSION   :: 4
+CTL_VM     :: 2
+CTL_VFS    :: 3
+CTL_NET    :: 4
+CTL_DEBUG  :: 5
+CTL_HW     :: 6
+	HW_MACHINE      ::  1
+	HW_MODEL        ::  2
+	HW_NCPU         ::  3
+	HW_BYTEORDER    ::  4
+	HW_PHYSMEM      ::  5
+	HW_USERMEM      ::  6
+	HW_PAGESIZE     ::  7
+	HW_DISKNAMES    ::  8
+	HW_DISKSTATS    ::  9
+	HW_FLOATINGPT   :: 10
+	HW_MACHINE_ARCH :: 11
+	HW_REALMEM      :: 12
+CTL_MACHDEP  :: 7
+CTL_USER     :: 8
+CTL_P1003_1B :: 9

+ 49 - 0
core/sys/unix/sysctl_openbsd.odin

@@ -0,0 +1,49 @@
+//+build openbsd
+package unix
+
+import "core:c"
+foreign import libc "system:c"
+
+@(default_calling_convention="c")
+foreign libc {
+	@(link_name="sysctl")	_unix_sysctl    :: proc(name: [^]i32, namelen: u32, oldp: rawptr, oldlenp: ^c.size_t, newp: rawptr, newlen: c.size_t) -> i32 ---
+}
+
+sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) {
+	mib := mib
+	result_size := c.size_t(size_of(T))
+	res := _unix_sysctl(raw_data(mib), u32(len(mib)), val, &result_size, nil, 0)
+	return res == 0
+}
+
+// See /usr/include/sys/sysctl.h for details
+CTL_SYSCTL :: 0
+CTL_KERN   :: 1
+	KERN_OSTYPE    :: 1
+	KERN_OSRELEASE :: 2
+	KERN_OSREV     :: 3
+	KERN_VERSION   :: 4
+CTL_VM     :: 2
+CTL_FS     :: 3
+CTL_NET    :: 4
+CTL_DEBUG  :: 5
+CTL_HW     :: 6
+	HW_MACHINE   ::  1
+	HW_MODEL     ::  2
+	HW_NCPU      ::  3
+	HW_BYTEORDER ::  4
+	HW_PHYSMEM   ::  5
+	HW_USERMEM   ::  6
+	HW_PAGESIZE  ::  7
+	HW_DISKNAMES ::  8
+	HW_DISKSTATS ::  9
+	HW_DISKCOUNT :: 10
+	HW_SENSORS   :: 11
+	HW_CPUSPEED  :: 12
+	HW_SETPERF   :: 13
+	HW_VENDOR    :: 14
+	HW_PRODUCT   :: 15
+	HW_VERSION   :: 16
+	HW_SERIALNO  :: 17
+	HW_UUID      :: 18
+	HW_PHYSMEM64 :: 19

+ 2 - 2
core/sys/valgrind/callgrind.odin

@@ -13,10 +13,10 @@ Callgrind_Client_Request :: enum uintptr {
 }
 }
 
 
 @(require_results)
 @(require_results)
-callgrind_client_request_expr :: proc "c" (default: uintptr, request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
+callgrind_client_request_expr :: #force_inline proc "c" (default: uintptr, request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
 	return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
 	return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
 }
 }
-callgrind_client_request_stmt :: proc "c" (request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) {
+callgrind_client_request_stmt :: #force_inline proc "c" (request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) {
 	_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
 	_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
 }
 }
 
 

+ 178 - 0
core/sys/valgrind/helgrind.odin

@@ -0,0 +1,178 @@
+//+build amd64
+package sys_valgrind
+
+import "core:intrinsics"
+
+Helgrind_Client_Request :: enum uintptr {
+	Clean_Memory = 'H'<<24 | 'G'<<16,
+	Set_My_pthread_t = ('H'<<25 | 'G'<<16)+256,
+	Pthread_Join_Post,
+	Pthread_Mutex_Init_Post,
+	Pthread_Mutex_Destroy_Pre,
+	Pthread_Mutex_Unlock_Pre,
+	Pthread_Mutex_Unlock_Post,
+	Pthread_Mutex_Lock_Pre,
+	Pthread_Mutex_Lock_Post,
+	Pthread_Cond_Signal_Pre,
+	Pthread_Cond_Broadcast_Pre,
+	Pthread_Cond_Wait_Pre,
+	Pthread_Cond_Wait_Post,
+	Pthread_Cond_Destroy_Pre,
+	Pthread_Rwlock_Init_Post,
+	Pthread_Rwlock_Destroy_Pre,
+	Pthread_Rwlock_Lock_Pre,
+	Pthread_Rwlock_Lock_Post,
+	Pthread_Rwlock_Unlock_Pre,
+	Pthread_Rwlock_Unlock_Post,
+	Posix_Sem_Init_Post,
+	Posix_Sem_Destroy_Pre,
+	Posix_Sem_Post_Pre,
+	Posix_Sem_Wait_Post,
+	Pthread_Barrier_Init_Pre,
+	Pthread_Barrier_Wait_Pre,
+	Pthread_Barrier_Destroy_Pre,
+	Pthread_Spin_Init_Or_Unlock_Pre,
+	Pthread_Spin_Init_Or_Unlock_Post,
+	Pthread_Spin_Lock_Pre,
+	Pthread_Spin_Lock_Post,
+	Pthread_Spin_Destroy_Pre,
+	Clientreq_Unimp,
+	Userso_Send_Pre,
+	Userso_Recv_Post,
+	Userso_Forget_All,
+	Reserved2,
+	Reserved3,
+	Reserved4,
+	Arange_Make_Untracked,
+	Arange_Make_Tracked,
+	Pthread_Barrier_Resize_Pre,
+	Clean_Memory_Heapblock,
+	Pthread_Cond_Init_Post,
+}
+
+@(require_results)
+helgrind_client_request_expr :: #force_inline proc "c" (default: uintptr, request: Helgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
+	return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
+}
+helgrind_client_request_stmt :: #force_inline proc "c" (request: Helgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) {
+	_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
+}
+
+helgrind_mutex_init_post :: proc "c" (mutex: rawptr, mb_rec: uint) {
+	helgrind_client_request_stmt(.Pthread_Mutex_Init_Post, uintptr(mutex), uintptr(mb_rec), 0, 0, 0)
+}
+helgrind_mutex_destroy_pre :: proc "c" (mutex: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Mutex_Destroy_Pre, uintptr(mutex), 0, 0, 0, 0)
+}
+helgrind_mutex_lock_pre :: proc "c" (mutex: rawptr, is_try_lock: bool) {
+	helgrind_client_request_stmt(.Pthread_Mutex_Lock_Pre, uintptr(mutex), uintptr(is_try_lock), 0, 0, 0)
+}
+helgrind_mutex_lock_post :: proc "c" (mutex: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Mutex_Lock_Post, uintptr(mutex), 0, 0, 0, 0)
+}
+helgrind_mutex_unlock_pre :: proc "c" (mutex: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Mutex_Unlock_Pre, uintptr(mutex), 0, 0, 0, 0)
+}
+helgrind_mutex_unlock_post :: proc "c" (mutex: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Mutex_Unlock_Post, uintptr(mutex), 0, 0, 0, 0)
+}
+
+helgrind_rwlock_init_post :: proc "c" (lock: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Rwlock_Init_Post, uintptr(lock), 0, 0, 0, 0)
+}
+helgrind_rwlock_destroy_pre :: proc "c" (lock: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Rwlock_Destroy_Pre, uintptr(lock), 0, 0, 0, 0)
+}
+helgrind_rwlock_lock_pre :: proc "c" (lock: rawptr, is_w: bool) {
+	helgrind_client_request_stmt(.Pthread_Rwlock_Lock_Pre, uintptr(lock), uintptr(is_w), 0, 0, 0)
+}
+helgrind_rwlock_unlock_post :: proc "c" (lock: rawptr, is_w: bool) {
+	helgrind_client_request_stmt(.Pthread_Rwlock_Unlock_Pre, uintptr(lock), uintptr(is_w), 0, 0, 0)
+}
+
+
+helgrind_sem_init_post :: proc "c" (sem: rawptr, value: uint) {
+	helgrind_client_request_stmt(.Posix_Sem_Init_Post, uintptr(sem), uintptr(value), 0, 0, 0)
+}
+helgrind_sem_wait_post :: proc "c" (sem: rawptr) {
+	helgrind_client_request_stmt(.Posix_Sem_Wait_Post, uintptr(sem), 0, 0, 0, 0)
+}
+helgrind_sem_post_pre :: proc "c" (sem: rawptr) {
+	helgrind_client_request_stmt(.Posix_Sem_Post_Pre, uintptr(sem), 0, 0, 0, 0)
+}
+helgrind_sem_destroy_pre :: proc "c" (sem: rawptr) {
+	helgrind_client_request_stmt(.Posix_Sem_Destroy_Pre, uintptr(sem), 0, 0, 0, 0)
+}
+
+
+helgrind_barrier_init_pre :: proc "c" (bar: rawptr, count: uint, resizable: bool) {
+	helgrind_client_request_stmt(.Pthread_Barrier_Init_Pre, uintptr(bar), uintptr(count), uintptr(resizable), 0, 0)
+}
+helgrind_barrier_wait_pre :: proc "c" (bar: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Barrier_Wait_Pre, uintptr(bar), 0, 0, 0, 0)
+}
+helgrind_barrier_resize_pre :: proc "c" (bar: rawptr, new_count: uint) {
+	helgrind_client_request_stmt(.Pthread_Barrier_Resize_Pre, uintptr(bar), uintptr(new_count), 0, 0, 0)
+}
+helgrind_barrier_destroy_pre :: proc "c" (bar: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Barrier_Destroy_Pre, uintptr(bar), 0, 0, 0, 0)
+}
+
+
+
+helgrind_clean_memory :: proc "c" (qzz_start: rawptr, qzz_len: uint) {
+	helgrind_client_request_stmt(.Clean_Memory, uintptr(qzz_start), uintptr(qzz_len), 0, 0, 0)
+}
+helgrind_clean_memory_slice :: proc "c" (qzz: []byte) {
+	helgrind_client_request_stmt(.Clean_Memory, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
+}
+helgrind_clean_memory_heap_block :: proc "c" (qzz_blockstart: rawptr) -> int {
+	return int(helgrind_client_request_expr(~uintptr(1), .Clean_Memory_Heapblock, uintptr(qzz_blockstart), 0, 0, 0, 0))
+}
+
+
+helgrind_disable_checking :: proc "c" (qzz_start: rawptr, qzz_len: uint) {
+	helgrind_client_request_stmt(.Arange_Make_Untracked, uintptr(qzz_start), uintptr(qzz_len), 0, 0, 0)
+}
+helgrind_enable_checking :: proc "c" (qzz_start: rawptr, qzz_len: uint) {
+	helgrind_client_request_stmt(.Arange_Make_Tracked, uintptr(qzz_start), uintptr(qzz_len), 0, 0, 0)
+}
+
+
+helgrind_cond_init_post :: proc "c" (cond: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Cond_Init_Post, uintptr(cond), 0, 0, 0, 0)
+}
+helgrind_cond_destroy_pre :: proc "c" (cond: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Cond_Destroy_Pre, uintptr(cond), 0, 0, 0, 0)
+}
+helgrind_cond_signal_pre :: proc "c" (cond: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Cond_Signal_Pre, uintptr(cond), 0, 0, 0, 0)
+}
+helgrind_cond_broadcast_pre :: proc "c" (cond: rawptr) {
+	helgrind_client_request_stmt(.Pthread_Cond_Broadcast_Pre, uintptr(cond), 0, 0, 0, 0)
+}
+helgrind_cond_wait_pre :: proc "c" (cond: rawptr, lock: rawptr) -> bool {
+	return 0 != helgrind_client_request_expr(0, .Pthread_Cond_Wait_Pre, uintptr(cond), uintptr(lock), 0, 0, 0)
+}
+helgrind_cond_wait_post :: proc "c" (cond: rawptr, lock: rawptr) -> bool {
+	return 0 != helgrind_client_request_expr(0, .Pthread_Cond_Wait_Post, uintptr(cond), uintptr(lock), 0, 0, 0)
+}
+
+
+helgrind_client_request_unimp :: #force_inline proc "c" (msg: cstring) {
+	helgrind_client_request_stmt(.Clientreq_Unimp, uintptr(rawptr(msg)), 0, 0, 0, 0)
+}
+
+
+helgrind_annotate_condvar_lock_wait :: #force_inline proc "c" (cv: rawptr, lock: rawptr) {
+	helgrind_client_request_unimp("ANNOTATE_CONDVAR_LOCK_WAIT")
+}
+helgrind_annotate_condvar_wait :: proc "c" (cv: rawptr) {
+	helgrind_client_request_unimp("ANNOTATE_CONDVAR_WAIT")
+}
+helgrind_annotate_condvar_signal :: proc "c" (cv: rawptr) {
+	helgrind_client_request_unimp("ANNOTATE_CONDVAR_SIGNAL")
+}
+helgrind_annotate_condvar_signal_all :: proc "c" (cv: rawptr) {
+	helgrind_client_request_unimp("ANNOTATE_CONDVAR_SIGNAL_ALL")
+}

+ 2 - 2
core/sys/valgrind/memcheck.odin

@@ -22,10 +22,10 @@ Mem_Check_Client_Request :: enum uintptr {
 }
 }
 
 
 @(require_results)
 @(require_results)
-mem_check_client_request_expr :: proc "c" (default: uintptr, request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
+mem_check_client_request_expr :: #force_inline proc "c" (default: uintptr, request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
 	return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
 	return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
 }
 }
-mem_check_client_request_stmt :: proc "c" (request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) {
+mem_check_client_request_stmt :: #force_inline proc "c" (request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) {
 	_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
 	_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
 }
 }
 
 

+ 4 - 4
core/sys/valgrind/valgrind.odin

@@ -38,10 +38,10 @@ Client_Request :: enum uintptr {
 }
 }
 
 
 @(require_results)
 @(require_results)
-client_request_expr :: proc "c" (default: uintptr, request: Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
+client_request_expr :: #force_inline proc "c" (default: uintptr, request: Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
 	return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
 	return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
 }
 }
-client_request_stmt :: proc "c" (request: Client_Request, a0, a1, a2, a3, a4: uintptr) {
+client_request_stmt :: #force_inline proc "c" (request: Client_Request, a0, a1, a2, a3, a4: uintptr) {
 	_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
 	_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
 }
 }
 
 
@@ -49,8 +49,8 @@ client_request_stmt :: proc "c" (request: Client_Request, a0, a1, a2, a3, a4: ui
 //     0 - running natively
 //     0 - running natively
 //     1 - running under Valgrind
 //     1 - running under Valgrind
 //     2 - running under Valgrind which is running under another Valgrind
 //     2 - running under Valgrind which is running under another Valgrind
-running_on_valgrind :: proc "c" () -> uintptr {
-	return client_request_expr(0, .Running_On_Valgrind, 0, 0, 0, 0, 0)
+running_on_valgrind :: proc "c" () -> uint {
+	return uint(client_request_expr(0, .Running_On_Valgrind, 0, 0, 0, 0, 0))
 }
 }
 
 
 // Discard translation of code in the slice qzz. Useful if you are debugging a JIT-er or some such,
 // Discard translation of code in the slice qzz. Useful if you are debugging a JIT-er or some such,

+ 36 - 10
core/sys/windows/kernel32.odin

@@ -24,6 +24,8 @@ foreign kernel32 {
 	                       lpMode: LPDWORD) -> BOOL ---
 	                       lpMode: LPDWORD) -> BOOL ---
 	SetConsoleMode :: proc(hConsoleHandle: HANDLE,
 	SetConsoleMode :: proc(hConsoleHandle: HANDLE,
 	                       dwMode: DWORD) -> BOOL ---
 	                       dwMode: DWORD) -> BOOL ---
+	SetConsoleCursorPosition :: proc(hConsoleHandle: HANDLE,
+						   dwCursorPosition: COORD) -> BOOL ---
 
 
 	GetFileInformationByHandle :: proc(hFile: HANDLE, lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) -> BOOL ---
 	GetFileInformationByHandle :: proc(hFile: HANDLE, lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) -> BOOL ---
 	SetHandleInformation :: proc(hObject: HANDLE,
 	SetHandleInformation :: proc(hObject: HANDLE,
@@ -94,6 +96,15 @@ foreign kernel32 {
 		dwCreationFlags: DWORD,
 		dwCreationFlags: DWORD,
 		lpThreadId: LPDWORD,
 		lpThreadId: LPDWORD,
 	) -> HANDLE ---
 	) -> HANDLE ---
+	CreateRemoteThread :: proc(
+		hProcess: HANDLE,
+		lpThreadAttributes: LPSECURITY_ATTRIBUTES,
+		dwStackSize: SIZE_T,
+		lpStartAddress: proc "stdcall" (rawptr) -> DWORD,
+		lpParameter: LPVOID,
+		dwCreationFlags: DWORD,
+		lpThreadId: LPDWORD,
+	) -> HANDLE ---
 	SwitchToThread :: proc() -> BOOL ---
 	SwitchToThread :: proc() -> BOOL ---
 	ResumeThread :: proc(thread: HANDLE) -> DWORD ---
 	ResumeThread :: proc(thread: HANDLE) -> DWORD ---
 	GetThreadPriority :: proc(thread: HANDLE) -> c_int ---
 	GetThreadPriority :: proc(thread: HANDLE) -> c_int ---
@@ -326,6 +337,15 @@ foreign kernel32 {
 	SetEndOfFile :: proc(hFile: HANDLE) -> BOOL ---
 	SetEndOfFile :: proc(hFile: HANDLE) -> BOOL ---
 
 
 	CreatePipe :: proc(hReadPipe, hWritePipe: ^HANDLE, lpPipeAttributes: LPSECURITY_ATTRIBUTES, nSize: DWORD) -> BOOL ---
 	CreatePipe :: proc(hReadPipe, hWritePipe: ^HANDLE, lpPipeAttributes: LPSECURITY_ATTRIBUTES, nSize: DWORD) -> BOOL ---
+
+	ConnectNamedPipe :: proc(hNamedPipe: HANDLE, lpOverlapped: LPOVERLAPPED,) -> BOOL ---
+	DisconnectNamedPipe :: proc(hNamedPipe: HANDLE,) -> BOOL ---
+	WaitNamedPipeW :: proc(lpNamedPipeName: LPCWSTR, nTimeOut: DWORD,) -> BOOL ---
+
+	SetConsoleCtrlHandler :: proc(HandlerRoutine: PHANDLER_ROUTINE, Add: BOOL) -> BOOL ---
+	GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL ---
+	FreeConsole :: proc() -> BOOL ---
+	GetConsoleWindow :: proc() -> HWND ---
 }
 }
 
 
 
 
@@ -637,6 +657,13 @@ foreign kernel32 {
 	) -> BOOL ---
 	) -> BOOL ---
 }
 }
 
 
+@(default_calling_convention="stdcall")
+foreign kernel32 {
+	GlobalMemoryStatusEx :: proc(
+		lpBuffer: ^MEMORYSTATUSEX,
+	) -> BOOL ---
+}
+
 PBAD_MEMORY_CALLBACK_ROUTINE :: #type proc "stdcall" ()
 PBAD_MEMORY_CALLBACK_ROUTINE :: #type proc "stdcall" ()
 
 
 @(default_calling_convention="stdcall")
 @(default_calling_convention="stdcall")
@@ -782,15 +809,14 @@ foreign kernel32 {
 
 
 @(default_calling_convention="stdcall")
 @(default_calling_convention="stdcall")
 foreign kernel32 {
 foreign kernel32 {
-	@(link_name="SetConsoleCtrlHandler") set_console_ctrl_handler :: proc(handler: Handler_Routine, add: BOOL) -> BOOL ---
+	GetProductInfo :: proc(
+		OSMajorVersion: DWORD,
+		OSMinorVersion: DWORD,
+		SpMajorVersion: DWORD,
+		SpMinorVersion: DWORD,
+		product_type: ^Windows_Product_Type,
+	) -> BOOL ---
 }
 }
 
 
-Handler_Routine :: proc(dwCtrlType: Control_Event) -> BOOL
-
-Control_Event :: enum DWORD {
-	control_c = 0,
-	_break    = 1,
-	close     = 2,
-	logoff    = 5,
-	shutdown  = 6,
-}
+HandlerRoutine :: proc "stdcall" (dwCtrlType: DWORD) -> BOOL
+PHANDLER_ROUTINE :: HandlerRoutine

+ 171 - 0
core/sys/windows/types.odin

@@ -17,6 +17,7 @@ size_t      :: c.size_t
 wchar_t     :: c.wchar_t
 wchar_t     :: c.wchar_t
 
 
 DWORD :: c_ulong
 DWORD :: c_ulong
+DWORDLONG :: c.ulonglong
 QWORD :: c.ulonglong
 QWORD :: c.ulonglong
 HANDLE :: distinct LPVOID
 HANDLE :: distinct LPVOID
 HINSTANCE :: HANDLE
 HINSTANCE :: HANDLE
@@ -978,6 +979,35 @@ WS_TILEDWINDOW      : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKF
 WS_VISIBLE          : UINT : 0x1000_0000
 WS_VISIBLE          : UINT : 0x1000_0000
 WS_VSCROLL          : UINT : 0x0020_0000
 WS_VSCROLL          : UINT : 0x0020_0000
 
 
+WS_EX_ACCEPTFILES           : UINT : 0x0000_0010
+WS_EX_APPWINDOW             : UINT : 0x0004_0000
+WS_EX_CLIENTEDGE            : UINT : 0x0000_0200
+WS_EX_COMPOSITED            : UINT : 0x0200_0000
+WS_EX_CONTEXTHELP           : UINT : 0x0000_0400
+WS_EX_CONTROLPARENT         : UINT : 0x0001_0000
+WS_EX_DLGMODALFRAME         : UINT : 0x0000_0001
+WS_EX_DRAGDETECT            : UINT : 0x0000_0002 // undocumented
+WS_EX_LAYERED               : UINT : 0x0008_0000
+WS_EX_LAYOUTRTL             : UINT : 0x0040_0000
+WS_EX_LEFT                  : UINT : 0x0000_0000
+WS_EX_LEFTSCROLLBAR         : UINT : 0x0000_4000
+WS_EX_LTRREADING            : UINT : 0x0000_0000
+WS_EX_MDICHILD              : UINT : 0x0000_0040
+WS_EX_NOACTIVATE            : UINT : 0x0800_0000
+WS_EX_NOINHERITLAYOUT       : UINT : 0x0010_0000
+WS_EX_NOPARENTNOTIFY        : UINT : 0x0000_0004
+WS_EX_NOREDIRECTIONBITMAP   : UINT : 0x0020_0000
+WS_EX_OVERLAPPEDWINDOW      : UINT : WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE
+WS_EX_PALETTEWINDOW         : UINT : WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST
+WS_EX_RIGHT                 : UINT : 0x0000_1000
+WS_EX_RIGHTSCROLLBAR        : UINT : 0x0000_0000
+WS_EX_RTLREADING            : UINT : 0x0000_2000
+WS_EX_STATICEDGE            : UINT : 0x0002_0000
+WS_EX_TOOLWINDOW            : UINT : 0x0000_0080
+WS_EX_TOPMOST               : UINT : 0x0000_0008
+WS_EX_TRANSPARENT           : UINT : 0x0000_0020
+WS_EX_WINDOWEDGE            : UINT : 0x0000_0100
+
 PBS_SMOOTH   :: 0x01
 PBS_SMOOTH   :: 0x01
 PBS_VERTICAL :: 0x04
 PBS_VERTICAL :: 0x04
 
 
@@ -1628,6 +1658,8 @@ CONDITION_VARIABLE_INIT :: CONDITION_VARIABLE{}
 SRWLOCK_INIT :: SRWLOCK{}
 SRWLOCK_INIT :: SRWLOCK{}
 
 
 DETACHED_PROCESS: DWORD : 0x00000008
 DETACHED_PROCESS: DWORD : 0x00000008
+CREATE_NEW_CONSOLE: DWORD : 0x00000010
+CREATE_NO_WINDOW: DWORD : 0x08000000
 CREATE_NEW_PROCESS_GROUP: DWORD : 0x00000200
 CREATE_NEW_PROCESS_GROUP: DWORD : 0x00000200
 CREATE_UNICODE_ENVIRONMENT: DWORD : 0x00000400
 CREATE_UNICODE_ENVIRONMENT: DWORD : 0x00000400
 STARTF_USESTDHANDLES: DWORD : 0x00000100
 STARTF_USESTDHANDLES: DWORD : 0x00000100
@@ -1689,6 +1721,7 @@ PIPE_WAIT: DWORD : 0x00000000
 PIPE_TYPE_BYTE: DWORD : 0x00000000
 PIPE_TYPE_BYTE: DWORD : 0x00000000
 PIPE_REJECT_REMOTE_CLIENTS: DWORD : 0x00000008
 PIPE_REJECT_REMOTE_CLIENTS: DWORD : 0x00000008
 PIPE_READMODE_BYTE: DWORD : 0x00000000
 PIPE_READMODE_BYTE: DWORD : 0x00000000
+PIPE_ACCEPT_REMOTE_CLIENTS: DWORD : 0x00000000
 
 
 FD_SETSIZE :: 64
 FD_SETSIZE :: 64
 
 
@@ -3265,3 +3298,141 @@ IFileSaveDialogVtbl :: struct {
 	GetProperties:          proc "stdcall" (this: ^IFileSaveDialog, ppStore: ^^IPropertyStore) -> HRESULT,
 	GetProperties:          proc "stdcall" (this: ^IFileSaveDialog, ppStore: ^^IPropertyStore) -> HRESULT,
 	ApplyProperties:        proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem, pStore: ^IPropertyStore, hwnd: HWND, pSink: ^IFileOperationProgressSink) -> HRESULT,
 	ApplyProperties:        proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem, pStore: ^IPropertyStore, hwnd: HWND, pSink: ^IFileOperationProgressSink) -> HRESULT,
 }
 }
+
+MEMORYSTATUSEX :: struct {
+	dwLength:                DWORD,
+	dwMemoryLoad:            DWORD,
+	ullTotalPhys:            DWORDLONG,
+	ullAvailPhys:            DWORDLONG,
+	ullTotalPageFil:         DWORDLONG,
+	ullAvailPageFil:         DWORDLONG,
+	ullTotalVirtual:         DWORDLONG,
+	ullAvailVirtual:         DWORDLONG,
+	ullAvailExtendedVirtual: DWORDLONG,
+}
+
+Windows_Product_Type :: enum DWORD {
+	BUSINESS                            = 0x00000006, // Business
+	BUSINESS_N                          = 0x00000010, // Business N
+	CLUSTER_SERVER                      = 0x00000012, // HPC Edition
+	CLUSTER_SERVER_V                    = 0x00000040, // Server Hyper Core V
+	CORE                                = 0x00000065, // Windows 10 Home
+	CORE_COUNTRYSPECIFIC                = 0x00000063, // Windows 10 Home China
+	CORE_N                              = 0x00000062, // Windows 10 Home N
+	CORE_SINGLELANGUAGE                 = 0x00000064, // Windows 10 Home Single Language
+	DATACENTER_EVALUATION_SERVER        = 0x00000050, // Server Datacenter (evaluation installation)
+	DATACENTER_A_SERVER_CORE            = 0x00000091, // Server Datacenter, Semi-Annual Channel (core installation)
+	STANDARD_A_SERVER_CORE              = 0x00000092, // Server Standard, Semi-Annual Channel (core installation)
+	DATACENTER_SERVER                   = 0x00000008, // Server Datacenter (full installation. For Server Core installations of Windows Server 2012 and later, use the method, Determining whether Server Core is running.)
+	DATACENTER_SERVER_CORE              = 0x0000000C, // Server Datacenter (core installation, Windows Server 2008 R2 and earlier)
+	DATACENTER_SERVER_CORE_V            = 0x00000027, // Server Datacenter without Hyper-V (core installation)
+	DATACENTER_SERVER_V                 = 0x00000025, // Server Datacenter without Hyper-V (full installation)
+	EDUCATION                           = 0x00000079, // Windows 10 Education
+	EDUCATION_N                         = 0x0000007A, // Windows 10 Education N
+	ENTERPRISE                          = 0x00000004, // Windows 10 Enterprise
+	ENTERPRISE_E                        = 0x00000046, // Windows 10 Enterprise E
+	ENTERPRISE_EVALUATION               = 0x00000048, // Windows 10 Enterprise Evaluation
+	ENTERPRISE_N                        = 0x0000001B, // Windows 10 Enterprise N
+	ENTERPRISE_N_EVALUATION             = 0x00000054, // Windows 10 Enterprise N Evaluation
+	ENTERPRISE_S                        = 0x0000007D, // Windows 10 Enterprise 2015 LTSB
+	ENTERPRISE_S_EVALUATION             = 0x00000081, // Windows 10 Enterprise 2015 LTSB Evaluation
+	ENTERPRISE_S_N                      = 0x0000007E, // Windows 10 Enterprise 2015 LTSB N
+	ENTERPRISE_S_N_EVALUATION           = 0x00000082, // Windows 10 Enterprise 2015 LTSB N Evaluation
+	ENTERPRISE_SERVER                   = 0x0000000A, // Server Enterprise (full installation)
+	ENTERPRISE_SERVER_CORE              = 0x0000000E, // Server Enterprise (core installation)
+	ENTERPRISE_SERVER_CORE_V            = 0x00000029, // Server Enterprise without Hyper-V (core installation)
+	ENTERPRISE_SERVER_IA64              = 0x0000000F, // Server Enterprise for Itanium-based Systems
+	ENTERPRISE_SERVER_V                 = 0x00000026, // Server Enterprise without Hyper-V (full installation)
+	ESSENTIALBUSINESS_SERVER_ADDL       = 0x0000003C, // Windows Essential Server Solution Additional
+	ESSENTIALBUSINESS_SERVER_ADDLSVC    = 0x0000003E, // Windows Essential Server Solution Additional SVC
+	ESSENTIALBUSINESS_SERVER_MGMT       = 0x0000003B, // Windows Essential Server Solution Management
+	ESSENTIALBUSINESS_SERVER_MGMTSVC    = 0x0000003D, // Windows Essential Server Solution Management SVC
+	HOME_BASIC                          = 0x00000002, // Home Basic
+	HOME_BASIC_E                        = 0x00000043, // Not supported
+	HOME_BASIC_N                        = 0x00000005, // Home Basic N
+	HOME_PREMIUM                        = 0x00000003, // Home Premium
+	HOME_PREMIUM_E                      = 0x00000044, // Not supported
+	HOME_PREMIUM_N                      = 0x0000001A, // Home Premium N
+	HOME_PREMIUM_SERVER                 = 0x00000022, // Windows Home Server 2011
+	HOME_SERVER                         = 0x00000013, // Windows Storage Server 2008 R2 Essentials
+	HYPERV                              = 0x0000002A, // Microsoft Hyper-V Server
+	IOTENTERPRISE                       = 0x000000BC, // Windows IoT Enterprise
+	IOTENTERPRISE_S                     = 0x000000BF, // Windows IoT Enterprise LTSC
+	IOTUAP                              = 0x0000007B, // Windows 10 IoT Core
+	IOTUAPCOMMERCIAL                    = 0x00000083, // Windows 10 IoT Core Commercial
+	MEDIUMBUSINESS_SERVER_MANAGEMENT    = 0x0000001E, // Windows Essential Business Server Management Server
+	MEDIUMBUSINESS_SERVER_MESSAGING     = 0x00000020, // Windows Essential Business Server Messaging Server
+	MEDIUMBUSINESS_SERVER_SECURITY      = 0x0000001F, // Windows Essential Business Server Security Server
+	MOBILE_CORE                         = 0x00000068, // Windows 10 Mobile
+	MOBILE_ENTERPRISE                   = 0x00000085, // Windows 10 Mobile Enterprise
+	MULTIPOINT_PREMIUM_SERVER           = 0x0000004D, // Windows MultiPoint Server Premium (full installation)
+	MULTIPOINT_STANDARD_SERVER          = 0x0000004C, // Windows MultiPoint Server Standard (full installation)
+	PRO_WORKSTATION                     = 0x000000A1, // Windows 10 Pro for Workstations
+	PRO_WORKSTATION_N                   = 0x000000A2, // Windows 10 Pro for Workstations N
+	PROFESSIONAL                        = 0x00000030, // Windows 10 Pro
+	PROFESSIONAL_E                      = 0x00000045, // Not supported
+	PROFESSIONAL_N                      = 0x00000031, // Windows 10 Pro N
+	PROFESSIONAL_WMC                    = 0x00000067, // Professional with Media Center
+	SB_SOLUTION_SERVER                  = 0x00000032, // Windows Small Business Server 2011 Essentials
+	SB_SOLUTION_SERVER_EM               = 0x00000036, // Server For SB Solutions EM
+	SERVER_FOR_SB_SOLUTIONS             = 0x00000033, // Server For SB Solutions
+	SERVER_FOR_SB_SOLUTIONS_EM          = 0x00000037, // Server For SB Solutions EM
+	SERVER_FOR_SMALLBUSINESS            = 0x00000018, // Windows Server 2008 for Windows Essential Server Solutions
+	SERVER_FOR_SMALLBUSINESS_V          = 0x00000023, // Windows Server 2008 without Hyper-V for Windows Essential Server Solutions
+	SERVER_FOUNDATION                   = 0x00000021, // Server Foundation
+	SMALLBUSINESS_SERVER                = 0x00000009, // Windows Small Business Server
+	SMALLBUSINESS_SERVER_PREMIUM        = 0x00000019, // Small Business Server Premium
+	SMALLBUSINESS_SERVER_PREMIUM_CORE   = 0x0000003F, // Small Business Server Premium (core installation)
+	SOLUTION_EMBEDDEDSERVER             = 0x00000038, // Windows MultiPoint Server
+	STANDARD_EVALUATION_SERVER          = 0x0000004F, // Server Standard (evaluation installation)
+	STANDARD_SERVER                     = 0x00000007, // Server Standard (full installation. For Server Core installations of Windows Server 2012 and later, use the method, Determining whether Server Core is running.)
+	STANDARD_SERVER_CORE                = 0x0000000D, // Server Standard (core installation, Windows Server 2008 R2 and earlier)
+	STANDARD_SERVER_CORE_V              = 0x00000028, // Server Standard without Hyper-V (core installation)
+	STANDARD_SERVER_V                   = 0x00000024, // Server Standard without Hyper-V
+	STANDARD_SERVER_SOLUTIONS           = 0x00000034, // Server Solutions Premium
+	STANDARD_SERVER_SOLUTIONS_CORE      = 0x00000035, // Server Solutions Premium (core installation)
+	STARTER                             = 0x0000000B, // Starter
+	STARTER_E                           = 0x00000042, // Not supported
+	STARTER_N                           = 0x0000002F, // Starter N
+	STORAGE_ENTERPRISE_SERVER           = 0x00000017, // Storage Server Enterprise
+	STORAGE_ENTERPRISE_SERVER_CORE      = 0x0000002E, // Storage Server Enterprise (core installation)
+	STORAGE_EXPRESS_SERVER              = 0x00000014, // Storage Server Express
+	STORAGE_EXPRESS_SERVER_CORE         = 0x0000002B, // Storage Server Express (core installation)
+	STORAGE_STANDARD_EVALUATION_SERVER  = 0x00000060, // Storage Server Standard (evaluation installation)
+	STORAGE_STANDARD_SERVER             = 0x00000015, // Storage Server Standard
+	STORAGE_STANDARD_SERVER_CORE        = 0x0000002C, // Storage Server Standard (core installation)
+	STORAGE_WORKGROUP_EVALUATION_SERVER = 0x0000005F, // Storage Server Workgroup (evaluation installation)
+	STORAGE_WORKGROUP_SERVER            = 0x00000016, // Storage Server Workgroup
+	STORAGE_WORKGROUP_SERVER_CORE       = 0x0000002D, // Storage Server Workgroup (core installation)
+	ULTIMATE                            = 0x00000001, // Ultimate
+	ULTIMATE_E                          = 0x00000047, // Not supported
+	ULTIMATE_N                          = 0x0000001C, // Ultimate N
+	UNDEFINED                           = 0x00000000, // An unknown product
+	WEB_SERVER                          = 0x00000011, // Web Server (full installation)
+	WEB_SERVER_CORE                     = 0x0000001D, // Web Server (core installation)
+}
+
+ENABLE_ECHO_INPUT : DWORD : 0x0004
+ENABLE_INSERT_MODE : DWORD : 0x0020
+ENABLE_LINE_INPUT : DWORD : 0x0002
+ENABLE_MOUSE_INPUT : DWORD : 0x0010
+ENABLE_PROCESSED_INPUT : DWORD : 0x0001
+ENABLE_QUICK_EDIT_MODE : DWORD : 0x0040
+ENABLE_WINDOW_INPUT : DWORD : 0x0008
+ENABLE_VIRTUAL_TERMINAL_INPUT : DWORD : 0x0200
+ENABLE_PROCESSED_OUTPUT : DWORD : 0x0001
+ENABLE_WRAP_AT_EOL_OUTPUT : DWORD : 0x0002
+ENABLE_VIRTUAL_TERMINAL_PROCESSING : DWORD : 0x0004
+DISABLE_NEWLINE_AUTO_RETURN : DWORD : 0x0008
+ENABLE_LVB_GRID_WORLDWIDE : DWORD : 0x0010
+
+CTRL_C_EVENT : DWORD : 0
+CTRL_BREAK_EVENT : DWORD : 1
+CTRL_CLOSE_EVENT : DWORD : 2
+CTRL_LOGOFF_EVENT : DWORD : 5
+CTRL_SHUTDOWN_EVENT : DWORD : 6
+
+COORD :: struct {
+	X: SHORT,
+	Y: SHORT,
+}

+ 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

+ 3 - 3
core/sys/windows/util.odin

@@ -73,10 +73,10 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
 
 
 	// If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated
 	// If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated
 	// and will scan it to find the first null terminated character. The resulting string will
 	// and will scan it to find the first null terminated character. The resulting string will
-	// also null terminated.
+	// also be null terminated.
 	// If N != -1 it assumes the wide string is not null terminated and the resulting string
 	// If N != -1 it assumes the wide string is not null terminated and the resulting string
-	// will not be null terminated, we therefore have to force it to be null terminated manually.
-	text := make([]byte, n+1 if N != -1 else n) or_return
+	// will not be null terminated.
+	text := make([]byte, n) or_return
 
 
 	n1 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(text), n, nil, nil)
 	n1 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(text), n, nil, nil)
 	if n1 == 0 {
 	if n1 == 0 {

+ 2 - 0
core/sys/windows/winerror.odin

@@ -42,6 +42,8 @@ ERROR_TIMEOUT                : DWORD : 1460
 ERROR_DATATYPE_MISMATCH      : DWORD : 1629
 ERROR_DATATYPE_MISMATCH      : DWORD : 1629
 ERROR_UNSUPPORTED_TYPE       : DWORD : 1630
 ERROR_UNSUPPORTED_TYPE       : DWORD : 1630
 ERROR_NOT_SAME_OBJECT        : DWORD : 1656
 ERROR_NOT_SAME_OBJECT        : DWORD : 1656
+ERROR_PIPE_CONNECTED         : DWORD : 0x80070217
+ERROR_PIPE_BUSY              : DWORD : 231
 
 
 E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001
 E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001
 
 

+ 3 - 0
examples/all/all_main.odin

@@ -109,6 +109,8 @@ import i18n           "core:text/i18n"
 import thread         "core:thread"
 import thread         "core:thread"
 import time           "core:time"
 import time           "core:time"
 
 
+import sysinfo        "core:sys/info"
+
 import unicode        "core:unicode"
 import unicode        "core:unicode"
 import utf8           "core:unicode/utf8"
 import utf8           "core:unicode/utf8"
 import utf8string     "core:unicode/utf8/utf8string"
 import utf8string     "core:unicode/utf8/utf8string"
@@ -206,6 +208,7 @@ _ :: scanner
 _ :: i18n
 _ :: i18n
 _ :: thread
 _ :: thread
 _ :: time
 _ :: time
+_ :: sysinfo
 _ :: unicode
 _ :: unicode
 _ :: utf8
 _ :: utf8
 _ :: utf8string
 _ :: utf8string

File diff suppressed because it is too large
+ 639 - 313
src/bug_report.cpp


+ 112 - 63
src/build_settings.cpp

@@ -116,6 +116,7 @@ struct TargetMetrics {
 	TargetArchKind arch;
 	TargetArchKind arch;
 	isize          word_size;
 	isize          word_size;
 	isize          max_align;
 	isize          max_align;
+	isize          max_simd_align;
 	String         target_triplet;
 	String         target_triplet;
 	String         target_data_layout;
 	String         target_data_layout;
 	TargetABIKind  abi;
 	TargetABIKind  abi;
@@ -224,6 +225,7 @@ struct BuildContext {
 	String ODIN_VENDOR;  // compiler vendor
 	String ODIN_VENDOR;  // compiler vendor
 	String ODIN_VERSION; // compiler version
 	String ODIN_VERSION; // compiler version
 	String ODIN_ROOT;    // Odin ROOT
 	String ODIN_ROOT;    // Odin ROOT
+	String ODIN_BUILD_PROJECT_NAME; // Odin main/initial package's directory name
 	bool   ODIN_DEBUG;   // Odin in debug mode
 	bool   ODIN_DEBUG;   // Odin in debug mode
 	bool   ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
 	bool   ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
 	bool   ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
 	bool   ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
@@ -235,8 +237,9 @@ struct BuildContext {
 	TargetEndianKind endian_kind;
 	TargetEndianKind endian_kind;
 
 
 	// In bytes
 	// In bytes
-	i64    word_size; // Size of a pointer, must be >= 4
-	i64    max_align; // max alignment, must be >= 1 (and typically >= word_size)
+	i64    word_size;      // Size of a pointer, must be >= 4
+	i64    max_align;      // max alignment, must be >= 1 (and typically >= word_size)
+	i64    max_simd_align; // max alignment, must be >= 1 (and typically >= word_size)
 
 
 	CommandKind command_kind;
 	CommandKind command_kind;
 	String command;
 	String command;
@@ -339,15 +342,13 @@ bool global_ignore_warnings(void) {
 gb_global TargetMetrics target_windows_i386 = {
 gb_global TargetMetrics target_windows_i386 = {
 	TargetOs_windows,
 	TargetOs_windows,
 	TargetArch_i386,
 	TargetArch_i386,
-	4,
-	8,
+	4, 4, 8,
 	str_lit("i386-pc-windows-msvc"),
 	str_lit("i386-pc-windows-msvc"),
 };
 };
 gb_global TargetMetrics target_windows_amd64 = {
 gb_global TargetMetrics target_windows_amd64 = {
 	TargetOs_windows,
 	TargetOs_windows,
 	TargetArch_amd64,
 	TargetArch_amd64,
-	8,
-	16,
+	8, 8, 16,
 	str_lit("x86_64-pc-windows-msvc"),
 	str_lit("x86_64-pc-windows-msvc"),
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 };
 };
@@ -355,24 +356,21 @@ gb_global TargetMetrics target_windows_amd64 = {
 gb_global TargetMetrics target_linux_i386 = {
 gb_global TargetMetrics target_linux_i386 = {
 	TargetOs_linux,
 	TargetOs_linux,
 	TargetArch_i386,
 	TargetArch_i386,
-	4,
-	8,
+	4, 4, 8,
 	str_lit("i386-pc-linux-gnu"),
 	str_lit("i386-pc-linux-gnu"),
 
 
 };
 };
 gb_global TargetMetrics target_linux_amd64 = {
 gb_global TargetMetrics target_linux_amd64 = {
 	TargetOs_linux,
 	TargetOs_linux,
 	TargetArch_amd64,
 	TargetArch_amd64,
-	8,
-	16,
+	8, 8, 16,
 	str_lit("x86_64-pc-linux-gnu"),
 	str_lit("x86_64-pc-linux-gnu"),
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 };
 };
 gb_global TargetMetrics target_linux_arm64 = {
 gb_global TargetMetrics target_linux_arm64 = {
 	TargetOs_linux,
 	TargetOs_linux,
 	TargetArch_arm64,
 	TargetArch_arm64,
-	8,
-	16,
+	8, 8, 16,
 	str_lit("aarch64-linux-elf"),
 	str_lit("aarch64-linux-elf"),
 	str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
 	str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
 };
 };
@@ -380,8 +378,7 @@ gb_global TargetMetrics target_linux_arm64 = {
 gb_global TargetMetrics target_linux_arm32 = {
 gb_global TargetMetrics target_linux_arm32 = {
 	TargetOs_linux,
 	TargetOs_linux,
 	TargetArch_arm32,
 	TargetArch_arm32,
-	4,
-	8,
+	4, 4, 8,
 	str_lit("arm-linux-gnu"),
 	str_lit("arm-linux-gnu"),
 	str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
 	str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
 };
 };
@@ -389,8 +386,7 @@ gb_global TargetMetrics target_linux_arm32 = {
 gb_global TargetMetrics target_darwin_amd64 = {
 gb_global TargetMetrics target_darwin_amd64 = {
 	TargetOs_darwin,
 	TargetOs_darwin,
 	TargetArch_amd64,
 	TargetArch_amd64,
-	8,
-	16,
+	8, 8, 16,
 	str_lit("x86_64-apple-darwin"),
 	str_lit("x86_64-apple-darwin"),
 	str_lit("e-m:o-i64:64-f80:128-n8:16:32:64-S128"),
 	str_lit("e-m:o-i64:64-f80:128-n8:16:32:64-S128"),
 };
 };
@@ -398,8 +394,7 @@ gb_global TargetMetrics target_darwin_amd64 = {
 gb_global TargetMetrics target_darwin_arm64 = {
 gb_global TargetMetrics target_darwin_arm64 = {
 	TargetOs_darwin,
 	TargetOs_darwin,
 	TargetArch_arm64,
 	TargetArch_arm64,
-	8,
-	16,
+	8, 8, 16,
 	str_lit("arm64-apple-macosx11.0.0"),
 	str_lit("arm64-apple-macosx11.0.0"),
 	str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
 	str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
 };
 };
@@ -407,16 +402,14 @@ gb_global TargetMetrics target_darwin_arm64 = {
 gb_global TargetMetrics target_freebsd_i386 = {
 gb_global TargetMetrics target_freebsd_i386 = {
 	TargetOs_freebsd,
 	TargetOs_freebsd,
 	TargetArch_i386,
 	TargetArch_i386,
-	4,
-	8,
+	4, 4, 8,
 	str_lit("i386-unknown-freebsd-elf"),
 	str_lit("i386-unknown-freebsd-elf"),
 };
 };
 
 
 gb_global TargetMetrics target_freebsd_amd64 = {
 gb_global TargetMetrics target_freebsd_amd64 = {
 	TargetOs_freebsd,
 	TargetOs_freebsd,
 	TargetArch_amd64,
 	TargetArch_amd64,
-	8,
-	16,
+	8, 8, 16,
 	str_lit("x86_64-unknown-freebsd-elf"),
 	str_lit("x86_64-unknown-freebsd-elf"),
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 };
 };
@@ -424,8 +417,7 @@ gb_global TargetMetrics target_freebsd_amd64 = {
 gb_global TargetMetrics target_openbsd_amd64 = {
 gb_global TargetMetrics target_openbsd_amd64 = {
 	TargetOs_openbsd,
 	TargetOs_openbsd,
 	TargetArch_amd64,
 	TargetArch_amd64,
-	8,
-	16,
+	8, 8, 16,
 	str_lit("x86_64-unknown-openbsd-elf"),
 	str_lit("x86_64-unknown-openbsd-elf"),
 	str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
 	str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
 };
 };
@@ -433,62 +425,48 @@ gb_global TargetMetrics target_openbsd_amd64 = {
 gb_global TargetMetrics target_essence_amd64 = {
 gb_global TargetMetrics target_essence_amd64 = {
 	TargetOs_essence,
 	TargetOs_essence,
 	TargetArch_amd64,
 	TargetArch_amd64,
-	8,
-	16,
+	8, 8, 16,
 	str_lit("x86_64-pc-none-elf"),
 	str_lit("x86_64-pc-none-elf"),
 };
 };
 
 
+
 gb_global TargetMetrics target_freestanding_wasm32 = {
 gb_global TargetMetrics target_freestanding_wasm32 = {
 	TargetOs_freestanding,
 	TargetOs_freestanding,
 	TargetArch_wasm32,
 	TargetArch_wasm32,
-	4,
-	8,
+	4, 8, 16,
 	str_lit("wasm32-freestanding-js"),
 	str_lit("wasm32-freestanding-js"),
-	str_lit(""),
+	str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
 };
 };
 
 
 gb_global TargetMetrics target_js_wasm32 = {
 gb_global TargetMetrics target_js_wasm32 = {
 	TargetOs_js,
 	TargetOs_js,
 	TargetArch_wasm32,
 	TargetArch_wasm32,
-	4,
-	8,
+	4, 8, 16,
 	str_lit("wasm32-js-js"),
 	str_lit("wasm32-js-js"),
-	str_lit(""),
-};
-
-gb_global TargetMetrics target_js_wasm64 = {
-	TargetOs_js,
-	TargetArch_wasm64,
-	8,
-	16,
-	str_lit("wasm64-js-js"),
-	str_lit(""),
+	str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
 };
 };
 
 
 gb_global TargetMetrics target_wasi_wasm32 = {
 gb_global TargetMetrics target_wasi_wasm32 = {
 	TargetOs_wasi,
 	TargetOs_wasi,
 	TargetArch_wasm32,
 	TargetArch_wasm32,
-	4,
-	8,
+	4, 8, 16,
 	str_lit("wasm32-wasi-js"),
 	str_lit("wasm32-wasi-js"),
-	str_lit(""),
+	str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
 };
 };
 
 
 
 
-// gb_global TargetMetrics target_freestanding_wasm64 = {
-// 	TargetOs_freestanding,
-// 	TargetArch_wasm64,
-// 	8,
-// 	16,
-// 	str_lit("wasm64-freestanding-js"),
-// 	str_lit(""),
-// };
+gb_global TargetMetrics target_js_wasm64 = {
+	TargetOs_js,
+	TargetArch_wasm64,
+	8, 8, 16,
+	str_lit("wasm64-js-js"),
+	str_lit(""),
+};
 
 
 gb_global TargetMetrics target_freestanding_amd64_sysv = {
 gb_global TargetMetrics target_freestanding_amd64_sysv = {
 	TargetOs_freestanding,
 	TargetOs_freestanding,
 	TargetArch_amd64,
 	TargetArch_amd64,
-	8,
-	16,
+	8, 8, 16,
 	str_lit("x86_64-pc-none-gnu"),
 	str_lit("x86_64-pc-none-gnu"),
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 	TargetABI_SysV,
 	TargetABI_SysV,
@@ -517,7 +495,7 @@ gb_global NamedTargetMetrics named_targets[] = {
 	{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
 	{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
 	{ str_lit("wasi_wasm32"),         &target_wasi_wasm32 },
 	{ str_lit("wasi_wasm32"),         &target_wasi_wasm32 },
 	{ str_lit("js_wasm32"),           &target_js_wasm32 },
 	{ str_lit("js_wasm32"),           &target_js_wasm32 },
-	{ str_lit("js_wasm64"),           &target_js_wasm64 },
+	// { str_lit("js_wasm64"),           &target_js_wasm64 },
 
 
 	{ str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv },
 	{ str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv },
 };
 };
@@ -853,10 +831,73 @@ String internal_odin_root_dir(void) {
 			gb_mfree(argv);
 			gb_mfree(argv);
 			return make_string(nullptr, 0);
 			return make_string(nullptr, 0);
 		}
 		}
-		// copy argv[0] to path_buf
+
+		// NOTE(Jeroen):
+		// On OpenBSD, if `odin` is on the path, `argv[0]` will contain just `odin`,
+		// even though that isn't then the relative path.
+		// When run from Odin's directory, it returns `./odin`.
+		// Check argv[0] for lack of / to see if it's a reference to PATH.
+		// If so, walk PATH to find the executable.
+
 		len = gb_strlen(argv[0]);
 		len = gb_strlen(argv[0]);
-		if(len < path_buf.count) {
-			gb_memmove(&path_buf[0], argv[0], len);
+
+		bool slash_found = false;
+		bool odin_found  = false;
+
+		for (int i = 0; i < len; i += 1) {
+			if (argv[0][i] == '/') {
+				slash_found = true;
+				break;
+			}
+		}
+
+		if (slash_found) {
+			// copy argv[0] to path_buf
+			if(len < path_buf.count) {
+				gb_memmove(&path_buf[0], argv[0], len);
+				odin_found = true;
+			}
+		} else {
+			gbAllocator a = heap_allocator();
+			char const *path_env = gb_get_env("PATH", a);
+			defer (gb_free(a, cast(void *)path_env));
+
+			if (path_env) {
+				int path_len = gb_strlen(path_env);
+				int path_start = 0;
+				int path_end   = 0;
+
+				for (; path_start < path_len; ) {
+					for (; path_end <= path_len; path_end++) {
+						if (path_env[path_end] == ':' || path_end == path_len) {
+							break;
+						}
+					}
+					String path_needle    = (const String)make_string((const u8 *)(path_env + path_start), path_end - path_start);
+					String argv0          = (const String)make_string((const u8 *)argv[0], len);
+					String odin_candidate = concatenate3_strings(a, path_needle, STR_LIT("/"), argv0);
+					defer (gb_free(a, odin_candidate.text));
+
+					if (gb_file_exists((const char *)odin_candidate.text)) {
+						len = odin_candidate.len;
+						if(len < path_buf.count) {
+							gb_memmove(&path_buf[0], odin_candidate.text, len);
+						}
+						odin_found = true;
+						break;
+					}
+
+					path_start = path_end + 1;
+					path_end   = path_start;
+					if (path_start > path_len) {
+						break;
+					}
+				}
+			}
+
+			if (!odin_found) {
+				gb_printf_err("Odin could not locate itself in PATH, and ODIN_ROOT wasn't set either.\n");
+			}
 		}
 		}
 		gb_mfree(argv);
 		gb_mfree(argv);
 #endif
 #endif
@@ -1084,14 +1125,16 @@ void init_build_context(TargetMetrics *cross_target) {
 	GB_ASSERT(metrics->arch != TargetArch_Invalid);
 	GB_ASSERT(metrics->arch != TargetArch_Invalid);
 	GB_ASSERT(metrics->word_size > 1);
 	GB_ASSERT(metrics->word_size > 1);
 	GB_ASSERT(metrics->max_align > 1);
 	GB_ASSERT(metrics->max_align > 1);
+	GB_ASSERT(metrics->max_simd_align > 1);
 
 
 
 
 	bc->metrics = *metrics;
 	bc->metrics = *metrics;
-	bc->ODIN_OS     = target_os_names[metrics->os];
-	bc->ODIN_ARCH   = target_arch_names[metrics->arch];
-	bc->endian_kind = target_endians[metrics->arch];
-	bc->word_size   = metrics->word_size;
-	bc->max_align   = metrics->max_align;
+	bc->ODIN_OS        = target_os_names[metrics->os];
+	bc->ODIN_ARCH      = target_arch_names[metrics->arch];
+	bc->endian_kind    = target_endians[metrics->arch];
+	bc->word_size      = metrics->word_size;
+	bc->max_align      = metrics->max_align;
+	bc->max_simd_align = metrics->max_simd_align;
 	bc->link_flags  = str_lit(" ");
 	bc->link_flags  = str_lit(" ");
 
 
 	#if defined(DEFAULT_TO_THREADED_CHECKER)
 	#if defined(DEFAULT_TO_THREADED_CHECKER)
@@ -1495,6 +1538,12 @@ bool init_build_paths(String init_filename) {
 		enable_target_feature({}, bc->target_features_string);
 		enable_target_feature({}, bc->target_features_string);
 	}
 	}
 
 
+	{
+		String build_project_name  = last_path_element(bc->build_paths[BuildPath_Main_Package].basename);
+		GB_ASSERT(build_project_name.len > 0);
+		bc->ODIN_BUILD_PROJECT_NAME = build_project_name;
+	}
+
 	return true;
 	return true;
 }
 }
 
 

+ 50 - 5
src/check_builtin.cpp

@@ -341,6 +341,13 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call
 		for (isize i = 2+arg_offset; i < ce->args.count; i++) {
 		for (isize i = 2+arg_offset; i < ce->args.count; i++) {
 			Operand x = {};
 			Operand x = {};
 			check_expr(c, &x, ce->args[i]);
 			check_expr(c, &x, ce->args[i]);
+			if (is_type_untyped(x.type)) {
+				gbString e = expr_to_string(x.expr);
+				gbString t = type_to_string(x.type);
+				error(x.expr, "'%.*s' expects typed parameters, got %s of type %s", LIT(builtin_name), e, t);
+				gb_string_free(t);
+				gb_string_free(e);
+			}
 			param_types[i-arg_offset] = x.type;
 			param_types[i-arg_offset] = x.type;
 		}
 		}
 
 
@@ -4523,7 +4530,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				if (x.mode != Addressing_Invalid) {
 				if (x.mode != Addressing_Invalid) {
 					convert_to_typed(c, &x, t_uintptr);	
 					convert_to_typed(c, &x, t_uintptr);	
 				}
 				}
-				if (!is_type_uintptr(operand->type)) {
+				convert_to_typed(c, &x, t_uintptr);
+				if (!is_type_uintptr(x.type)) {
 					gbString t = type_to_string(x.type);
 					gbString t = type_to_string(x.type);
 					error(x.expr, "Argument %td must be of type 'uintptr', got %s", i, t);
 					error(x.expr, "Argument %td must be of type 'uintptr', got %s", i, t);
 					gb_string_free(t);
 					gb_string_free(t);
@@ -4605,6 +4613,46 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		operand->mode = Addressing_Type;
 		operand->mode = Addressing_Type;
 		break;
 		break;
 
 
+	case BuiltinProc_type_convert_variants_to_pointers:
+		if (operand->mode != Addressing_Type) {
+			error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+		} else {
+			Type *bt = base_type(operand->type);
+			if (is_type_polymorphic(bt)) {
+				// IGNORE polymorphic types
+				return true;
+			} else if (bt->kind != Type_Union) {
+				gbString t = type_to_string(operand->type);
+				error(operand->expr, "Expected a union type for '%.*s', got %s", LIT(builtin_name), t);
+				gb_string_free(t);
+
+				operand->mode = Addressing_Invalid;
+				operand->type = t_invalid;
+				return false;
+			} else if (bt->Union.is_polymorphic) {
+				gbString t = type_to_string(operand->type);
+				error(operand->expr, "Expected a non-polymorphic union type for '%.*s', got %s", LIT(builtin_name), t);
+				gb_string_free(t);
+
+				operand->mode = Addressing_Invalid;
+				operand->type = t_invalid;
+				return false;
+			}
+
+			Type *new_type = alloc_type_union();
+			auto variants = slice_make<Type *>(permanent_allocator(), bt->Union.variants.count);
+			for_array(i, bt->Union.variants) {
+				variants[i] = alloc_type_pointer(bt->Union.variants[i]);
+			}
+			new_type->Union.variants = variants;
+
+			// NOTE(bill): Is this even correct?
+			new_type->Union.scope = bt->Union.scope;
+
+			operand->type = new_type;
+		}
+		operand->mode = Addressing_Type;
+		break;
 
 
 	case BuiltinProc_type_is_boolean:
 	case BuiltinProc_type_is_boolean:
 	case BuiltinProc_type_is_integer:
 	case BuiltinProc_type_is_integer:
@@ -5390,10 +5438,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 
 	case BuiltinProc_valgrind_client_request:
 	case BuiltinProc_valgrind_client_request:
 		{
 		{
-			if (!is_arch_x86()) {
-				error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name));
-				return false;
-			}
+			// NOTE(bill): Check it but make it a no-op for non x86 (i386, amd64) targets
 
 
 			enum {ARG_COUNT = 7};
 			enum {ARG_COUNT = 7};
 			GB_ASSERT(builtin_procs[BuiltinProc_valgrind_client_request].arg_count == ARG_COUNT);
 			GB_ASSERT(builtin_procs[BuiltinProc_valgrind_client_request].arg_count == ARG_COUNT);

+ 18 - 2
src/check_expr.cpp

@@ -3039,8 +3039,8 @@ void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand
 		x->type = xt;
 		x->type = xt;
 		goto matrix_success;
 		goto matrix_success;
 	} else {
 	} else {
-		GB_ASSERT(is_type_matrix(yt));
 		GB_ASSERT(!is_type_matrix(xt));
 		GB_ASSERT(!is_type_matrix(xt));
+		GB_ASSERT(is_type_matrix(yt));
 		
 		
 		if (op.kind == Token_Mul) {
 		if (op.kind == Token_Mul) {
 			// NOTE(bill): no need to handle the matrix case here since it should be handled above
 			// NOTE(bill): no need to handle the matrix case here since it should be handled above
@@ -3061,6 +3061,9 @@ void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand
 					x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count);
 					x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count);
 				}
 				}
 				goto matrix_success;
 				goto matrix_success;
+			} else if (are_types_identical(yt->Matrix.elem, xt)) {
+				x->type = check_matrix_type_hint(y->type, type_hint);
+				return;
 			}
 			}
 		}
 		}
 		if (!are_types_identical(xt, yt)) {
 		if (!are_types_identical(xt, yt)) {
@@ -7359,6 +7362,14 @@ ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *t
 		return kind;
 		return kind;
 	}
 	}
 
 
+	if (x.mode == Addressing_Type || y.mode == Addressing_Type) {
+		Ast *type_expr = (x.mode == Addressing_Type) ? x.expr : y.expr;
+		gbString type_string = expr_to_string(type_expr);
+		error(node, "Type %s is invalid operand for ternary if expression", type_string);
+		gb_string_free(type_string);
+		return kind;
+	}
+
 	if (x.type == nullptr || x.type == t_invalid ||
 	if (x.type == nullptr || x.type == t_invalid ||
 	    y.type == nullptr || y.type == t_invalid) {
 	    y.type == nullptr || y.type == t_invalid) {
 		return kind;
 		return kind;
@@ -7395,6 +7406,7 @@ ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *t
 		    check_cast_internal(c, &y, type_hint)) {
 		    check_cast_internal(c, &y, type_hint)) {
 			convert_to_typed(c, o, type_hint);
 			convert_to_typed(c, o, type_hint);
 			update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
 			update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
+			o->type = type_hint;
 		}
 		}
 	}
 	}
 	return kind;
 	return kind;
@@ -8769,7 +8781,10 @@ ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type
 	Ast *first_arg = x.expr->SelectorExpr.expr;
 	Ast *first_arg = x.expr->SelectorExpr.expr;
 	GB_ASSERT(first_arg != nullptr);
 	GB_ASSERT(first_arg != nullptr);
 
 
-	first_arg->state_flags |= StateFlag_SelectorCallExpr;
+	Entity *e = entity_of_node(se->expr);
+	if (!(e != nullptr && (e->kind == Entity_Procedure || e->kind == Entity_ProcGroup))) {
+		first_arg->state_flags |= StateFlag_SelectorCallExpr;
+	}
 
 
 	Type *pt = base_type(x.type);
 	Type *pt = base_type(x.type);
 	GB_ASSERT(pt->kind == Type_Proc);
 	GB_ASSERT(pt->kind == Type_Proc);
@@ -9571,6 +9586,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 	case Ast_MapType:
 	case Ast_MapType:
 	case Ast_BitSetType:
 	case Ast_BitSetType:
 	case Ast_MatrixType:
 	case Ast_MatrixType:
+	case Ast_RelativeType:
 		o->mode = Addressing_Type;
 		o->mode = Addressing_Type;
 		o->type = check_type(c, node);
 		o->type = check_type(c, node);
 		break;
 		break;

+ 49 - 6
src/check_stmt.cpp

@@ -1396,6 +1396,23 @@ bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *id_) {
 	return id != BuiltinProc_Invalid;
 	return id != BuiltinProc_Invalid;
 }
 }
 
 
+bool check_expr_is_stack_variable(Ast *expr) {
+	expr = unparen_expr(expr);
+	Entity *e = entity_of_node(expr);
+	if (e && e->kind == Entity_Variable) {
+		if (e->flags & (EntityFlag_Static|EntityFlag_Using|EntityFlag_ImplicitReference|EntityFlag_ForValue)) {
+			// okay
+		} else if (e->Variable.thread_local_model.len != 0) {
+			// okay
+		} else if (e->scope) {
+			if ((e->scope->flags & (ScopeFlag_Global|ScopeFlag_File|ScopeFlag_Type)) == 0) {
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
 void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	switch (node->kind) {
 	switch (node->kind) {
@@ -1447,6 +1464,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 				AstSelectorCallExpr *se = &expr->SelectorCallExpr;
 				AstSelectorCallExpr *se = &expr->SelectorCallExpr;
 				ast_node(ce, CallExpr, se->call);
 				ast_node(ce, CallExpr, se->call);
 				Type *t = base_type(type_of_expr(ce->proc));
 				Type *t = base_type(type_of_expr(ce->proc));
+				if (t == nullptr) {
+					gbString expr_str = expr_to_string(ce->proc);
+					error(node, "'%s' is not a value field nor procedure", expr_str);
+					gb_string_free(expr_str);
+					return;
+				}
 				if (t->kind == Type_Proc) {
 				if (t->kind == Type_Proc) {
 					do_require = t->Proc.require_results;
 					do_require = t->Proc.require_results;
 				} else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
 				} else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
@@ -1678,6 +1701,29 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 				if (is_type_untyped(o->type)) {
 				if (is_type_untyped(o->type)) {
 					update_untyped_expr_type(ctx, o->expr, e->type, true);
 					update_untyped_expr_type(ctx, o->expr, e->type, true);
 				}
 				}
+
+
+				// NOTE(bill): This is very basic escape analysis
+				// This needs to be improved tremendously, and a lot of it done during the
+				// middle-end (or LLVM side) to improve checks and error messages
+				Ast *expr = unparen_expr(o->expr);
+				if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
+					Ast *x = unparen_expr(expr->UnaryExpr.expr);
+					if (x->kind == Ast_CompoundLit) {
+						error(expr, "Cannot return the address to a stack value from a procedure");
+					} else if (x->kind == Ast_IndexExpr) {
+						Ast *array = x->IndexExpr.expr;
+						if (is_type_array_like(type_of_expr(array)) && check_expr_is_stack_variable(array)) {
+							gbString t = type_to_string(type_of_expr(array));
+							error(expr, "Cannot return the address to an element of stack variable from a procedure, of type %s", t);
+							gb_string_free(t);
+						}
+					} else {
+						if (check_expr_is_stack_variable(x)) {
+							error(expr, "Cannot return the address to a stack variable from a procedure");
+						}
+					}
+				}
 			}
 			}
 		}
 		}
 	case_end;
 	case_end;
@@ -1895,13 +1941,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 				}
 				}
 				if (found == nullptr) {
 				if (found == nullptr) {
 					entity = alloc_entity_variable(ctx->scope, token, type, EntityState_Resolved);
 					entity = alloc_entity_variable(ctx->scope, token, type, EntityState_Resolved);
+					entity->flags |= EntityFlag_ForValue;
 					entity->flags |= EntityFlag_Value;
 					entity->flags |= EntityFlag_Value;
-					if (i == addressable_index) {
-						if (use_by_reference_for_value) {
-							entity->flags &= ~EntityFlag_Value;
-						} else {
-							entity->flags |= EntityFlag_ForValue;
-						}
+					if (i == addressable_index && use_by_reference_for_value) {
+						entity->flags &= ~EntityFlag_Value;
 					}
 					}
 					if (is_soa) {
 					if (is_soa) {
 						if (i == 0) {
 						if (i == 0) {

+ 4 - 2
src/check_type.cpp

@@ -1643,8 +1643,10 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 						bool valid = false;
 						bool valid = false;
 						if (is_type_proc(op.type)) {
 						if (is_type_proc(op.type)) {
 							Entity *proc_entity = entity_from_expr(op.expr);
 							Entity *proc_entity = entity_from_expr(op.expr);
-							valid = proc_entity != nullptr;
-							poly_const = exact_value_procedure(proc_entity->identifier.load() ? proc_entity->identifier.load() : op.expr);
+							valid = (proc_entity != nullptr) && (op.value.kind == ExactValue_Procedure);
+							if (valid) {
+								poly_const = exact_value_procedure(proc_entity->identifier.load() ? proc_entity->identifier.load() : op.expr);
+							}
 						}
 						}
 						if (!valid) {
 						if (!valid) {
 							if (op.mode == Addressing_Constant) {
 							if (op.mode == Addressing_Constant) {

+ 1 - 0
src/checker.cpp

@@ -938,6 +938,7 @@ void init_universal(void) {
 	add_global_string_constant("ODIN_VENDOR",  bc->ODIN_VENDOR);
 	add_global_string_constant("ODIN_VENDOR",  bc->ODIN_VENDOR);
 	add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
 	add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
 	add_global_string_constant("ODIN_ROOT",    bc->ODIN_ROOT);
 	add_global_string_constant("ODIN_ROOT",    bc->ODIN_ROOT);
+	add_global_string_constant("ODIN_BUILD_PROJECT_NAME", bc->ODIN_BUILD_PROJECT_NAME);
 
 
 	{
 	{
 		GlobalEnumValue values[TargetOs_COUNT] = {
 		GlobalEnumValue values[TargetOs_COUNT] = {

+ 3 - 0
src/checker_builtin_procs.hpp

@@ -200,6 +200,8 @@ BuiltinProc__type_begin,
 	BuiltinProc_type_core_type,
 	BuiltinProc_type_core_type,
 	BuiltinProc_type_elem_type,
 	BuiltinProc_type_elem_type,
 
 
+	BuiltinProc_type_convert_variants_to_pointers,
+
 BuiltinProc__type_simple_boolean_begin,
 BuiltinProc__type_simple_boolean_begin,
 	BuiltinProc_type_is_boolean,
 	BuiltinProc_type_is_boolean,
 	BuiltinProc_type_is_integer,
 	BuiltinProc_type_is_integer,
@@ -492,6 +494,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("type_base_type"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_base_type"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_core_type"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_core_type"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_elem_type"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_elem_type"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("type_convert_variants_to_pointers"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_boolean"),           1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_boolean"),           1, false, Expr_Expr, BuiltinProcPkg_intrinsics},

+ 4 - 0
src/common.cpp

@@ -38,6 +38,10 @@ i64 next_pow2(i64 n);
 isize next_pow2_isize(isize n);
 isize next_pow2_isize(isize n);
 void debugf(char const *fmt, ...);
 void debugf(char const *fmt, ...);
 
 
+#if defined(GB_SYSTEM_WINDOWS) && defined(GB_ARCH_32_BIT)
+#error Odin on Windows requires a 64-bit build-system. The 'Developer Command Prompt' for VS still defaults to 32-bit shell. The 64-bit shell can be found under the name 'x64 Native Tools Command Prompt' for VS. For more information, please see https://odin-lang.org/docs/install/#for-windows
+#endif
+
 #include "threading.cpp"
 #include "threading.cpp"
 #include "unicode.cpp"
 #include "unicode.cpp"
 #include "array.cpp"
 #include "array.cpp"

+ 1 - 1
src/llvm_abi.cpp

@@ -294,7 +294,7 @@ i64 lb_alignof(LLVMTypeRef type) {
 			i64 elem_size = lb_sizeof(elem);
 			i64 elem_size = lb_sizeof(elem);
 			i64 count = LLVMGetVectorSize(type);
 			i64 count = LLVMGetVectorSize(type);
 			i64 size = count * elem_size;
 			i64 size = count * elem_size;
-			return gb_clamp(next_pow2(size), 1, build_context.max_align);
+			return gb_clamp(next_pow2(size), 1, build_context.max_simd_align);
 		}
 		}
 
 
 	}
 	}

+ 2 - 1
src/llvm_backend.cpp

@@ -1420,7 +1420,7 @@ void lb_generate_code(lbGenerator *gen) {
 				}
 				}
 			}
 			}
 
 
-			LLVMBool split_debug_inlining = false;
+			LLVMBool split_debug_inlining = build_context.build_mode == BuildMode_Assembly;
 			LLVMBool debug_info_for_profiling = false;
 			LLVMBool debug_info_for_profiling = false;
 
 
 			m->debug_compile_unit = LLVMDIBuilderCreateCompileUnit(m->debug_builder, LLVMDWARFSourceLanguageC99,
 			m->debug_compile_unit = LLVMDIBuilderCreateCompileUnit(m->debug_builder, LLVMDWARFSourceLanguageC99,
@@ -1615,6 +1615,7 @@ void lb_generate_code(lbGenerator *gen) {
 		}
 		}
 		if (is_foreign) {
 		if (is_foreign) {
 			LLVMSetLinkage(g.value, LLVMExternalLinkage);
 			LLVMSetLinkage(g.value, LLVMExternalLinkage);
+			LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass);
 			LLVMSetExternallyInitialized(g.value, true);
 			LLVMSetExternallyInitialized(g.value, true);
 			lb_add_foreign_library_path(m, e->Variable.foreign_library);
 			lb_add_foreign_library_path(m, e->Variable.foreign_library);
 			
 			

+ 4 - 0
src/llvm_backend_const.cpp

@@ -297,12 +297,16 @@ LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_
 	}
 	}
 
 
 	if (!is_const) {
 	if (!is_const) {
+		LLVMTypeRef llvm_elem_type = lb_type(m, elem_type);
 		lbProcedure *p = m->curr_procedure;
 		lbProcedure *p = m->curr_procedure;
 		GB_ASSERT(p != nullptr);
 		GB_ASSERT(p != nullptr);
 		lbAddr v = lb_add_local_generated(p, type, false);
 		lbAddr v = lb_add_local_generated(p, type, false);
 		lbValue ptr = lb_addr_get_ptr(p, v);
 		lbValue ptr = lb_addr_get_ptr(p, v);
 		for (isize i = 0; i < count; i++) {
 		for (isize i = 0; i < count; i++) {
 			lbValue elem = lb_emit_array_epi(p, ptr, i);
 			lbValue elem = lb_emit_array_epi(p, ptr, i);
+			if (is_type_proc(elem_type)) {
+				values[i] = LLVMConstPointerCast(values[i], llvm_elem_type);
+			}
 			LLVMBuildStore(p->builder, values[i], elem.value);
 			LLVMBuildStore(p->builder, values[i], elem.value);
 		}
 		}
 		return lb_addr_load(p, v).value;
 		return lb_addr_load(p, v).value;

+ 2 - 1
src/llvm_backend_debug.cpp

@@ -1042,7 +1042,8 @@ void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
 	// NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block
 	// NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block
 	// The reason is that if the parameter is at index 0 and a pointer, there is not such things as an
 	// The reason is that if the parameter is at index 0 and a pointer, there is not such things as an
 	// instruction "before" it.
 	// instruction "before" it.
-	LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block);
+	LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block);
+	// LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block);
 }
 }
 
 
 
 

+ 22 - 12
src/llvm_backend_expr.cpp

@@ -1,4 +1,4 @@
-lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise=false);
+lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise);
 
 
 lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type) {
 lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type) {
 	lbModule *m = p->module;
 	lbModule *m = p->module;
@@ -137,7 +137,7 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type)
 		lbAddr res_addr = lb_add_local(p, type, nullptr, false, 0, true);
 		lbAddr res_addr = lb_add_local(p, type, nullptr, false, 0, true);
 		lbValue res = lb_addr_get_ptr(p, res_addr);
 		lbValue res = lb_addr_get_ptr(p, res_addr);
 
 
-		bool inline_array_arith = type_size_of(type) <= build_context.max_align;
+		bool inline_array_arith = lb_can_try_to_inline_array_arith(type);
 
 
 		i32 count = cast(i32)get_array_type_count(tl);
 		i32 count = cast(i32)get_array_type_count(tl);
 
 
@@ -436,7 +436,7 @@ lbValue lb_emit_arith_array(lbProcedure *p, TokenKind op, lbValue lhs, lbValue r
 		return direct_vector_res;
 		return direct_vector_res;
 	}
 	}
 
 
-	bool inline_array_arith = type_size_of(type) <= build_context.max_align;
+	bool inline_array_arith = lb_can_try_to_inline_array_arith(type);
 	if (inline_array_arith) {
 	if (inline_array_arith) {
 
 
 		auto dst_ptrs = slice_make<lbValue>(temporary_allocator(), n);
 		auto dst_ptrs = slice_make<lbValue>(temporary_allocator(), n);
@@ -987,7 +987,6 @@ lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type
 lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise) {
 lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise) {
 	GB_ASSERT(is_type_matrix(lhs.type) || is_type_matrix(rhs.type));
 	GB_ASSERT(is_type_matrix(lhs.type) || is_type_matrix(rhs.type));
 
 
-
 	if (op == Token_Mul && !component_wise) {
 	if (op == Token_Mul && !component_wise) {
 		Type *xt = base_type(lhs.type);
 		Type *xt = base_type(lhs.type);
 		Type *yt = base_type(rhs.type);
 		Type *yt = base_type(rhs.type);
@@ -1001,8 +1000,22 @@ lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue
 		} else if (is_type_array_like(xt)) {
 		} else if (is_type_array_like(xt)) {
 			GB_ASSERT(yt->kind == Type_Matrix);
 			GB_ASSERT(yt->kind == Type_Matrix);
 			return lb_emit_vector_mul_matrix(p, lhs, rhs, type);
 			return lb_emit_vector_mul_matrix(p, lhs, rhs, type);
-		}
+		} else {
+			GB_ASSERT(xt->kind == Type_Basic);
+			GB_ASSERT(yt->kind == Type_Matrix);
+			GB_ASSERT(is_type_matrix(type));
+
+			Type *array_type = alloc_type_array(yt->Matrix.elem, matrix_type_total_internal_elems(yt));
+			GB_ASSERT(type_size_of(array_type) == type_size_of(yt));
+
+			lbValue array_lhs = lb_emit_conv(p, lhs, array_type);
+			lbValue array_rhs = rhs;
+			array_rhs.type = array_type;
 
 
+			lbValue array = lb_emit_arith(p, op, array_lhs, array_rhs, array_type);
+			array.type = type;
+			return array;
+		}
 	} else {
 	} else {
 		if (is_type_matrix(lhs.type)) {
 		if (is_type_matrix(lhs.type)) {
 			rhs = lb_emit_conv(p, rhs, lhs.type);
 			rhs = lb_emit_conv(p, rhs, lhs.type);
@@ -1047,7 +1060,7 @@ lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Ty
 	if (is_type_array_like(lhs.type) || is_type_array_like(rhs.type)) {
 	if (is_type_array_like(lhs.type) || is_type_array_like(rhs.type)) {
 		return lb_emit_arith_array(p, op, lhs, rhs, type);
 		return lb_emit_arith_array(p, op, lhs, rhs, type);
 	} else if (is_type_matrix(lhs.type) || is_type_matrix(rhs.type)) {
 	} else if (is_type_matrix(lhs.type) || is_type_matrix(rhs.type)) {
-		return lb_emit_arith_matrix(p, op, lhs, rhs, type);
+		return lb_emit_arith_matrix(p, op, lhs, rhs, type, false);
 	} else if (is_type_complex(type)) {
 	} else if (is_type_complex(type)) {
 		lhs = lb_emit_conv(p, lhs, type);
 		lhs = lb_emit_conv(p, lhs, type);
 		rhs = lb_emit_conv(p, rhs, type);
 		rhs = lb_emit_conv(p, rhs, type);
@@ -1320,7 +1333,7 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
 	if (is_type_matrix(be->left->tav.type) || is_type_matrix(be->right->tav.type)) {
 	if (is_type_matrix(be->left->tav.type) || is_type_matrix(be->right->tav.type)) {
 		lbValue left = lb_build_expr(p, be->left);
 		lbValue left = lb_build_expr(p, be->left);
 		lbValue right = lb_build_expr(p, be->right);
 		lbValue right = lb_build_expr(p, be->right);
-		return lb_emit_arith_matrix(p, be->op.kind, left, right, default_type(tv.type));
+		return lb_emit_arith_matrix(p, be->op.kind, left, right, default_type(tv.type), false);
 	}
 	}
 
 
 
 
@@ -1777,9 +1790,6 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 			lbValue res = {};
 			lbValue res = {};
 			res = lb_emit_conv(p, value, platform_src_type);
 			res = lb_emit_conv(p, value, platform_src_type);
 			res = lb_emit_conv(p, res, platform_dst_type);
 			res = lb_emit_conv(p, res, platform_dst_type);
-			if (is_type_different_to_arch_endianness(dst)) {
-				res = lb_emit_byte_swap(p, res, platform_dst_type);
-			}
 			return lb_emit_conv(p, res, t);
 			return lb_emit_conv(p, res, t);
 		}
 		}
 
 
@@ -1927,7 +1937,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 		}
 		}
 		if (dst->Union.variants.count == 1) {
 		if (dst->Union.variants.count == 1) {
 			Type *vt = dst->Union.variants[0];
 			Type *vt = dst->Union.variants[0];
-			if (internal_check_is_assignable_to(src, vt)) {
+			if (internal_check_is_assignable_to(src_type, vt)) {
 				value = lb_emit_conv(p, value, vt);
 				value = lb_emit_conv(p, value, vt);
 				lbAddr parent = lb_add_local_generated(p, t, true);
 				lbAddr parent = lb_add_local_generated(p, t, true);
 				lb_emit_store_union_variant(p, parent.addr, value, vt);
 				lb_emit_store_union_variant(p, parent.addr, value, vt);
@@ -2306,7 +2316,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
 			cmp_op = Token_And;
 			cmp_op = Token_And;
 		}
 		}
 
 
-		bool inline_array_arith = type_size_of(tl) <= build_context.max_align;
+		bool inline_array_arith = lb_can_try_to_inline_array_arith(tl);
 		i32 count = 0;
 		i32 count = 0;
 		switch (tl->kind) {
 		switch (tl->kind) {
 		case Type_Array:           count = cast(i32)tl->Array.count;           break;
 		case Type_Array:           count = cast(i32)tl->Array.count;           break;

+ 11 - 23
src/llvm_backend_general.cpp

@@ -591,6 +591,9 @@ bool lb_try_update_alignment(lbValue ptr, unsigned alignment) {
 	return lb_try_update_alignment(ptr.value, alignment);
 	return lb_try_update_alignment(ptr.value, alignment);
 }
 }
 
 
+bool lb_can_try_to_inline_array_arith(Type *t) {
+	return type_size_of(t) <= build_context.max_simd_align;
+}
 
 
 bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vector_type_) {
 bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vector_type_) {
 	Type *array_type = base_type(type_deref(ptr.type));
 	Type *array_type = base_type(type_deref(ptr.type));
@@ -599,7 +602,7 @@ bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vector_type_) {
 	Type *elem_type = base_array_type(array_type);
 	Type *elem_type = base_array_type(array_type);
 
 
 	// TODO(bill): Determine what is the correct limit for doing vector arithmetic
 	// TODO(bill): Determine what is the correct limit for doing vector arithmetic
-	if (type_size_of(array_type) <= build_context.max_align &&
+	if (lb_can_try_to_inline_array_arith(array_type) &&
 	    is_type_valid_vector_elem(elem_type)) {
 	    is_type_valid_vector_elem(elem_type)) {
 		// Try to treat it like a vector if possible
 		// Try to treat it like a vector if possible
 		bool possible = false;
 		bool possible = false;
@@ -874,18 +877,6 @@ bool lb_is_type_proc_recursive(Type *t) {
 		case Type_Pointer:
 		case Type_Pointer:
 			t = t->Pointer.elem;
 			t = t->Pointer.elem;
 			break;
 			break;
-		case Type_Array:
-			t = t->Array.elem;
-			break;
-		case Type_EnumeratedArray:
-			t = t->EnumeratedArray.elem;
-			break;
-		case Type_Slice:
-			t = t->Slice.elem;
-			break;
-		case Type_DynamicArray:
-			t = t->DynamicArray.elem;
-			break;
 		case Type_Proc:
 		case Type_Proc:
 			return true;
 			return true;
 		default:
 		default:
@@ -1887,16 +1878,16 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 		return LLVMPointerType(lb_type(m, type->Pointer.elem), 0);
 		return LLVMPointerType(lb_type(m, type->Pointer.elem), 0);
 
 
 	case Type_Array: {
 	case Type_Array: {
-		m->internal_type_level -= 1;
-		LLVMTypeRef t = LLVMArrayType(lb_type(m, type->Array.elem), cast(unsigned)type->Array.count);
 		m->internal_type_level += 1;
 		m->internal_type_level += 1;
+		LLVMTypeRef t = LLVMArrayType(lb_type(m, type->Array.elem), cast(unsigned)type->Array.count);
+		m->internal_type_level -= 1;
 		return t;
 		return t;
 	}
 	}
 
 
 	case Type_EnumeratedArray: {
 	case Type_EnumeratedArray: {
-		m->internal_type_level -= 1;
-		LLVMTypeRef t = LLVMArrayType(lb_type(m, type->EnumeratedArray.elem), cast(unsigned)type->EnumeratedArray.count);
 		m->internal_type_level += 1;
 		m->internal_type_level += 1;
+		LLVMTypeRef t = LLVMArrayType(lb_type(m, type->EnumeratedArray.elem), cast(unsigned)type->EnumeratedArray.count);
+		m->internal_type_level -= 1;
 		return t;
 		return t;
 	}
 	}
 
 
@@ -2098,14 +2089,11 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 		}
 		}
 
 
 	case Type_Proc:
 	case Type_Proc:
-		// if (m->internal_type_level > 256) { // TODO HACK(bill): is this really enough?
-		if (m->internal_type_level > 1) { // TODO HACK(bill): is this really enough?
-			return LLVMPointerType(LLVMIntTypeInContext(m->ctx, 8), 0);
-		} else {
+		{
 			LLVMTypeRef proc_raw_type = lb_type_internal_for_procedures_raw(m, type);
 			LLVMTypeRef proc_raw_type = lb_type_internal_for_procedures_raw(m, type);
-			return LLVMPointerType(proc_raw_type, 0);
+			gb_unused(proc_raw_type);
+			return LLVMPointerType(LLVMIntTypeInContext(m->ctx, 8), 0);
 		}
 		}
-
 		break;
 		break;
 	case Type_BitSet:
 	case Type_BitSet:
 		{
 		{

+ 5 - 3
src/llvm_backend_proc.cpp

@@ -736,6 +736,9 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
 	}
 	}
 	for_array(i, processed_args) {
 	for_array(i, processed_args) {
 		lbValue arg = processed_args[i];
 		lbValue arg = processed_args[i];
+		if (is_type_proc(arg.type)) {
+			arg.value = LLVMBuildPointerCast(p->builder, arg.value, lb_type(p->module, arg.type), "");
+		}
 		args[arg_index++] = arg.value;
 		args[arg_index++] = arg.value;
 	}
 	}
 	if (context_ptr.addr.value != nullptr) {
 	if (context_ptr.addr.value != nullptr) {
@@ -2867,9 +2870,9 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 		return y;
 		return y;
 	}
 	}
 
 
-	Ast *pexpr = unparen_expr(ce->proc);
+	Ast *proc_expr = unparen_expr(ce->proc);
 	if (proc_mode == Addressing_Builtin) {
 	if (proc_mode == Addressing_Builtin) {
-		Entity *e = entity_of_node(pexpr);
+		Entity *e = entity_of_node(proc_expr);
 		BuiltinProcId id = BuiltinProc_Invalid;
 		BuiltinProcId id = BuiltinProc_Invalid;
 		if (e != nullptr) {
 		if (e != nullptr) {
 			id = cast(BuiltinProcId)e->Builtin.id;
 			id = cast(BuiltinProcId)e->Builtin.id;
@@ -2881,7 +2884,6 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 
 
 	// NOTE(bill): Regular call
 	// NOTE(bill): Regular call
 	lbValue value = {};
 	lbValue value = {};
-	Ast *proc_expr = unparen_expr(ce->proc);
 
 
 	Entity *proc_entity = entity_of_node(proc_expr);
 	Entity *proc_entity = entity_of_node(proc_expr);
 	if (proc_entity != nullptr) {
 	if (proc_entity != nullptr) {

+ 13 - 3
src/llvm_backend_stmt.cpp

@@ -646,7 +646,7 @@ void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs, Scope *sco
 
 
 
 
 	lbAddr array = lb_build_addr(p, expr);
 	lbAddr array = lb_build_addr(p, expr);
-	if (is_type_pointer(type_deref(lb_addr_type(array)))) {
+	if (is_type_pointer(lb_addr_type(array))) {
 		array = lb_addr(lb_addr_load(p, array));
 		array = lb_addr(lb_addr_load(p, array));
 	}
 	}
 	lbValue count = lb_soa_struct_len(p, lb_addr_load(p, array));
 	lbValue count = lb_soa_struct_len(p, lb_addr_load(p, array));
@@ -1796,7 +1796,7 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
 
 
 	lbValue rhs = lb_emit_conv(p, value, lhs_type);
 	lbValue rhs = lb_emit_conv(p, value, lhs_type);
 
 
-	bool inline_array_arith = type_size_of(array_type) <= build_context.max_align;
+	bool inline_array_arith = lb_can_try_to_inline_array_arith(array_type);
 
 
 
 
 	if (lhs.kind == lbAddr_Swizzle) {
 	if (lhs.kind == lbAddr_Swizzle) {
@@ -1959,8 +1959,18 @@ void lb_build_assign_stmt(lbProcedure *p, AstAssignStmt *as) {
 	} else {
 	} else {
 		lbAddr lhs = lb_build_addr(p, as->lhs[0]);
 		lbAddr lhs = lb_build_addr(p, as->lhs[0]);
 		lbValue value = lb_build_expr(p, as->rhs[0]);
 		lbValue value = lb_build_expr(p, as->rhs[0]);
-
 		Type *lhs_type = lb_addr_type(lhs);
 		Type *lhs_type = lb_addr_type(lhs);
+
+		// NOTE(bill): Allow for the weird edge case of:
+		// array *= matrix
+		if (op == Token_Mul && is_type_matrix(value.type) && is_type_array(lhs_type)) {
+			lbValue old_value = lb_addr_load(p, lhs);
+			Type *type = old_value.type;
+			lbValue new_value = lb_emit_vector_mul_matrix(p, old_value, value, type);
+			lb_addr_store(p, lhs, new_value);
+			return;
+		}
+
 		if (is_type_array(lhs_type)) {
 		if (is_type_array(lhs_type)) {
 			lb_build_assign_stmt_array(p, op, lhs, value);
 			lb_build_assign_stmt_array(p, op, lhs, value);
 			return;
 			return;

+ 14 - 0
src/llvm_backend_utility.cpp

@@ -225,6 +225,20 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
 	if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
 	if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
 		res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
 		res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
 		return res;
 		return res;
+	} else if (is_type_array_like(src) && is_type_simd_vector(dst)) {
+		unsigned align = cast(unsigned)gb_max(type_align_of(src), type_align_of(dst));
+		lbValue ptr = lb_address_from_load_or_generate_local(p, value);
+		if (lb_try_update_alignment(ptr, align)) {
+			LLVMTypeRef result_type = lb_type(p->module, t);
+			res.value = LLVMBuildPointerCast(p->builder, ptr.value, LLVMPointerType(result_type, 0), "");
+			res.value = LLVMBuildLoad2(p->builder, result_type, res.value, "");
+			return res;
+		}
+		lbAddr addr = lb_add_local_generated(p, t, false);
+		lbValue ap = lb_addr_get_ptr(p, addr);
+		ap = lb_emit_conv(p, ap, alloc_type_pointer(value.type));
+		lb_emit_store(p, ap, value);
+		return lb_addr_load(p, addr);
 	}
 	}
 
 
 	if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) {
 	if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) {

+ 43 - 43
src/main.cpp

@@ -288,11 +288,15 @@ i32 linker_stage(lbGenerator *gen) {
 
 
 			char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
 			char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
 			if (!build_context.use_lld) { // msvc
 			if (!build_context.use_lld) { // msvc
+				String res_path = {};
+				defer (gb_free(heap_allocator(), res_path.text));
 				if (build_context.has_resource) {
 				if (build_context.has_resource) {
+					String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
+					res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\""));
+					gb_free(heap_allocator(), temp_res_path.text);
+
 					String rc_path  = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
 					String rc_path  = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
-					String res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
 					defer (gb_free(heap_allocator(), rc_path.text));
 					defer (gb_free(heap_allocator(), rc_path.text));
-					defer (gb_free(heap_allocator(), res_path.text));
 
 
 					result = system_exec_command_line_app("msvc-link",
 					result = system_exec_command_line_app("msvc-link",
 						"\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
 						"\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
@@ -304,42 +308,25 @@ i32 linker_stage(lbGenerator *gen) {
 					if (result) {
 					if (result) {
 						return result;
 						return result;
 					}
 					}
-
-					result = system_exec_command_line_app("msvc-link",
-						"\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s "
-						"/nologo /incremental:no /opt:ref /subsystem:%s "
-						" %.*s "
-						" %.*s "
-						" %s "
-						"",
-						LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
-						link_settings,
-						subsystem_str,
-						LIT(build_context.link_flags),
-						LIT(build_context.extra_linker_flags),
-						lib_str
-					  );
-				} else {
-					result = system_exec_command_line_app("msvc-link",
-						"\"%.*slink.exe\" %s -OUT:\"%.*s\" %s "
-						"/nologo /incremental:no /opt:ref /subsystem:%s "
-						" %.*s "
-						" %.*s "
-						" %s "
-						"",
-						LIT(vs_exe_path), object_files, LIT(output_filename),
-						link_settings,
-						subsystem_str,
-						LIT(build_context.link_flags),
-						LIT(build_context.extra_linker_flags),
-						lib_str
-					);
 				}
 				}
 
 
+				result = system_exec_command_line_app("msvc-link",
+					"\"%.*slink.exe\" %s %.*s -OUT:\"%.*s\" %s "
+					"/nologo /incremental:no /opt:ref /subsystem:%s "
+					" %.*s "
+					" %.*s "
+					" %s "
+					"",
+					LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
+					link_settings,
+					subsystem_str,
+					LIT(build_context.link_flags),
+					LIT(build_context.extra_linker_flags),
+					lib_str
+				);
 				if (result) {
 				if (result) {
 					return result;
 					return result;
 				}
 				}
-
 			} else { // lld
 			} else { // lld
 				result = system_exec_command_line_app("msvc-lld-link",
 				result = system_exec_command_line_app("msvc-lld-link",
 					"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
 					"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
@@ -590,8 +577,8 @@ void usage(String argv0) {
 	print_usage_line(1, "version           print version");
 	print_usage_line(1, "version           print version");
 	print_usage_line(1, "report            print information useful to reporting a bug");
 	print_usage_line(1, "report            print information useful to reporting a bug");
 	print_usage_line(0, "");
 	print_usage_line(0, "");
-	print_usage_line(0, "For further details on a command, use -help after the command name");
-	print_usage_line(1, "e.g. odin build -help");
+	print_usage_line(0, "For further details on a command, invoke command help:");
+	print_usage_line(1, "e.g. `odin build -help` or `odin help build`");
 }
 }
 
 
 enum BuildFlagKind {
 enum BuildFlagKind {
@@ -1963,13 +1950,13 @@ void print_show_help(String const arg0, String const &command) {
 		print_usage_line(2, "Parse and type check .odin file(s) and then remove unneeded semicolons from the entire project");
 		print_usage_line(2, "Parse and type check .odin file(s) and then remove unneeded semicolons from the entire project");
 	}
 	}
 
 
-	bool doc = command == "doc";
-	bool build = command == "build";
-	bool run_or_build = command == "run" || command == "build" || command == "test";
-	bool test_only = command == "test";
+	bool doc             = command == "doc";
+	bool build           = command == "build";
+	bool run_or_build    = command == "run" || command == "build" || command == "test";
+	bool test_only       = command == "test";
 	bool strip_semicolon = command == "strip-semicolon";
 	bool strip_semicolon = command == "strip-semicolon";
-	bool check_only = command == "check" || strip_semicolon;
-	bool check = run_or_build || check_only;
+	bool check_only      = command == "check" || strip_semicolon;
+	bool check           = run_or_build || check_only;
 
 
 	print_usage_line(0, "");
 	print_usage_line(0, "");
 	print_usage_line(1, "Flags");
 	print_usage_line(1, "Flags");
@@ -2085,7 +2072,7 @@ void print_show_help(String const arg0, String const &command) {
 		print_usage_line(3, "-build-mode:shared    Build as a dynamically linked library");
 		print_usage_line(3, "-build-mode:shared    Build as a dynamically linked library");
 		print_usage_line(3, "-build-mode:obj       Build as an object file");
 		print_usage_line(3, "-build-mode:obj       Build as an object file");
 		print_usage_line(3, "-build-mode:object    Build as an object file");
 		print_usage_line(3, "-build-mode:object    Build as an object file");
-		print_usage_line(3, "-build-mode:assembly  Build as an object file");
+		print_usage_line(3, "-build-mode:assembly  Build as an assembly file");
 		print_usage_line(3, "-build-mode:assembler Build as an assembly file");
 		print_usage_line(3, "-build-mode:assembler Build as an assembly file");
 		print_usage_line(3, "-build-mode:asm       Build as an assembly file");
 		print_usage_line(3, "-build-mode:asm       Build as an assembly file");
 		print_usage_line(3, "-build-mode:llvm-ir   Build as an LLVM IR file");
 		print_usage_line(3, "-build-mode:llvm-ir   Build as an LLVM IR file");
@@ -2701,6 +2688,14 @@ int main(int arg_count, char const **arg_ptr) {
 		build_context.command_kind = Command_bug_report;
 		build_context.command_kind = Command_bug_report;
 		print_bug_report_help();
 		print_bug_report_help();
 		return 0;
 		return 0;
+	} else if (command == "help") {
+		if (args.count <= 2) {
+			usage(args[0]);
+			return 1;
+		} else {
+			print_show_help(args[0], args[2]);
+			return 0;
+		}
 	} else {
 	} else {
 		usage(args[0]);
 		usage(args[0]);
 		return 1;
 		return 1;
@@ -2727,7 +2722,12 @@ int main(int arg_count, char const **arg_ptr) {
 
 
 			if (!single_file_package) {
 			if (!single_file_package) {
 				gb_printf_err("ERROR: `%.*s %.*s` takes a package as its first argument.\n", LIT(args[0]), LIT(command));
 				gb_printf_err("ERROR: `%.*s %.*s` takes a package as its first argument.\n", LIT(args[0]), LIT(command));
-				gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename));
+				if (init_filename == "-file") {
+					gb_printf_err("Did you mean `%.*s %.*s <filename.odin> -file`?\n", LIT(args[0]), LIT(command));
+				} else {
+					gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename));
+				}
+
 				gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n");
 				gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n");
 				return 1;
 				return 1;
 			} else {
 			} else {

+ 50 - 1
src/parser.cpp

@@ -5506,6 +5506,51 @@ isize calc_decl_count(Ast *decl) {
 	return count;
 	return count;
 }
 }
 
 
+bool parse_build_project_directory_tag(Token token_for_pos, String s) {
+	String const prefix = str_lit("+build-project-name");
+	GB_ASSERT(string_starts_with(s, prefix));
+	s = string_trim_whitespace(substring(s, prefix.len, s.len));
+	if (s.len == 0) {
+		return true;
+	}
+
+	bool any_correct = false;
+
+	while (s.len > 0) {
+		bool this_kind_correct = true;
+
+		do {
+			String p = string_trim_whitespace(build_tag_get_token(s, &s));
+			if (p.len == 0) break;
+			if (p == ",") break;
+
+			bool is_notted = false;
+			if (p[0] == '!') {
+				is_notted = true;
+				p = substring(p, 1, p.len);
+				if (p.len == 0) {
+					syntax_error(token_for_pos, "Expected a build-project-name after '!'");
+					break;
+				}
+			}
+
+			if (p.len == 0) {
+				continue;
+			}
+
+			if (is_notted) {
+				this_kind_correct = this_kind_correct && (p != build_context.ODIN_BUILD_PROJECT_NAME);
+			} else {
+				this_kind_correct = this_kind_correct && (p == build_context.ODIN_BUILD_PROJECT_NAME);
+			}
+		} while (s.len > 0);
+
+		any_correct = any_correct || this_kind_correct;
+	}
+
+	return any_correct;
+}
+
 bool parse_file(Parser *p, AstFile *f) {
 bool parse_file(Parser *p, AstFile *f) {
 	if (f->tokens.count == 0) {
 	if (f->tokens.count == 0) {
 		return true;
 		return true;
@@ -5561,7 +5606,11 @@ bool parse_file(Parser *p, AstFile *f) {
 			if (string_starts_with(str, str_lit("//"))) {
 			if (string_starts_with(str, str_lit("//"))) {
 				String lc = string_trim_whitespace(substring(str, 2, str.len));
 				String lc = string_trim_whitespace(substring(str, 2, str.len));
 				if (lc.len > 0 && lc[0] == '+') {
 				if (lc.len > 0 && lc[0] == '+') {
-					if (string_starts_with(lc, str_lit("+build"))) {
+					 if (string_starts_with(lc, str_lit("+build-project-name"))) {
+						if (!parse_build_project_directory_tag(tok, lc)) {
+							return false;
+						}
+					} else if (string_starts_with(lc, str_lit("+build"))) {
 						if (!parse_build_tag(tok, lc)) {
 						if (!parse_build_tag(tok, lc)) {
 							return false;
 							return false;
 						}
 						}

+ 3 - 4
src/types.cpp

@@ -1428,7 +1428,7 @@ i64 matrix_align_of(Type *t, struct TypePath *tp) {
 	}
 	}
 	GB_ASSERT(min_alignment >= elem_align);
 	GB_ASSERT(min_alignment >= elem_align);
 	
 	
-	i64 align = gb_min(min_alignment, build_context.max_align);
+	i64 align = gb_min(min_alignment, build_context.max_simd_align);
 	return align;
 	return align;
 }
 }
 
 
@@ -3556,7 +3556,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
 
 
 	case Type_SimdVector: {
 	case Type_SimdVector: {
 		// IMPORTANT TODO(bill): Figure out the alignment of vector types
 		// IMPORTANT TODO(bill): Figure out the alignment of vector types
-		return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align*2);
+		return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_simd_align*2);
 	}
 	}
 	
 	
 	case Type_Matrix: 
 	case Type_Matrix: 
@@ -3571,10 +3571,9 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
 		return build_context.word_size;
 		return build_context.word_size;
 	}
 	}
 
 
-	// return gb_clamp(next_pow2(type_size_of(t)), 1, build_context.max_align);
 	// NOTE(bill): Things that are bigger than build_context.word_size, are actually comprised of smaller types
 	// NOTE(bill): Things that are bigger than build_context.word_size, are actually comprised of smaller types
 	// TODO(bill): Is this correct for 128-bit types (integers)?
 	// TODO(bill): Is this correct for 128-bit types (integers)?
-	return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.word_size);
+	return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align);
 }
 }
 
 
 i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union) {
 i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union) {

+ 6 - 1
tests/core/build.bat

@@ -69,4 +69,9 @@ echo ---
 echo ---
 echo ---
 echo Running core:text/i18n tests
 echo Running core:text/i18n tests
 echo ---
 echo ---
-%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe
+%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe
+
+echo ---
+echo Running core:slice tests
+echo ---
+%PATH_TO_ODIN% run slice %COMMON% -out:test_core_slice.exe

+ 182 - 0
tests/core/slice/test_core_slice.odin

@@ -0,0 +1,182 @@
+package test_core_slice
+
+import "core:slice"
+import "core:testing"
+import "core:fmt"
+import "core:os"
+import "core:math/rand"
+
+TEST_count := 0
+TEST_fail  := 0
+
+when ODIN_TEST {
+	expect  :: testing.expect
+	log     :: testing.log
+} else {
+	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
+		TEST_count += 1
+		if !condition {
+			TEST_fail += 1
+			fmt.printf("[%v] %v\n", loc, message)
+			return
+		}
+	}
+	log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
+		fmt.printf("[%v] ", loc)
+		fmt.printf("log: %v\n", v)
+	}
+}
+
+main :: proc() {
+	t := testing.T{}
+	test_sort_with_indices(&t)
+
+	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
+}
+
+@test
+test_sort_with_indices :: proc(t: ^testing.T) {
+	seed := rand.uint64()
+	fmt.printf("Random seed: %v\n", seed)
+
+	// Test sizes are all prime.
+	test_sizes :: []int{7, 13, 347, 1031, 10111, 100003}
+
+	for test_size in test_sizes {
+		fmt.printf("Sorting %v random u64 values along with index.\n", test_size)
+
+		r := rand.create(seed)
+
+		vals  := make([]u64, test_size)
+		r_idx := make([]int, test_size) // Reverse index
+		defer {
+			delete(vals)
+			delete(r_idx)
+		}
+
+		// Set up test values
+		for _, i in vals {
+			vals[i]     = rand.uint64(&r)
+		}
+
+		// Sort
+		f_idx := slice.sort_with_indices(vals)
+		defer delete(f_idx)
+
+		// Verify sorted test values
+		rand.init(&r, seed)
+
+		for v, i in f_idx {
+			r_idx[v] = i
+		}
+
+		last: u64
+		for v, i in vals {
+			if i > 0 {
+				val_pass := v >= last
+				expect(t, val_pass, "Expected values to have been sorted.")
+				if !val_pass {
+					break
+				}
+			}
+
+			idx_pass := vals[r_idx[i]] == rand.uint64(&r)
+			expect(t, idx_pass, "Expected index to have been sorted.")
+			if !idx_pass {
+				break
+			}
+			last = v
+		}
+	}
+}
+
+@test
+test_sort_by_indices :: proc(t: ^testing.T) {
+	seed := rand.uint64()
+	fmt.printf("Random seed: %v\n", seed)
+
+	// Test sizes are all prime.
+	test_sizes :: []int{7, 13, 347, 1031, 10111, 100003}
+
+	for test_size in test_sizes {
+		fmt.printf("Sorting %v random u64 values along with index.\n", test_size)
+
+		r := rand.create(seed)
+
+		vals  := make([]u64, test_size)
+		r_idx := make([]int, test_size) // Reverse index
+		defer {
+			delete(vals)
+			delete(r_idx)
+		}
+
+		// Set up test values
+		for _, i in vals {
+			vals[i]     = rand.uint64(&r)
+		}
+
+		// Sort
+		f_idx := slice.sort_with_indices(vals)
+		defer delete(f_idx)
+
+		// Verify sorted test values
+		rand.init(&r, seed)
+
+		{
+			indices := make([]int, test_size)
+			defer delete(indices)
+			for _, i in indices {
+				indices[i] = i
+			}
+
+			sorted_indices := slice.sort_by_indices(indices, f_idx)
+			defer delete(sorted_indices)
+			for v, i in sorted_indices {
+				idx_pass := v == f_idx[i]
+				expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices")
+				if !idx_pass {
+					break
+				}
+			}
+		}
+		{
+			indices := make([]int, test_size)
+			defer delete(indices)
+			for _, i in indices {
+				indices[i] = i
+			}
+
+			slice.sort_by_indices_overwrite(indices, f_idx)
+			for v, i in indices {
+				idx_pass := v == f_idx[i]
+				expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices")
+				if !idx_pass {
+					break
+				}
+			}
+		}
+		{
+			indices := make([]int, test_size)
+			swap := make([]int, test_size)
+			defer {
+				delete(indices)
+				delete(swap)
+			}
+			for _, i in indices {
+				indices[i] = i
+			}
+
+			slice.sort_by_indices(indices, swap, f_idx)
+			for v, i in swap {
+				idx_pass := v == f_idx[i]
+				expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices")
+				if !idx_pass {
+					break
+				}
+			}
+		}
+	}
+}

+ 1 - 1
tools/odinfmt/flag/flag.odin

@@ -146,7 +146,7 @@ reflect_args_structure :: proc(ctx: ^Flag_Context, v: any) -> Flag_Error {
 		type := types[i];
 		type := types[i];
 
 
 		if named_type, ok := type.variant.(Type_Info_Named); ok {
 		if named_type, ok := type.variant.(Type_Info_Named); ok {
-			if union_type, ok := named_type.base.variant.(Type_Info_Union); ok && union_type.maybe && len(union_type.variants) == 1 {
+			if union_type, ok := named_type.base.variant.(Type_Info_Union); ok && len(union_type.variants) == 1 {
 				flag.optional = true;
 				flag.optional = true;
 				flag.tag_ptr = rawptr(uintptr(union_type.tag_offset) + uintptr(v.data) + uintptr(offsets[i]));
 				flag.tag_ptr = rawptr(uintptr(union_type.tag_offset) + uintptr(v.data) + uintptr(offsets[i]));
 				type = union_type.variants[0];
 				type = union_type.variants[0];

+ 1 - 1
tools/odinfmt/main.odin

@@ -70,7 +70,7 @@ walk_files :: proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip
 }
 }
 
 
 main :: proc() {
 main :: proc() {
-	init_global_temporary_allocator(mem.megabytes(100));
+	init_global_temporary_allocator(mem.Megabyte * 100)
 
 
 	args: Args;
 	args: Args;
 
 

+ 2 - 0
vendor/OpenGL/constants.odin

@@ -1,5 +1,7 @@
 package odin_gl
 package odin_gl
 
 
+GL_DEBUG :: #config(GL_DEBUG, ODIN_DEBUG)
+
 FALSE                          :: false
 FALSE                          :: false
 TRUE                           :: true
 TRUE                           :: true
 
 

+ 1 - 1
vendor/OpenGL/helpers.odin

@@ -46,7 +46,7 @@ get_last_error_message :: proc() -> (compile_message: string, compile_type: Shad
 // Shader checking and linking checking are identical
 // Shader checking and linking checking are identical
 // except for calling differently named GL functions
 // except for calling differently named GL functions
 // it's a bit ugly looking, but meh
 // it's a bit ugly looking, but meh
-when ODIN_DEBUG {
+when GL_DEBUG {
 	import "core:runtime"
 	import "core:runtime"
 	
 	
 	@private
 	@private

+ 1 - 1
vendor/OpenGL/wrappers.odin

@@ -2,7 +2,7 @@ package odin_gl
 
 
 #assert(size_of(bool) == size_of(u8))
 #assert(size_of(bool) == size_of(u8))
 
 
-when !ODIN_DEBUG {
+when !GL_DEBUG {
 	// VERSION_1_0
 	// VERSION_1_0
 	CullFace               :: proc "c" (mode: u32)                                                                                         {        impl_CullFace(mode)                                                                         }
 	CullFace               :: proc "c" (mode: u32)                                                                                         {        impl_CullFace(mode)                                                                         }
 	FrontFace              :: proc "c" (mode: u32)                                                                                         {        impl_FrontFace(mode)                                                                        }
 	FrontFace              :: proc "c" (mode: u32)                                                                                         {        impl_FrontFace(mode)                                                                        }

+ 8 - 1
vendor/README.md

@@ -134,4 +134,11 @@ See also LICENSE in the `GGPO` directory itself.
 `botan.lib` is available under Botan's [BSD](https://botan.randombit.net/license.txt) license.
 `botan.lib` is available under Botan's [BSD](https://botan.randombit.net/license.txt) license.
 
 
 See also LICENSE in the `botan` directory itself.
 See also LICENSE in the `botan` directory itself.
-Includes full bindings as well as wrappers to match the `core:crypto` API.
+Includes full bindings as well as wrappers to match the `core:crypto` API.
+
+## CommonMark
+
+[CMark](https://github.com/commonmark/cmark) CommonMark parsing library.
+
+See also LICENSE in the `commonmark` directory itself.
+Includes full bindings and Windows `.lib` and `.dll`.

+ 170 - 0
vendor/commonmark/LICENSE

@@ -0,0 +1,170 @@
+Copyright (c) 2014, John MacFarlane
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-----
+
+houdini.h, houdini_href_e.c, houdini_html_e.c, houdini_html_u.c
+
+derive from https://github.com/vmg/houdini (with some modifications)
+
+Copyright (C) 2012 Vicent Martí
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+-----
+
+buffer.h, buffer.c, chunk.h
+
+are derived from code (C) 2012 Github, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+-----
+
+utf8.c and utf8.c
+
+are derived from utf8proc
+(<http://www.public-software-group.org/utf8proc>),
+(C) 2009 Public Software Group e. V., Berlin, Germany.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+-----
+
+The normalization code in normalize.py was derived from the
+markdowntest project, Copyright 2013 Karl Dubost:
+
+The MIT License (MIT)
+
+Copyright (c) 2013 Karl Dubost
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-----
+
+The CommonMark spec (test/spec.txt) is
+
+Copyright (C) 2014-15 John MacFarlane
+
+Released under the Creative Commons CC-BY-SA 4.0 license:
+<http://creativecommons.org/licenses/by-sa/4.0/>.
+
+-----
+
+The test software in test/ is
+
+Copyright (c) 2014, John MacFarlane
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 4 - 0
vendor/commonmark/build.bat

@@ -0,0 +1,4 @@
+@echo off
+pushd W:\Odin-other\Odin-test
+call build.bat
+popd

+ 526 - 0
vendor/commonmark/cmark.odin

@@ -0,0 +1,526 @@
+/*
+	Bindings against CMark (https://github.com/commonmark/cmark)
+
+	Original authors: John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
+	See LICENSE for license details.
+*/
+package commonmark
+
+import "core:c"
+import "core:c/libc"
+import "core:runtime"
+
+BINDING_VERSION :: Version_Info{major = 0, minor = 30, patch = 2}
+
+when ODIN_OS == .Windows {
+	foreign import lib {
+		"cmark_static.lib",
+	}
+} else when ODIN_OS == .Linux {
+	foreign import lib {
+		"libcmark.a",
+	}
+}
+
+Option :: enum c.int {
+	Source_Position =  1, // Include a `data-sourcepos` attribute on all block elements.
+	Hard_Breaks     =  2, // Render `softbreak` as hard line breaks.
+	Safe            =  3, // Defined for API compatibility, now enabled by default.
+	Unsafe          = 17, // Render raw HTML and unsafe links (`javascript:`, `vbscript:`,
+						  // `file:`, and `data:`, except for `image/png`, `image/gif`,
+						  // `image/jpeg`, or `image/webp` mime types).  By default,
+						  // raw HTML is replaced by a placeholder HTML comment. Unsafe
+						  // links are replaced by empty strings.
+	No_Breaks       =  4, // Render `softbreak` elements as spaces.
+	Normalize       =  8, // Legacy option, no effect.
+	Validate_UTF8   =  9, // Validate UTF-8 input before parsing, replacing illegal
+						  // sequences with the replacement character U+FFFD.
+	Smart           = 10, // Convert straight quotes to curly, --- to em dashes, -- to en dashes.
+}
+Options :: bit_set[Option; c.int]
+
+DEFAULT_OPTIONS :: Options{}
+
+Node_Type :: enum u16 {
+	// Error status
+	None = 0,
+
+	/* Block */
+	Document,
+	Block_Quote,
+	List,
+	Item,
+	Code_Block,
+	HTML_Block,
+	Custom_Block,
+	Paragraph,
+	Heading,
+	Thematic_Break,
+
+	/* Inline */
+	Text,
+	Soft_Break,
+	Line_Break,
+	Code,
+	HTML_Inline,
+	Custom_Inline,
+	Emph,
+	Strong,
+	Link,
+	Image,
+
+	First_Block  = Document,
+	Last_Block   = Thematic_Break,
+
+	First_Inline = Text,
+	Last_Inline  = Image,
+}
+
+List_Type :: enum c.int {
+	None,
+	Bullet,
+	Ordered,
+}
+
+Delim_Type :: enum c.int {
+	None,
+	Period,
+	Paren,
+}
+
+// Version information
+Version_Info :: struct {
+	patch: u8,
+	minor: u8,
+	major: u8,
+	_:     u8,
+}
+
+@(default_calling_convention="c", link_prefix="cmark_")
+foreign lib {
+	version        :: proc() -> (res: Version_Info) ---
+	version_string :: proc() -> (res: cstring) ---
+}
+
+// Simple API
+@(default_calling_convention="c", link_prefix="cmark_")
+foreign lib {
+	// Convert 'text' (assumed to be a UTF-8 encoded string with length `len`) from CommonMark Markdown to HTML
+	// returning a null-terminated, UTF-8-encoded string. It is the caller's responsibility
+	// to free the returned buffer.
+	markdown_to_html :: proc(text: cstring, length: c.size_t, options: Options) -> (html: cstring) ---
+}
+
+markdown_to_html_from_string :: proc(text: string, options: Options) -> (html: string) {
+	return string(markdown_to_html(cstring(raw_data(text)), len(text), options))
+}
+
+// Custom allocator - Defines the memory allocation functions to be used by CMark
+// when parsing and allocating a document tree
+Allocator :: struct {
+	calloc:  proc "c" (num: c.size_t, size: c.size_t)  -> rawptr,
+	realloc: proc "c" (ptr: rawptr, new_size: c.size_t) -> rawptr,
+	free:    proc "c" (ptr: rawptr),
+}
+
+@(default_calling_convention="c", link_prefix="cmark_")
+foreign lib {
+	// Returns a pointer to the default memory allocator.
+	get_default_mem_allocator :: proc() -> (mem: ^Allocator) ---
+}
+
+bufsize_t :: distinct i32
+
+// Node creation, destruction, and tree traversal
+Node :: struct {
+	mem:          ^Allocator,
+
+	next:         ^Node,
+	prev:         ^Node,
+	parent:       ^Node,
+	first_child:  ^Node,
+	last_child:   ^Node,
+
+	user_data:    rawptr,
+	data:         [^]u8,
+	len:          bufsize_t,
+
+	start_line:   c.int,
+	start_column: c.int,
+	end_line:     c.int,
+	end_column:   c.int,
+
+	type:         Node_Type,
+	flags:        Node_Flags,
+
+	as: struct #raw_union {
+		list:    List,
+		code:    Code,
+		heading: Heading,
+		link:    Link,
+		custom:  Custom,
+		html_block_type: c.int,
+	},
+}
+
+Node_Flag :: enum u16 {
+	Open              = 0,
+	Last_Line_Blank   = 1,
+	Last_Line_Checked = 2,
+}
+Node_Flags :: bit_set[Node_Flag; u16]
+
+List :: struct {
+	marker_offset: c.int,
+	padding:       c.int,
+	start:         c.int,
+	list_type:     u8,
+	delimiter:     u8,
+	bullet_char:   u8,
+	tight:         c.bool,
+}
+
+Code :: struct {
+	info:         cstring,
+	fence_length: u8,
+	fence_offset: u8,
+	fence_char:   u8,
+	fenced:       b8,
+}
+
+Heading :: struct {
+	internal_offset: c.int,
+	level:           i8,
+	setext:          c.bool,
+}
+
+
+Link :: struct {
+	url:   cstring,
+	title: cstring,
+}
+
+Custom :: struct {
+	on_enter: cstring,
+	on_exit:  cstring,
+}
+
+@(default_calling_convention="c", link_prefix="cmark_")
+foreign lib {
+	// Creates a new node of type 'type'.
+	// Note that the node may have other required properties, which it is the caller's responsibility
+	// to assign.
+	node_new :: proc(type: Node_Type) -> (node: ^Node) ---
+
+	// Same as `node_new`, but explicitly listing the memory allocator used to allocate the node.
+	// Note: be sure to use the same allocator for every node in a tree, or bad things can happen.
+	node_new_with_mem :: proc(type: Node_Type, mem: ^Allocator) -> (node: ^Node) ---
+
+	// Frees the memory allocated for a node and any children.
+	node_free :: proc(node: ^Node) ---
+
+	/*
+		Tree Traversal
+	*/
+	// Returns the next node in the sequence after `node`, or nil if there is none.
+	node_next :: proc(node: ^Node) -> (next: ^Node) ---
+
+	// Returns the previous node in the sequence after `node`, or nil if there is none.
+	node_previous :: proc(node: ^Node) -> (prev: ^Node) ---
+
+	// Returns the parent of `node`, or nil if there is none.
+	node_parent :: proc(node: ^Node) -> (parent: ^Node) ---
+
+	// Returns the first child of `node`, or nil if `node` has no children.
+	node_first_child :: proc(node: ^Node) -> (child: ^Node) ---
+
+	// Returns the last child of `node`, or nil if `node` has no children.
+	node_last_child :: proc(node: ^Node) -> (child: ^Node) ---
+
+}
+
+Iter   :: distinct rawptr
+
+Event_Type :: enum c.int {
+	None,
+	Done,
+	Enter,
+	Exit,
+}
+
+@(default_calling_convention="c", link_prefix="cmark_")
+foreign lib {
+	// Creates a new iterator starting at 'root'.  The current node and event
+	// type are undefined until `iter_next` is called for the first time.
+	// The memory allocated for the iterator should be released using
+	// 'iter_free' when it is no longer needed.
+	iter_new :: proc(root: ^Node) -> (iter: ^Iter) ---
+
+	// Frees the memory allocated for an iterator.
+	iter_free :: proc(iter: ^Iter) ---
+
+	// Advances to the next node and returns the event type (`.Enter`, `.Exit`, `.Done`)
+	iter_next :: proc(iter: ^Iter) -> (event_type: Event_Type) ---
+
+	// Returns the current node.
+	iter_get_node :: proc(iter: ^Iter) -> (node: ^Node) ---
+
+	// Returns the current event type.
+	iter_get_event_type :: proc(iter: ^Iter) -> (event_type: Event_Type) ---
+
+	// Returns the root node.
+	iter_get_root :: proc(iter: ^Iter) -> (root: ^Node) ---
+
+	// Resets the iterator so that the current node is `current` and
+	// the event type is `event_type`. The new current node must be a
+	// descendant of the root node or the root node itself.
+	iter_reset :: proc(iter: ^Iter, current: ^Node, event_type: Event_Type) ---
+}
+
+// Accessors
+@(default_calling_convention="c", link_prefix="cmark_")
+foreign lib {
+	// Returns the user data of `node`.
+	node_get_user_data :: proc(node: ^Node) -> (user_data: rawptr) ---
+
+	// Sets arbitrary user data for `node`. Returns `true` on success, `false` on failure.
+	node_set_user_data :: proc(node: ^Node, user_data: rawptr) -> (success: b32) ---
+
+	// Returns the type of `node`, or `.None` on error.
+	node_get_type :: proc(node: ^Node) -> (node_type: Node_Type) ---
+
+	// Like `node_get_type`, but returns a string representation of the type, or "<unknown>".
+	node_get_type_string :: proc(node: ^Node) -> (node_type: cstring) ---
+
+	// Returns the string contents of `node`, or an empty string if none is set.
+	// Returns `nil` if called on a node that does not have string content.
+	node_get_literal :: proc(node: ^Node) -> (content: cstring) ---
+
+	// Sets the string contents of `node`. Returns `true` on success, `false` on failure.
+	node_set_literal :: proc(node: ^Node, content: cstring) -> (success: b32) ---
+
+	// Returns the heading level of `node`, or 0 if `node` is not a heading.
+	node_get_heading_level :: proc(node: ^Node) -> (level: c.int) ---
+
+	// Sets the heading level of `node`. Returns `true` on success, `false` on failure.
+	node_set_heading_level :: proc(node: ^Node, level: c.int) -> (success: b32) ---
+
+	// Returns the list type of `node`, or `.No_List` if not a list.
+	node_get_list_type :: proc(node: ^Node) -> (list_type: List_Type) ---
+
+	// Sets the list type of `node`. Returns `true` on success, `false` on failure.
+	node_set_list_type :: proc(node: ^Node, list_type: List_Type) -> (success: b32) ---
+
+	// Returns the list delimiter type of `node`, or `.No_Delim` if not a list.
+	node_get_list_delim :: proc(node: ^Node) -> (delim_type: Delim_Type) ---
+
+	// Sets the delimiter type of `node`. Returns `true` on success, `false` on failure.
+	node_set_list_delim :: proc(node: ^Node, delim_type: Delim_Type) -> (success: b32) ---
+
+	// Returns starting number of `node`, if it is an ordered list, otherwise 0.
+	node_get_list_start :: proc(node: ^Node) -> (start: c.int) ---
+
+	// Sets starting number of `node`, if it is an ordered list.
+	// Returns `true` on success, `false` on failure.
+	node_set_list_start :: proc(node: ^Node, start: c.int) -> (success: b32) ---
+
+	// Returns `true` if `node` is a tight list, `false` otherwise.
+	node_get_list_tight :: proc(node: ^Node) -> (tight: b32) ---
+
+	// Sets the "tightness" of a list. Returns `true` on success, `false` on failure.
+	node_set_list_tight :: proc(node: ^Node, tight: b32) -> (success: b32) ---
+
+	// Returns the info string from a fenced code block.
+	get_fence_info :: proc(node: ^Node) -> (fence_info: cstring) ---
+
+	// Sets the info string in a fenced code block, returning `true` on success and `false` on failure.
+	node_set_fence_info :: proc(node: ^Node, fence_info: cstring) -> (success: b32) ---
+
+	// Returns the URL of a link or image `node`, or an empty string if no URL is set.
+	// Returns nil if called on a node that is not a link or image.
+	node_get_url :: proc(node: ^Node) -> (url: cstring) ---
+
+	// Sets the URL of a link or image `node`. Returns `true` on success, `false` on failure.
+	node_set_url :: proc(node: ^Node, url: cstring) -> (success: b32) ---
+
+	// Returns the title of a link or image `node`, or an empty string if no title is set.
+	// Returns nil if called on a node that is not a link or image.
+	node_get_title :: proc(node: ^Node) -> (title: cstring) ---
+
+	// Sets the title of a link or image `node`. Returns `true` on success, `false` on failure.
+	node_set_title :: proc(node: ^Node, title: cstring) -> (success: b32) ---
+
+	// Returns the literal "on enter" text for a custom `node`, or an empty string if no on_enter is set.
+	// Returns nil if called on a non-custom node.
+	node_get_on_enter :: proc(node: ^Node) -> (on_enter: cstring) ---
+
+	// Sets the literal text to render "on enter" for a custom `node`.
+	// Any children of the node will be rendered after this text.
+	// Returns `true` on success, `false`on failure.
+	node_set_on_enter :: proc(node: ^Node, on_enter: cstring) -> (success: b32) ---
+
+	// Returns the line on which `node` begins.
+	node_get_start_line :: proc(node: ^Node) -> (line: c.int) ---
+
+	// Returns the column at which `node` begins.
+	node_get_start_column :: proc(node: ^Node) -> (column: c.int) ---
+
+	// Returns the line on which `node` ends.
+	node_get_end_line :: proc(node: ^Node) -> (line: c.int) ---
+
+	// Returns the column at which `node` ends.
+	node_get_end_column :: proc(node: ^Node) -> (column: c.int) ---
+}
+
+// Tree Manipulation
+@(default_calling_convention="c", link_prefix="cmark_")
+foreign lib {
+	// Unlinks a `node`, removing it from the tree, but not freeing its memory.
+	// (Use `node_free` for that.)
+	node_unlink :: proc(node: ^Node) ---
+
+	// Inserts 'sibling' before `node`.  Returns `true` on success, `false` on failure.
+	node_insert_before :: proc(node: ^Node, sibling: ^Node) -> (success: b32) ---
+
+	// Inserts 'sibling' after `node`. Returns `true` on success, `false` on failure.
+	node_insert_after :: proc(node: ^Node, sibling: ^Node) -> (success: b32) ---
+
+	// Replaces 'oldnode' with 'newnode' and unlinks 'oldnode'
+	// (but does not free its memory).
+	// Returns `true` on success, `false` on failure.
+	node_replace :: proc(old_node: ^Node, new_node: ^Node) -> (success: b32) ---
+
+	// Adds 'child' to the beginning of the children of `node`.
+	// Returns `true` on success, `false` on failure.
+	node_prepend_child :: proc(node: ^Node, child: ^Node) -> (success: b32) ---
+
+	// Adds 'child' to the end of the children of `node`.
+	// Returns `true` on success, `false` on failure.
+	node_append_child :: proc(node: ^Node, child: ^Node) -> (success: b32) ---
+
+	// Consolidates adjacent text nodes.
+	consolidate_text_nodes :: proc(root: ^Node) ---
+}
+
+Parser :: distinct rawptr
+
+@(default_calling_convention="c", link_prefix="cmark_")
+foreign lib {
+	// Creates a new parser object.
+	parser_new :: proc(options: Options) -> (parser: ^Parser) ---
+
+	// Creates a new parser object with the given memory allocator.
+	parser_new_with_mem :: proc(options: Options, mem: ^Allocator) -> (parser: ^Parser) ---
+
+	// Frees memory allocated for a parser object.
+	parser_free :: proc(parser: ^Parser) ---
+
+	// Feeds a string of length 'len' to 'parser'.
+	parser_feed :: proc(parser: ^Parser, buffer: [^]byte, len: c.size_t) ---
+
+	// Finish parsing and return a pointer to a tree of nodes.
+	parser_finish :: proc(parser: ^Parser) -> (root: ^Node) ---
+
+	// Parse a CommonMark document in 'buffer' of length 'len'.
+	// Returns a pointer to a tree of nodes. The memory allocated for
+	// the node tree should be released using 'node_free' when it is no longer needed.
+	parse_document :: proc(buffer: [^]byte, len: c.size_t, options: Options) -> (root: ^Node) ---
+
+	// Parse a CommonMark document in file 'f', returning a pointer to a tree of nodes.
+	// The memory allocated for the node tree should be released using 'node_free'
+	// when it is no longer needed.
+	//
+	// Called `parse_from_libc_file` so as not to confuse with Odin's file handling.
+
+	@(link_name = "parse_from_file")
+	parse_from_libc_file :: proc(file: ^libc.FILE, options: Options) -> (root: ^Node) ---
+}
+
+parser_feed_from_string :: proc "c" (parser: ^Parser, s: string) {
+	parser_feed(parser, raw_data(s), len(s))
+}
+parse_document_from_string :: proc "c" (s: string, options: Options) -> (root: ^Node) {
+	return parse_document(raw_data(s), len(s), options)
+}
+
+// Rendering
+@(default_calling_convention="c", link_prefix="cmark_")
+foreign lib {
+	// Render a `node` tree as XML.
+	// It is the caller's responsibilityto free the returned buffer.
+	render_xml :: proc(root: ^Node, options: Options) -> (xml: cstring) ---
+
+	// Render a `node` tree as an HTML fragment.
+	// It is up to the user to add an appropriate header and footer.
+	// It is the caller's responsibility to free the returned buffer.
+	render_html :: proc(root: ^Node, options: Options) -> (html: cstring) ---
+
+	// Render a `node` tree as a groff man page, without the header.
+	// It is the caller's responsibility to free the returned buffer.
+	render_man :: proc(root: ^Node, options: Options, width: c.int) -> (groff: cstring) ---
+
+	// Render a `node` tree as a commonmark document.
+	// It is the caller's responsibility to free the returned buffer.
+	render_commonmark :: proc(root: ^Node, options: Options, width: c.int) -> (commonmark: cstring) ---
+
+	// Render a `node` tree as a LaTeX document.
+	// It is the caller's responsibility to free the returned buffer.
+	render_latex :: proc(root: ^Node, options: Options, width: c.int) -> (latex: cstring) ---
+}
+
+// Helpers to free results from `render_*`.
+free_rawptr :: proc "c" (ptr: rawptr) {
+	cmm := get_default_mem_allocator()
+	cmm.free(ptr)
+}
+free_cstring :: proc "c" (str: cstring) {
+	free_rawptr(rawptr(str))
+}
+free_string :: proc "c" (s: string) {
+    free_rawptr(raw_data(s))
+}
+free :: proc{free_rawptr, free_cstring}
+
+// Wrap CMark allocator as Odin allocator
+@(private)
+cmark_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
+                           size, alignment: int,
+                           old_memory: rawptr, old_size: int, loc := #caller_location) -> (res: []byte, err: runtime.Allocator_Error) {
+
+	cmark_alloc := cast(^Allocator)allocator_data
+	switch mode {
+	case .Alloc:
+		ptr := cmark_alloc.calloc(1, c.size_t(size))
+		res  = transmute([]byte)runtime.Raw_Slice{ptr, size}
+		return res, nil
+
+	case .Free:
+		cmark_alloc.free(old_memory)
+		return nil, nil
+
+	case .Free_All:
+		return nil, .Mode_Not_Implemented
+
+	case .Resize:
+		new_ptr := cmark_alloc.realloc(old_memory, c.size_t(size))
+		res = transmute([]byte)runtime.Raw_Slice{new_ptr, size}
+		if size > old_size {
+			runtime.mem_zero(raw_data(res[old_size:]), size - old_size)
+		}
+		return res, nil
+
+	case .Query_Features:
+		return nil, nil
+
+	case .Query_Info:
+		return nil, .Mode_Not_Implemented
+	}
+	return nil, nil
+}
+
+get_default_mem_allocator_as_odin :: proc() -> runtime.Allocator {
+	return runtime.Allocator{
+		procedure = cmark_allocator_proc,
+		data = rawptr(get_default_mem_allocator()),
+	}
+}

BIN
vendor/commonmark/cmark_static.lib


+ 117 - 0
vendor/commonmark/doc.odin

@@ -0,0 +1,117 @@
+// +ignore
+/*
+	Bindings against CMark (https://github.com/commonmark/cmark)
+
+	Original authors: John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
+	See LICENSE for license details.
+*/
+package commonmark
+
+/*
+	Parsing - Simple interface:
+
+	```odin
+	import cm "vendor:commonmark"
+
+	hellope_world :: proc() {
+		fmt.printf("CMark version: %v\n", cm.version_string())
+
+		str := "Hellope *world*!"
+		root := cm.parse_document(raw_data(str), len(str), cm.DEFAULT_OPTIONS)
+		defer cm.node_free(root)
+
+		html := cm.render_html(root, cm.DEFAULT_OPTIONS)
+		defer cm.free(html)
+
+		fmt.println(html)
+	}
+	```
+
+	Parsing - Streaming interface:
+
+	```odin
+	import cm "vendor:commonmark"
+
+	streaming :: proc() {
+		using cm
+
+		STR :: "Hellope *world*!\n\n"
+		N   :: 50
+		STREAM_SIZE :: 42
+
+		str_buf: [len(STR) * N]u8
+		for i in 0..<N {
+			copy(str_buf[i*len(STR):], STR)
+		}
+
+		parser := parser_new(DEFAULT_OPTIONS)
+		defer parser_free(parser)
+
+		buf := str_buf[:]
+		for len(buf) > STREAM_SIZE {
+			parser_feed(parser, raw_data(buf), STREAM_SIZE)
+			buf = buf[STREAM_SIZE:]
+		}
+
+		if len(buf) > 0 {
+			parser_feed(parser, raw_data(buf), len(buf))
+			buf = buf[len(buf):]
+		}
+
+		root := parser_finish(parser)
+		defer cm.node_free(root)
+
+		html := cm.render_html(root, cm.DEFAULT_OPTIONS)
+		defer cm.free(html)
+
+		fmt.println(html)
+	}
+
+	```
+
+	An iterator will walk through a tree of nodes, starting from a root
+	node, returning one node at a time, together with information about
+	whether the node is being entered or exited.
+
+	The iterator will first descend to a child node, if there is one.
+	When there is no child, the iterator will go to the next sibling.
+	When there is no next sibling, the iterator will return to the parent
+	(but with an `Event_Type.Exit`).
+
+	The iterator will return `.Done` when it reaches the root node again.
+
+	One natural application is an HTML renderer, where an `.Enter` event
+	outputs an open tag and an `.Exit` event outputs a close tag.
+
+	An iterator might also be used to transform an AST in some systematic
+	way, for example, turning all level-3 headings into regular paragraphs.
+
+	```odin
+    usage_example(root: ^Node) {
+        ev_type: Event_Type
+        iter := iter_new(root)
+        defer iter_free(iter)
+        for {
+            ev_type = iter_next(iter)
+            if ev_type == .Done do break
+            cur := iter_get_node(iter)
+            // Do something with `cur` and `ev_type`
+        }
+    }
+    ```
+
+	Iterators will never return `.Exit` events for leaf nodes,
+	which are nodes of type:
+
+	* HTML_Block
+	* Thematic_Break
+	* Code_Block
+	* Text
+	* Soft_Break
+	* Line_Break
+	* Code
+	* HTML_Inline
+
+	Nodes must only be modified after an `.Exit` event, or an `.Enter` event for
+	leaf nodes.
+*/

+ 1 - 12
vendor/darwin/Foundation/NSWindow.odin

@@ -103,18 +103,7 @@ Window_alloc :: proc() -> ^Window {
 
 
 @(objc_type=Window, objc_name="initWithContentRect")
 @(objc_type=Window, objc_name="initWithContentRect")
 Window_initWithContentRect :: proc (self: ^Window, contentRect: Rect, styleMask: WindowStyleMask, backing: BackingStoreType, doDefer: bool) -> ^Window {
 Window_initWithContentRect :: proc (self: ^Window, contentRect: Rect, styleMask: WindowStyleMask, backing: BackingStoreType, doDefer: bool) -> ^Window {
-	self := self
-	// HACK: due to a compiler bug, the generated calling code does not
-	// currently work for this message. Has to do with passing a struct along
-	// with other parameters, so we don't send the rect here.
-	// Omiting the rect argument here actually works, because of how the C
-	// calling conventions are defined.
-	self = msgSend(^Window, self, "initWithContentRect:styleMask:backing:defer:", styleMask, backing, doDefer)
-
-	// apply the contentRect now, since we did not pass it to the init call
-	msgSend(nil, self, "setContentSize:", contentRect.size)
-	msgSend(nil, self, "setFrameOrigin:", contentRect.origin)
-	return self
+	return msgSend(^Window, self, "initWithContentRect:styleMask:backing:defer:", contentRect, styleMask, backing, doDefer)
 }
 }
 @(objc_type=Window, objc_name="contentView")
 @(objc_type=Window, objc_name="contentView")
 Window_contentView :: proc(self: ^Window) -> ^View {
 Window_contentView :: proc(self: ^Window) -> ^View {

+ 1 - 1
vendor/sdl2/sdl2.odin

@@ -200,7 +200,7 @@ Locale :: struct {
 
 
 @(default_calling_convention="c", link_prefix="SDL_")
 @(default_calling_convention="c", link_prefix="SDL_")
 foreign lib {
 foreign lib {
-	GetPreferredLocales :: proc() -> ^Locale ---
+	GetPreferredLocales :: proc() -> [^]Locale ---
 }
 }
 
 
 // misc
 // misc

+ 1 - 1
vendor/wasm/js/runtime.js

@@ -1340,7 +1340,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
 			sqrt:    (x) => Math.sqrt(x),
 			sqrt:    (x) => Math.sqrt(x),
 			sin:     (x) => Math.sin(x),
 			sin:     (x) => Math.sin(x),
 			cos:     (x) => Math.cos(x),
 			cos:     (x) => Math.cos(x),
-			pow:     (x) => Math.pow(x),
+			pow:     (x, power) => Math.pow(x, power),
 			fmuladd: (x, y, z) => x*y + z,
 			fmuladd: (x, y, z) => x*y + z,
 			ln:      (x) => Math.log(x),
 			ln:      (x) => Math.log(x),
 			exp:     (x) => Math.exp(x),
 			exp:     (x) => Math.exp(x),

Some files were not shown because too many files changed in this diff