Code Monkey home page Code Monkey logo

sledge-formats's Introduction

Half-Life Formats Library

These C# libraries parse the formats you'll find in your Half-Life install directory. These are low-level libraries intended for developers. Some additional formats are included to include other related formats, such as those found in the Source and Quake engines.

Install using NuGet

Nuget Nuget Nuget Nuget Nuget Nuget Nuget

Currently Supported Formats

  • Sledge.Formats - Small formats, or formats that are shared
    • Id
      • MipTexture - Quake MipTexture used in WAD and BSP files
    • Valve
      • Liblist - The format used for the liblist.gam file.
      • SerialisedObject - The format used for many Valve config files used in Steam HL, Source games, and Steam. Some file types that use this format are VMT, VMF, RES, VDF, gameinfo.txt, and many text files found in Source game directories.
    • Pointfile - .lin & .pts pointfiles generated by map compilers where there's a leak
  • Sledge.Formats.Map - Map source files used by level editors
    • Formats
      • HammerVmfFormat - The format used by Valve Hammer Editor 4 for map source files.
      • JackhammerJmfFormat - Experimental support for Jackhammer's JMF format
        • Only supports JMF v121
      • QuakeMapFormat - The format used by most Quake engines for map source files.
        • Supports the formats used in Quake 1 (idTech2) and Half-Life 1 .map files.
        • idTech3 and idTech4 .map files are not currently supported.
      • WorldcraftPrefabLibrary - .ol prfab files created by Worldcraft/Hammer.
        • Prefab libraries are just bundled RMF files, so the version restrictions apply to the RMFs inside the prefabs as well. Some of the default prefabs that ship with Worldcraft/Hammer will not load currently because of this.
      • WorldcraftRmfFormat - The format used by Valve Hammer Editor 3 for map source files.
        • RMF version 1.6 and up (Worldcraft 1.5b+) is currently supported.
  • Sledge.Formats.Bsp - Compiled map files used by the engine
    • BspFile - A format used by Quake based engines for compiled maps.
      • Currently supports Quake 1 (v29), Quake 2 (IBSP v38), and Half-Life 1 (v30) bsp formats.
      • Not currently supported: BSP2 (DarkPlaces engine), Quake 3 (IBSP v46), Source (VBSP v17-21)
      • Currently, visibility data is not parsed, it is kept as a binary blob.
      • Editing of lightmap data is currently not well supported and must be done manually.
      • The library does no checking to ensure that the indexes and offsets are correct. Possibly a higher-level library could wrap around this format to provide developers with a more flexible BSP creation experience.
  • Sledge.Formats.Packages - File package formats used by Quake/HL1/Source
    • PakPackage - The PAK format used in Quake 1/2 and non-Steam Half-Life.
    • VpkPackage - The VPK format used by post-SteamPipe Source engine games.
  • Sledge.Formats.Texture - Texture formats used by Quake and Half-Life
    • Wad
      • WadFile - The WAD format used by Quake 1 and Goldsource to store textures
        • Currently supports Quake 1 (WAD2) and Goldsource (WAD3) formats
        • The ColorMap and ColorMap2 will load, but won't contain any data. These lump types aren't used anywhere.
        • Quake 1's gfx.wad contains a lump called "CONCHARS", which has an invalid type. There's special logic to handle this lump.
    • Vtf
      • VtfFile - The VTF format used by the Source engine.
        • Currently supports all formats that VtfLib supports
  • Sledge.Formats.Texture.ImageSharp - ImageSharp extensions to allow creation of textures
    • Currently only supports VTF formats
  • Sledge.Formats.GameData - Game data formats used by level editors
    • Fgd - The FGD format used by Worldcraft, Valve Hammer Editor, JACK, TrenchBroom, Sledge, and other editors
      • Source 1 & 2 FGDs will load as long as they are valid, but their use is not fully tested.
      • FGD files with invalid syntax will usually work, but in some cases will not be as lenient as Hammer/JACK can be

Unsupported formats (may be added in the future)

  • MDL, used for models in many Quake-based games.
    • MDL v6 (IDPO), used in Quake 1.
    • MDL v10 (IDSQ/IDST), used in Half-Life, which adds skeletal animation.
    • MDL v44-49 (IDSQ/IDST), used in Source, along with VTX, VVD, ANI, and PHY files. This is a very complex format, so it's not high priority.
  • DEF, game data format used by older Quake editors

Unsupported formats (probably won't be added)

  • GCF, used by pre-SteamPipe Steam Half-Life - this format is no longer in use, so it's not really useful to create a library for it. Use HLLib if you need to work with these files.
  • PK3, used in Quake 3 games - this format is just a zip file with a different extension. Other libraries (including .NET itself) already have good support for zip files.
  • WAD1, used in the Doom engine - this is a bit too far out of scope for this project, which is focused mostly on Half-Life 1.
  • MD2 and MD3, used for models in non-Valve Quake engines - MDL formats are extremely complex and not very well documented, so these are considered out of scope for this project for now.
  • Anything introduced in Doom 3, Source 2 or newer engines.

sledge-formats's People

Contributors

duude92 avatar jarroddoyle avatar logicandtrick avatar samvanheer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

sledge-formats's Issues

Issues reading data from GetBgra32Data

I've been trying convert TF2 sprays into bmp files, but the images end up all warped and I'm unable to pinpoint the issue.

This is the original image:
image

And this is what I end up with:
Output

Here's a snipet of my code:
image

Perhaps I misinterpreted how the pixels in the byte array are organized? I couldn't find much information about it.
The spray is in the Dxt1 format.

Texture lumps are written incorrectly

When writing a wad file the texture lumps are written without padding them to a 4 byte boundary like qlumpy does: https://github.com/ValveSoftware/halflife/blob/c7240b965743a53a29491dd49320c88eecf6257b/utils/qlumpy/qlumpy.c#L197-L201

The resulting wad files can't be read properly by Wally because the workaround for an old Wally bug happens to trigger here: https://github.com/Ty-Matthews-VisualStudio/Wally/blob/e8394c574e8efd63da3b56d2ea8f27e13ff3af32/Source/Wally/WADList.cpp#L104-L121

Additionally the padding byte for lump info is written as the value 2 instead of 0 which shows up when doing a binary diff.

More things to support in Source 2 fgd

There can be dictionaries in properties:
color(color255) { enabled={ variable="colormode" value="0" } }
fade_size_start(float) { group="Render" min="0.0" max="1.0" enabled={ variable="directlight" values=["2", "3"] } }

Adding this code after Source 2 metadata for properties: works:

                /* Source 2 metadata for properties:
                color(color255) { enabled={ variable="colormode" value="0" } } : "Color" : "255 255 255"
                luminaire_shape(choices) { group="Luminaire" }: "Area Light" : "0" : "Shape of the area light"
                 */
                if (it.Current?.Is(TokenType.Symbol, Symbols.OpenBrace) == true)
                {
                    var dict = new GameDataDictionary(string.Empty);
                    ParseGameDataDictionary(it, dict);
                }

However ParseGameDataDictionary also needs to be updated to support arrays: values=["2", "3"].

Arrays can also appear in class metadata:

	metadata
	{
		model_archetypes = [ "generic_actor_model" ]
	}

There's also property type that has a slash and ampersand in it which fails to parse:
light_style( vdata_choice:scripts/light_styles.vdata )
item_1( vdata_choice:scripts/grenades.vdata&scripts/misc.vdata&scripts/npc_abilities.vdata )

I don't know if you want to add / and & in Tokeniser.TokenName.

There's three new variable types:

        VDataChoice, // Source 2
        SubclassChoice, // Source 2
        NPCAbilityName, // Source 2
        PathNodeClass, // Source 2
        Color255Alpha, // Source 2
        ParticleCfg, // Source 2
        ModelClothVertexMap, // Source 2

Files:
https://github.com/SteamDatabase/GameTracking-Dota2/blob/master/game/core/lights2.fgd
https://github.com/SteamDatabase/GameTracking-Dota2/blob/master/game/core/ai_defaultnpc.fgd


There's also @VData and @VDataDerived commands here: https://github.com/SteamDatabase/GameTracking-Dota2/blob/master/game/core/vdata_base.fgd


There's @helpinfo command:

@helpinfo( "ai_basenpc", "tools/help/fgd/ai_basenpc.txt" )

SerialisedObjectFormatter fails to parse property with serialized JSON inside

The easiest way to reproduce is to try and parse steam\userdata\<user_id>\7\remote\sharedconfig.vdf, which has such property at UserLocalConfigStore>Software>Valve>Steam>FriendsUI>FriendsUIJSON.

Here's an excerpt:

"FriendsUI"
{
	"FriendsUIJSON"		"{\"bNotifications_ShowIngame\":false,\"bNotifications_ShowOnline\":false,\"bNotifications_ShowMessage\":true,\"bNotifications_EventsAndAnnouncements\":true,\"bSounds_PlayIngame\":false,\"bSounds_PlayOnline\":false,\"bSounds_PlayMessage\":true,\"bSounds_EventsAndAnnouncements\":false,\"bAlwaysNewChatWindow\":false,\"bForceAlphabeticFriendSorting\":false,\"nChatFlashMode\":1,\"bRememberOpenChats\":false,\"bCompactQuickAccess\":true,\"bCompactFriendsList\":true,\"bNotifications_ShowChatRoomNotification\":false,\"bSounds_PlayChatRoomNotification\":false,\"bHideOfflineFriendsInTagGroups\":false,\"bHideCategorizedFriends\":true,\"bCategorizeInGameFriendsByGame\":false,\"nChatFontSize\":2,\"b24HourClock\":true,\"bDoNotDisturbMode\":true,\"bDisableEmbedInlining\":false,\"bSignIntoFriends\":true,\"bDisableSpellcheck\":false,\"bDisableRoomEffects\":false,\"bAnimatedAvatars\":true,\"featuresEnabled\":{\"DoNotDisturb\":1,\"LoaderWindowSynchronization\":1,\"NonFriendMessageHandling\":1,\"NewVoiceHotKeyState\":1,\"PersonaNotifications\":1,\"ServerVirtualizedMemberLists\":1,\"SteamworksChatAPI\":1},\"bAutoSignIntoFriends\":true,\"bShowTimeInChatLogCheck\":true,\"bSingleWindowMode\":false}"
}

Surface and content flags crash when parsing Valve formats

Hello it's me again :D

When parsing valve formats as output by TrenchBroom (e.g. "Quake 2 (Valve)", "Quake 3 (Valve)"), if any surface has a surface or content flag the QuakeMapFormat will crash with the following error:

System.Exception: Parsing error (line 10, column 83): Unexpected token value Whitespace( )
   at Sledge.Formats.Tokens.TokenParsing.Expect(IEnumerator`1 it, TokenType type, Predicate`1 valueChecker) in E:\Projects\C#\sledge-formats\Sledge.Formats\Tokens\TokenParsing.cs:line 29
   at Sledge.Formats.Map.Formats.QuakeMapFormat.ReadSolid(IEnumerator`1 it) in E:\Projects\C#\sledge-formats\Sledge.Formats.Map\Formats\QuakeMapFormat.cs:line 180
   at Sledge.Formats.Map.Formats.QuakeMapFormat.ReadEntity(IEnumerator`1 it) in E:\Projects\C#\sledge-formats\Sledge.Formats.Map\Formats\QuakeMapFormat.cs:line 155
   at Sledge.Formats.Map.Formats.QuakeMapFormat.Read(Stream stream) in E:\Projects\C#\sledge-formats\Sledge.Formats.Map\Formats\QuakeMapFormat.cs:line 111

I assume this is because it's expecting a new line but got the contentflags number instead. Here is a map file that can reproduce this error:

// Format: Quake2 (Valve)
// entity 0
{
"mapversion" "220"
"classname" "worldspawn"
"_tb_textures" "textures"
// brush 0
{
( -64 -64 -16 ) ( -64 -63 -16 ) ( -64 -64 -15 ) __TB_empty [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 1 0 0
( -64 -64 -16 ) ( -64 -64 -15 ) ( -63 -64 -16 ) __TB_empty [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 1 0 0
( -64 -64 -16 ) ( -63 -64 -16 ) ( -64 -63 -16 ) __TB_empty [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 1 0 0
( 64 64 16 ) ( 64 65 16 ) ( 65 64 16 ) __TB_empty [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 1 0 0
( 64 64 16 ) ( 65 64 16 ) ( 64 64 17 ) __TB_empty [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 1 0 0
( 64 64 16 ) ( 64 64 17 ) ( 64 65 16 ) __TB_empty [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 1 0 0
}
}

I think the "Valve" format is the same as the "Worldcraft" format but with the contentflags surfaceflags value values tacked on at the end like in "idTech3" format.

// Worldcraft:
( x y z ) ( x y z ) ( x y z ) texturename [ ux uy uz xshift ] [ vx vy vz yshift ] rotation xscale yscale
// Valve:
( x y z ) ( x y z ) ( x y z ) texturename [ ux uy uz xshift ] [ vx vy vz yshift ] rotation xscale yscale contentflags surfaceflags value

Quake map face texture names are not always read correctly

Some of the WADs I'm using use the old standard of prefixing certain textures with special characters to denote their type. One of these characters is {. The .map parser incorrectly drops this symbol and reads a face with texture name {stone_blck2_f1 as stone_blck2_f1.

Standardise construction methods for each type

Currently there are multiple different ways to create an object:

  • SerialisedObject: SerialisedObjectFormatter.Deserialize(Stream)
  • Liblist: Liblist(Stream)
  • BspFile: BspFile(Stream)
  • MapFile: WorldcraftRmfFormat.Read(Stream)
  • IPackage: VpkPackage(string)
  • VtfFile: VtfFile(Stream stream)
  • WadFile: WadFile(Stream stream, bool = true)

Each way can be justified individually, but together they're all just inconsistent. At the very least, provide a Constructor(Stream) option for all type. VPK and possibly MDL and FGD (once implemented) would need to be a FileStream at least, as those need to hit the disk to load extra data. Investigate supporting a virtual file system interface to facilitate non-traditional loading environments for formats with dependencies.

Wad file lumps should have a default constructor

Wad file lumps currently do not allow default construction:
https://github.com/LogicAndTrick/sledge-formats/blob/b9b5f956a694a5faa9a307afb364c090573c2d05/Sledge.Formats.Texture/Wad/Lumps/MipTextureLump.cs

When creating new wad files this requires the lumps to be re-implemented to support this:

internal sealed class JobMipTextureLump : MipTexture, ILump
{
    public LumpType Type => LumpType.MipTexture;

    public int Write(BinaryWriter bw)
    {
        var pos = bw.BaseStream.Position;
        Write(bw, true, this);
        return (int)(bw.BaseStream.Position - pos);
    }
}
 WadFile wadFile = new(WadVersion.Wad3);

 foreach (var texture in bspFile.Textures.Where(t => t.NumMips > 0))
 {
     JobMipTextureLump lump = new()
     {
         Name = texture.Name,
         Width = texture.Width,
         Height = texture.Height,
         NumMips = texture.NumMips,
         MipData = texture.MipData,
         Palette = texture.Palette
     };

     wadFile.AddLump(texture.Name, lump);
 }

 using var stream = File.Open(wadFileName, FileMode.Create);

 wadFile.Write(stream);

Adding a public default constructor to the lumps should suffice.

BSP Entities lump writes "classname" and "model" keys twice on write

When a BSP file's entities lump is written it writes the classname and model keys twice. This is because it writes both all keyvalues and these keyvalues explicitly:

if (entity.Model > 0)
{
sb.Append($"\"model\" \"*{entity.Model}\"\n");
}
foreach (var kv in entity.KeyValues.Where(x => x.Key?.Length > 0 && x.Value?.Length > 0))
{
sb.Append($"\"{kv.Key}\" \"{kv.Value}\"\n");
}
if (entity.ClassName?.Length > 0)
{
sb.Append($"\"classname\" \"{entity.ClassName}\"\n");
}

Both keyvalues are added to the list of keyvalues on load:

void SetKeyValue(Entity e, string k, string v)
{
e.KeyValues[k] = v;
switch (k)
{
case "classname":
e.ClassName = v;
break;
case "model":
if (int.TryParse(v.Substring(1), out var m)) e.Model = m;
break;
}
}

It should use one or the other to save these keyvalues. It might be better to remove the explicit keyvalue properties for classname and model and rely solely on the keyvalues dictionary, since users will likely have their own wrapper around this data.

Support creating VTF files

I'm trying to make a nextbot generator but I couldn't figure out how to convert a png to vtf. I decided to post this here so that this can maybe could get added.

MDL support?

Very nice and easy to use, would like to see mdl supported, so far what I need is to read mdl infos, like material and texture names, so I can find them for this mdl automatically, don't know how to do that myself though.

Textures lump deserialization handles invalid offsets incorrectly

When the Textures lump deserializes textures invalid offsets are ignored, but no texture is added:

foreach (var offset in offsets)
{
if (offset < 0) continue; // ignore negative offsets
br.BaseStream.Seek(blob.Offset + offset, SeekOrigin.Begin);
var tex = MipTexture.Read(br, version == Version.Goldsource);
_textures.Add(tex);
}

Maps that have invalid offsets will thus have Texinfo objects that refer to incorrect textures and to textures with an index exceeding the size of the Textures lump.

The engine handles this differently. It allocates a list of nummiptex textures, zero initializes all of them and loads them one by one. Textures with invalid offsets are skipped, leaving the texture object as all zeroes.

See https://github.com/id-Software/Quake/blob/bf4ac424ce754894ac8f1dae6a3981954bc9852d/WinQuake/gl_model.c#L358-L364

The compiler initializes the offsets here:
https://github.com/ValveSoftware/halflife/blob/c7240b965743a53a29491dd49320c88eecf6257b/utils/qcsg/textures.c#L353-L368

The correct way to handle this case here would be to create a MipTexture object with an empty name and defaulted properties.

This is known to happen with cz_recoil, cz_stadium_cz and possibly Sven Co-op's grunts2.

Entities lump does not write null terminator

When the Entities lump writes the entity data string to the stream it writes the string but it doesn't append a null terminator:

bw.Write(Encoding.ASCII.GetBytes(sb.ToString()));

In most cases there will be a null byte right after the entities lump, but in some cases such as c1a2c this will not be the case. The engine will continue to parse data past the entity data string and will fail to read a { character, causing a Host_Error: ED_LoadFromFile: found � when expecting { error.

Although the engine does copy the string before parsing it, the lack of null terminator in the file itself seems to consistently affect parsing as well.

Comparing the bsp file before and after modification shows the entities lump is shorter by one byte.

Adding this after the string is written should fix this problem:

bw.Write((byte)0);

Can't access fgd behaviour dictionary in S2

@PointClass
	base( Targetname, Parentname, CanBeClientOnly, Light2Inputs )
	omnilight()
	metadata
	{
		entity_tool_name = "Omni Light"
		entity_tool_tip = "Omni directional light source"
		entity_tool_group = "Lighting"
		entity_tool_sortname = "lights2"
		default_pitch = 90.0
	}
	iconsprite
	{
		image = "materials/editor/light_omni.vmat"
		tintKey = "color"
	}
= light_omni2 : "A sphere or tube shaped light"

In this example, class.Behaviour for iconsprite has no values, and the actual values are in class.Dictionaries, but as far as I can tell there is no way to know which dictionary to access for this behaviour since I don't know which dictionary index it is.

[QuakeMapFormat] System.FormatException: Input string was not in a correct format

When using the QuakeMapFormat class on this map file (see below) I get a System.FormatException: Input string was not in a correct format with the following stack trace:

Unhandled exception. System.FormatException: Input string was not in a correct format.
   at System.Number.ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type)
   at System.Decimal.Parse(String s, NumberStyles style)
   at Sledge.Formats.Map.Formats.QuakeMapFormat.ParseDecimal(IEnumerator`1 it)
   at Sledge.Formats.Map.Formats.QuakeMapFormat.ReadTextureAxis(IEnumerator`1 it)
   at Sledge.Formats.Map.Formats.QuakeMapFormat.ReadFace(IEnumerator`1 it)
   at Sledge.Formats.Map.Formats.QuakeMapFormat.ReadSolid(IEnumerator`1 it)
   at Sledge.Formats.Map.Formats.QuakeMapFormat.ReadEntity(IEnumerator`1 it)
   at Sledge.Formats.Map.Formats.QuakeMapFormat.Read(Stream stream)

The map file is short and simple, it is the default map with the texture replaced. The texture is the "Icon.png" file in the "Generic" game mode folder (not that it matters... probably).

The map was created using TrenchBroom and is in the "Valve" format as specified here: https://trenchbroom.github.io/manual/latest/#compiling_maps:~:text=for%20more%20information.-,File%20Formats,-The%20file%20format

{
"mapversion" "220"
"classname" "worldspawn"
"_tb_textures" "textures"
// brush 0
{
( -64 -64 -16 ) ( -64 -63 -16 ) ( -64 -64 -15 ) Icon [ 0 -0.9999999999999848 -1.7484556036340138e-07 0 ] [ 0 1.7484556036340138e-07 -0.9999999999999848 0 ] 360 1 1
( -64 -64 -16 ) ( -64 -64 -15 ) ( -63 -64 -16 ) Icon [ 1 0 -0 55 ] [ 0 -0 -1 64 ] 0 1 1
( -64 -64 -16 ) ( -63 -64 -16 ) ( -64 -63 -16 ) Icon [ -0.9999999999999848 -1.7484556036340138e-07 0 0 ] [ 1.7484556036340138e-07 -0.9999999999999848 0 0 ] 360 1 1
( 64 64 16 ) ( 64 65 16 ) ( 65 64 16 ) Icon [ 0.9999999999999848 -1.7484556036340138e-07 0 0 ] [ -1.7484556036340138e-07 -0.9999999999999848 0 0 ] 360 1 1
( 64 64 16 ) ( 65 64 16 ) ( 64 64 17 ) Icon [ -0.9999999999999848 0 -1.7484556036340138e-07 0 ] [ 1.7484556036340138e-07 0 -0.9999999999999848 0 ] 360 1 1
( 64 64 16 ) ( 64 64 17 ) ( 64 65 16 ) Icon [ 0 0.9999999999999848 -1.7484556036340138e-07 0 ] [ 0 -1.7484556036340138e-07 -0.9999999999999848 0 ] 360 1 1
}
}

Package version: 1.0.4 (NuGet)

Incorrect handling of // in serialized objects

Right now SerializedObjectFormatter.CleanLine() truncates anything after // token even if it's inside a valid string (such as URL).
It should be only removed if it's outside the string or the sole content of the line.

Example:

"web"
{
	"WebFav0_URL"		"https://www.google.com/"
	"WebFav0_Name"		"Google"
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.