private void ForEachGlyph <T>(CommandList commandList, ref StringProxy text, ref Vector2 requestedFontSize, GlyphAction <T> action, ref T parameters, TextAlignment scanOrder, bool updateGpuResources, Vector2?textBoxSize = null) { if (scanOrder == TextAlignment.Left) { // scan the whole text only one time following the text letter order ForGlyph(commandList, ref text, ref requestedFontSize, action, ref parameters, 0, text.Length, updateGpuResources); } else { // scan the text line by line incrementing y start position // measure the whole string in order to be able to determine xStart var wholeSize = textBoxSize ?? MeasureString(ref text, ref requestedFontSize); // scan the text line by line var yStart = 0f; var startIndex = 0; var endIndex = FindCariageReturn(ref text, 0); while (startIndex < text.Length) { // measure the size of the current line var lineSize = Vector2.Zero; ForGlyph(commandList, ref text, ref requestedFontSize, MeasureStringGlyph, ref lineSize, startIndex, endIndex, updateGpuResources); // Determine the start position of the line along the x axis // We round this value to the closest integer to force alignment of all characters to the same pixels // Otherwise the starting offset can fall just in between two pixels and due to float imprecision // some characters can be aligned to the pixel before and others to the pixel after, resulting in gaps and character overlapping var xStart = (scanOrder == TextAlignment.Center) ? (wholeSize.X - lineSize.X) / 2 : wholeSize.X - lineSize.X; xStart = (float)Math.Round(xStart); // scan the line ForGlyph(commandList, ref text, ref requestedFontSize, action, ref parameters, startIndex, endIndex, updateGpuResources, xStart, yStart); // update variable before going to next line yStart += GetTotalLineSpacing(requestedFontSize.Y); startIndex = endIndex + 1; endIndex = FindCariageReturn(ref text, startIndex); } } }
private static Vector2 MeasureString(Font font, StringProxy text, int start, int end) { if (text.Length == 0) { return(Vector2.Zero); } float currentLength = 0; float maxLength = 0; int lines = 1; for (int i = start; i <= end; i++) { char character = text[i]; if (character != '\r') { if (character != '\n') { var fontChar = font.GetFontChar(character); int length = fontChar.XAdvance; if (i < text.Length - 1) { length += font.GetKerning(character, text[i + 1]); } currentLength += length; maxLength = Math.Max(maxLength, currentLength); } else { currentLength = 0; lines++; } } } return(new Vector2(maxLength, lines * font.Definition.Common.LineHeight)); }
// Token: 0x06001146 RID: 4422 RVA: 0x0001BD30 File Offset: 0x00019F30 public static Coroutine LoginMemberEmail(string email, string password, ChannelType channelType, string machineId, Action <MemberAuthenticationResultView> callback, Action <Exception> handler) { Coroutine result; using (MemoryStream memoryStream = new MemoryStream()) { StringProxy.Serialize(memoryStream, email); StringProxy.Serialize(memoryStream, password); EnumProxy <ChannelType> .Serialize(memoryStream, channelType); StringProxy.Serialize(memoryStream, machineId); result = MonoInstance.Mono.StartCoroutine(SoapClient.MakeRequest("IAuthenticationWebServiceContract", "AuthenticationWebService", "LoginMemberEmail", memoryStream.ToArray(), delegate(byte[] data) { if (callback != null) { callback(MemberAuthenticationResultViewProxy.Deserialize(new MemoryStream(data))); } }, handler)); } return(result); }
// Token: 0x060011C0 RID: 4544 RVA: 0x0001D414 File Offset: 0x0001B614 public static Coroutine BuyBundle(string authToken, int bundleId, ChannelType channel, string hashedReceipt, Action <bool> callback, Action <Exception> handler) { Coroutine result; using (MemoryStream memoryStream = new MemoryStream()) { StringProxy.Serialize(memoryStream, authToken); Int32Proxy.Serialize(memoryStream, bundleId); EnumProxy <ChannelType> .Serialize(memoryStream, channel); StringProxy.Serialize(memoryStream, hashedReceipt); result = MonoInstance.Mono.StartCoroutine(SoapClient.MakeRequest("IShopWebServiceContract", "ShopWebService", "BuyBundle", memoryStream.ToArray(), delegate(byte[] data) { if (callback != null) { callback(BooleanProxy.Deserialize(new MemoryStream(data))); } }, handler)); } return(result); }
// Token: 0x060012B7 RID: 4791 RVA: 0x0001FD84 File Offset: 0x0001DF84 public void SendCreateRoom(GameRoomData metaData, string password, string clientVersion, string authToken, string magicHash) { using (MemoryStream memoryStream = new MemoryStream()) { GameRoomDataProxy.Serialize(memoryStream, metaData); StringProxy.Serialize(memoryStream, password); StringProxy.Serialize(memoryStream, clientVersion); StringProxy.Serialize(memoryStream, authToken); StringProxy.Serialize(memoryStream, magicHash); Dictionary <byte, object> customOpParameters = new Dictionary <byte, object> { { this.__id, memoryStream.ToArray() } }; if (this.sendOperation != null) { this.sendOperation(6, customOpParameters, true, 0, false); } } }
// Token: 0x060013D5 RID: 5077 RVA: 0x00023510 File Offset: 0x00021710 private byte[] Serialize(T obj) { byte[] result; using (MemoryStream memoryStream = new MemoryStream()) { Type typeFromHandle = typeof(T); if (typeFromHandle == typeof(int)) { Int32Proxy.Serialize(memoryStream, (int)((object)obj)); } else if (typeFromHandle == typeof(float)) { SingleProxy.Serialize(memoryStream, (float)((object)obj)); } else if (typeFromHandle == typeof(string)) { StringProxy.Serialize(memoryStream, (string)((object)obj)); } result = memoryStream.ToArray(); } return(result); }
// Token: 0x06001272 RID: 4722 RVA: 0x0001F034 File Offset: 0x0001D234 public void SendPlayersReported(List <int> cmids, int type, string details, string logs) { using (MemoryStream memoryStream = new MemoryStream()) { ListProxy <int> .Serialize(memoryStream, cmids, new ListProxy <int> .Serializer <int>(Int32Proxy.Serialize)); Int32Proxy.Serialize(memoryStream, type); StringProxy.Serialize(memoryStream, details); StringProxy.Serialize(memoryStream, logs); Dictionary <byte, object> customOpParameters = new Dictionary <byte, object> { { this.__id, memoryStream.ToArray() } }; if (this.sendOperation != null) { this.sendOperation(21, customOpParameters, true, 0, false); } } }
// Token: 0x06001145 RID: 4421 RVA: 0x0001BC90 File Offset: 0x00019E90 public static Coroutine CompleteAccount(int cmid, string name, ChannelType channel, string locale, string machineId, Action <AccountCompletionResultView> callback, Action <Exception> handler) { Coroutine result; using (MemoryStream memoryStream = new MemoryStream()) { Int32Proxy.Serialize(memoryStream, cmid); StringProxy.Serialize(memoryStream, name); EnumProxy <ChannelType> .Serialize(memoryStream, channel); StringProxy.Serialize(memoryStream, locale); StringProxy.Serialize(memoryStream, machineId); result = MonoInstance.Mono.StartCoroutine(SoapClient.MakeRequest("IAuthenticationWebServiceContract", "AuthenticationWebService", "CompleteAccount", memoryStream.ToArray(), delegate(byte[] data) { if (callback != null) { callback(AccountCompletionResultViewProxy.Deserialize(new MemoryStream(data))); } }, handler)); } return(result); }
public byte[] GetMember(byte[] data) { var inputStream = new MemoryStream(data); var steamId = Encoding.UTF8.GetString(Convert.FromBase64String(StringProxy.Deserialize(inputStream))); var json = File.ReadAllText(playerDataPath); var userData = JsonConvert.DeserializeObject <Dictionary <string, MemberView> >(json); var outputStream = new MemoryStream(); if (userData[steamId] != null) { var instance = new UberstrikeUserViewModel { CmuneMemberView = userData[steamId], UberstrikeMemberView = new UberstrikeMemberView() }; UberstrikeUserViewModelProxy.Serialize(outputStream, instance); } return(outputStream.ToArray()); }
public OperationResponse OnOperationRequest(PeerBase peer, OperationRequest operationRequest, SendParameters sendParameters) { var commPeer = (CommPeer)peer; // Basically all the operations here are to check the integrity of the game instance (Some form of Anti-Cheat). var operationType = (ICommPeerOperationsType)operationRequest.OperationCode; switch (operationType) { // AuthenticationRequest is used to check if the .dlls haven't been modified, // I forgot the exact procedure, but its somewhere on my computer. case ICommPeerOperationsType.AuthenticationRequest: var authData = (byte[])operationRequest.Parameters[OP_ID]; using (var stream = new MemoryStream(authData)) { var authToken = StringProxy.Deserialize(stream); var magicHash = StringProxy.Deserialize(stream); // Not doing much with it at the moment. } commPeer.Lobby.SendMessage(0, "Dropshot", "Test chat message."); break; // SendHeartbeatResponse is used to check if any other assemblies other than allowed // ones are in the AppDomain, but I forgot the exact procedure for this one too, but its // somewhere on my computer. case ICommPeerOperationsType.SendHeartbeatResponse: var heartbeatData = (byte[])operationRequest.Parameters[OP_ID]; using (var stream = new MemoryStream(heartbeatData)) { var authToken = StringProxy.Deserialize(stream); var responseHash = StringProxy.Deserialize(stream); // Not doing much with it at the moment. } break; } return(null); }
byte[] IApplicationWebServiceContract.GetConfigurationData(byte[] data) { try { using (var bytes = new MemoryStream(data)) { var version = StringProxy.Deserialize(bytes); var view = OnGetConfigurationData(version); using (var outBytes = new MemoryStream()) { ApplicationConfigurationViewProxy.Serialize(outBytes, view); return(outBytes.ToArray()); } } } catch (Exception ex) { Log.Error("Unable to handle GetConfigurationData request:"); Log.Error(ex); return(null); } }
public byte[] SetLoadout(byte[] data) { var inputStream = new MemoryStream(data); var steamId = Encoding.UTF8.GetString(Convert.FromBase64String(StringProxy.Deserialize(inputStream))); var loadoutView = LoadoutViewProxy.Deserialize(inputStream); var outputStream = new MemoryStream(); if (loadoutData.ContainsKey(steamId) && loadoutData[steamId] != null) { loadoutData[steamId] = loadoutView; UpdateInventoryData(); Int32Proxy.Serialize(outputStream, (int)MemberOperationResult.Ok); } else { Int32Proxy.Serialize(outputStream, (int)MemberOperationResult.MemberNotFound); } return(outputStream.ToArray()); }
byte[] IUserWebServiceContract.IsDuplicateMemberName(byte[] data) { try { using (var bytes = new MemoryStream(data)) { var username = StringProxy.Deserialize(bytes); var result = OnIsDuplicateMemberName(username); using (var outBytes = new MemoryStream()) { BooleanProxy.Serialize(outBytes, result); return(outBytes.ToArray()); } } } catch (Exception ex) { Log.Error("Unable to handle IsDuplicateMemberName request:"); Log.Error(ex); return(null); } }
byte[] IUserWebServiceContract.GetMember(byte[] data) { try { using (var bytes = new MemoryStream(data)) { var authToken = StringProxy.Deserialize(bytes); var view = OnGetMember(authToken); using (var outBytes = new MemoryStream()) { UberstrikeUserViewProxy.Serialize(outBytes, view); return(outBytes.ToArray()); } } } catch (Exception ex) { Log.Error("Unable to handle GetMember request:"); Log.Error(ex); return(null); } }
private void ForGlyph <T>(CommandList commandList, ref StringProxy text, ref Vector2 fontSize, GlyphAction <T> action, ref T parameters, int forStart, int forEnd, bool updateGpuResources, ref Stack <Color4> colorStack, float startX = 0, float startY = 0, TextVerticalAlignment vertAlign = TextVerticalAlignment.Top, float fontSizeY = 0f, bool skipTags = false) { var key = 0; var x = startX; var y = startY; // tag management var escaping = false; for (var i = forStart; i < forEnd; i++) { var character = text[i]; if (!escaping && character == '<') { // check tags? if (CheckAndProcessColorTag(ref text, ref i, out Color4 color)) { if (skipTags == false) { if (colorStack == null) { colorStack = new Stack <Color4>(); } colorStack.Push(color); } } else if (EndsTag("</color>", ref text, ref i)) { if (skipTags == false && colorStack != null && colorStack.Count > 0) { colorStack.Pop(); } } }
public static GameRoomDataView Deserialize(Stream bytes) { int mask = Int32Proxy.Deserialize(bytes); var view = new GameRoomDataView(); view.ConnectedPlayers = Int32Proxy.Deserialize(bytes); view.GameFlags = Int32Proxy.Deserialize(bytes); view.GameMode = EnumProxy <GameModeType> .Deserialize(bytes); if ((mask & 1) != 0) { view.Guid = StringProxy.Deserialize(bytes); } view.IsPasswordProtected = BooleanProxy.Deserialize(bytes); view.IsPermanentGame = BooleanProxy.Deserialize(bytes); view.KillLimit = Int32Proxy.Deserialize(bytes); view.LevelMax = ByteProxy.Deserialize(bytes); view.LevelMin = ByteProxy.Deserialize(bytes); view.MapID = Int32Proxy.Deserialize(bytes); if ((mask & 2) != 0) { view.Name = StringProxy.Deserialize(bytes); } view.Number = Int32Proxy.Deserialize(bytes); view.PlayerLimit = Int32Proxy.Deserialize(bytes); if ((mask & 4) != 0) { view.Server = ConnectionAddressViewProxy.Deserialize(bytes); } view.TimeLimit = Int32Proxy.Deserialize(bytes); return(view); }
public static void Serialize(Stream stream, PhotonView instance) { int mask = 0; using (var bytes = new MemoryStream()) { if (instance.IP != null) { StringProxy.Serialize(bytes, instance.IP); } else { mask |= 1; } Int32Proxy.Serialize(bytes, instance.MinLatency); if (instance.Name != null) { StringProxy.Serialize(bytes, instance.Name); } else { mask |= 2; } Int32Proxy.Serialize(bytes, instance.PhotonId); Int32Proxy.Serialize(bytes, instance.Port); EnumProxy <RegionType> .Serialize(bytes, instance.Region); EnumProxy <PhotonUsageType> .Serialize(bytes, instance.UsageType); Int32Proxy.Serialize(stream, ~mask); bytes.WriteTo(stream); } }
byte[] IUserWebServiceContract.GetInventory(byte[] data) { try { using (var bytes = new MemoryStream(data)) { var authToken = StringProxy.Deserialize(bytes); var view = OnGetInventory(authToken); using (var outBytes = new MemoryStream()) { ListProxy <ItemInventoryView> .Serialize(outBytes, view, ItemInventoryViewProxy.Serialize); return(outBytes.ToArray()); } } } catch (Exception ex) { Log.Error("Unable to handle GetInventory request:"); Log.Error(ex); return(null); } }
public static void Serialize(Stream stream, ServerConnectionView instance) { int mask = 0; using (var bytes = new MemoryStream()) { EnumProxy <MemberAccessLevel> .Serialize(bytes, instance.AccessLevel); if (instance.ApiVersion != null) { StringProxy.Serialize(bytes, instance.ApiVersion); } else { mask |= 1; } EnumProxy <ChannelType> .Serialize(bytes, instance.Channel); Int32Proxy.Serialize(bytes, instance.Cmid); Int32Proxy.Serialize(stream, ~mask); bytes.WriteTo(stream); } }
public static void Serialize(Stream stream, LuckyDrawSetUnityView instance) { int mask = 0; using (var bytes = new MemoryStream()) { Int32Proxy.Serialize(bytes, instance.CreditsAttributed); BooleanProxy.Serialize(bytes, instance.ExposeItemsToPlayers); Int32Proxy.Serialize(bytes, instance.Id); if (instance.ImageUrl != null) { StringProxy.Serialize(bytes, instance.ImageUrl); } else { mask |= 1; } Int32Proxy.Serialize(bytes, instance.LuckyDrawId); if (instance.LuckyDrawSetItems != null) { ListProxy <BundleItemView> .Serialize(bytes, instance.LuckyDrawSetItems, BundleItemViewProxy.Serialize); } else { mask |= 2; } Int32Proxy.Serialize(bytes, instance.PointsAttributed); Int32Proxy.Serialize(bytes, instance.SetWeight); Int32Proxy.Serialize(stream, ~mask); bytes.WriteTo(stream); } }
// Token: 0x060013BE RID: 5054 RVA: 0x000717F4 File Offset: 0x0006F9F4 public static ButtonInputChannel FromBytes(MemoryStream stream) { string button = StringProxy.Deserialize(stream); return(new ButtonInputChannel(button)); }
// Token: 0x060013BD RID: 5053 RVA: 0x0000D727 File Offset: 0x0000B927 public void Serialize(MemoryStream stream) { StringProxy.Serialize(stream, this._button); }
internal void InternalDraw(ref StringProxy text, SpriteBatch spriteBatch, Vector2 position, Color color, float rotation, Vector2 origin, ref Vector2 scale, SpriteEffects spriteEffects, float depth) { var baseOffset = origin; //baseOffset.Y += globalBaseOffsetY; // If the text is mirrored, offset the start position accordingly. if (spriteEffects != SpriteEffects.None) { baseOffset -= MeasureString(ref text)*axisIsMirroredTable[(int) spriteEffects & 3]; } var localScale = scale; // Draw each character in turn. ForEachGlyph(ref text, (ref SpriteFontData.Glyph glyph, float x, float y) => { var offset = new Vector2(x, y + glyph.Offset.Y); Vector2.Modulate(ref offset, ref axisDirectionTable[(int) spriteEffects & 3], out offset); Vector2.Add(ref offset, ref baseOffset, out offset); if (spriteEffects != SpriteEffects.None) { // For mirrored characters, specify bottom and/or right instead of top left. var glyphRect = new Vector2(glyph.Subrect.Right - glyph.Subrect.Left, glyph.Subrect.Top - glyph.Subrect.Bottom); Vector2.Modulate(ref glyphRect, ref axisIsMirroredTable[(int) spriteEffects & 3], out offset); } var destination = new RectangleF(position.X, position.Y, localScale.X, localScale.Y); Rectangle? sourceRectangle = glyph.Subrect; spriteBatch.DrawSprite(textures[glyph.BitmapIndex], ref destination, true, ref sourceRectangle, color, rotation, ref offset, spriteEffects, depth); }); }
/// <summary>Returns the width and height of a string as a Vector2.</summary> /// <param name="text">The string to measure.</param> public Vector2 MeasureString(StringBuilder text) { var proxyText = new StringProxy(text); return MeasureString(ref proxyText); }
internal virtual void PreGenerateGlyphs(ref StringProxy text, ref Vector2 size) { }
/// <summary> /// Pre-generate synchronously the glyphs of the character needed to render the provided text at the provided size. /// </summary> /// <param name="text">The text containing the characters to pre-generate</param> /// <param name="size">The size of the font</param> public void PreGenerateGlyphs(string text, Vector2 size) { var proxyText = new StringProxy(text); PreGenerateGlyphs(ref proxyText, ref size); }
private void ChatMessageToAll(CommPeer peer, MemoryStream bytes) { var message = StringProxy.Deserialize(bytes); OnChatMessageToAll(peer, message); }
private void GetPlayersWithMatchingName(CommPeer peer, MemoryStream bytes) { var search = StringProxy.Deserialize(bytes); OnGetPlayersWithMatchingName(peer, search); }
private unsafe void ForEachGlyph <T>(ref StringProxy text, GlyphAction <T> action, ref T parameters) { float x = 0; float y = 0; // TODO: Not sure how to handle globalBaseOffsetY from AngelCode BMFont fixed(void *pGlyph = glyphs) { var key = 0; for (int i = 0; i < text.Length; i++) { char character = text[i]; switch (character) { case '\r': // Skip carriage returns. key |= character; continue; case '\n': // New line. x = 0; y += LineSpacing; key |= character; break; default: // Output this character. int glyphIndex; if (!characterMap.TryGetValue(character, out glyphIndex)) { if (IgnoreUnkownCharacters) { continue; } if (DefaultCharacter.HasValue && defaultGlyphIndex >= 0) { character = DefaultCharacter.Value; glyphIndex = defaultGlyphIndex; } else { throw new ArgumentException(string.Format("Character '{0}' is not available in the SpriteFont character map", character), "text"); } } key |= character; var glyph = (SpriteFontData.Glyph *)pGlyph + glyphIndex; // do not offset the first character, otherwise it is impossible to compute correct alignment // using MeasureString results if (x > 0f) { x += glyph->Offset.X; } // reset negative offset (it can happen only for first character) if (x < 0f) { x = 0f; } // Offset the kerning float kerningOffset; if (kerningMap != null && kerningMap.TryGetValue(key, out kerningOffset)) { x += kerningOffset; } if (!char.IsWhiteSpace(character)) { action(ref parameters, ref *glyph, x, y); } x += glyph->XAdvance + Spacing; break; } // Shift the kerning key key = (key << 16); } } }
/// <summary>Returns the width and height of a string as a Vector2.</summary> /// <param name="text">The string to measure.</param> public Vector2 MeasureString(StringBuilder text) { var proxyText = new StringProxy(text); return(MeasureString(ref proxyText)); }
// Token: 0x060013AE RID: 5038 RVA: 0x0000D655 File Offset: 0x0000B855 public void Serialize(MemoryStream stream) { StringProxy.Serialize(stream, this._axis); SingleProxy.Serialize(stream, this._deadRange); EnumProxy <AxisInputChannel.AxisReadingMethod> .Serialize(stream, this._axisReading); }
private void ForEachGlyph <T>(CommandList commandList, ref StringProxy text, ref Vector2 requestedFontSize, GlyphAction <T> action, ref T parameters, TextAlignment scanOrder, TextVerticalAlignment vertAlign, bool updateGpuResources, Vector2?textBoxSize = null, float lineSpaceAdjustment = 0f) { float rawYSpacing = GetTotalLineSpacing(requestedFontSize.Y); float yStart, ySpacing = rawYSpacing + lineSpaceAdjustment; if (textBoxSize.HasValue && vertAlign != TextVerticalAlignment.Top) { int extraLines = text.LineCount - 1; float lineHeight = rawYSpacing + extraLines * ySpacing; switch (vertAlign) { default: case TextVerticalAlignment.Center: yStart = textBoxSize.Value.Y * 0.5f - (lineHeight * 0.5f); break; case TextVerticalAlignment.Bottom: yStart = textBoxSize.Value.Y - lineHeight; break; } } else { yStart = 0f; } if (scanOrder == TextAlignment.Left) { // scan the whole text only one time following the text letter order ForGlyph(commandList, ref text, ref requestedFontSize, action, ref parameters, 0, text.Length, updateGpuResources, 0f, yStart, vertAlign, ySpacing); } else { // scan the text line by line incrementing y start position // measure the whole string in order to be able to determine xStart var wholeSize = textBoxSize ?? MeasureString(ref text, ref requestedFontSize); // scan the text line by line var startIndex = 0; var endIndex = FindCariageReturn(ref text, 0); while (startIndex < text.Length) { // measure the size of the current line var lineSize = Vector2.Zero; ForGlyph(commandList, ref text, ref requestedFontSize, MeasureStringGlyph, ref lineSize, startIndex, endIndex, updateGpuResources, 0f, 0f, vertAlign, ySpacing); // Determine the start position of the line along the x axis // We round this value to the closest integer to force alignment of all characters to the same pixels // Otherwise the starting offset can fall just in between two pixels and due to float imprecision // some characters can be aligned to the pixel before and others to the pixel after, resulting in gaps and character overlapping var xStart = (scanOrder == TextAlignment.Center) ? (wholeSize.X - lineSize.X) / 2 : wholeSize.X - lineSize.X; xStart = (float)Math.Round(xStart); // scan the line ForGlyph(commandList, ref text, ref requestedFontSize, action, ref parameters, startIndex, endIndex, updateGpuResources, xStart, yStart, vertAlign, ySpacing); // update variable before going to next line yStart += ySpacing; startIndex = endIndex + 1; endIndex = FindCariageReturn(ref text, startIndex); } } }
private Vector2 MeasureString(ref StringProxy text) { var result = Vector2.Zero; ForEachGlyph(ref text, (ref SpriteFontData.Glyph glyph, float x, float y) => { float w = x + (glyph.Subrect.Right - glyph.Subrect.Left); float h = y + Math.Max((glyph.Subrect.Bottom - glyph.Subrect.Top) + glyph.Offset.Y, LineSpacing); if (w > result.X) result.X = w; if (h > result.Y) result.Y = h; }); return result; }
private unsafe void ForEachGlyph(ref StringProxy text, GlyphAction action) { float x = 0; float y = 0; // TODO: Not sure how to handle globalBaseOffsetY from AngelCode BMFont fixed (void* pGlyph = glyphs) { var key = 0; for (int i = 0; i < text.Length; i++) { char character = text[i]; switch (character) { case '\r': // Skip carriage returns. key |= character; continue; case '\n': // New line. x = 0; y += LineSpacing; key |= character; break; default: // Output this character. int glyphIndex; if (!characterMap.TryGetValue(character, out glyphIndex)) { if(DefaultCharacter.HasValue && defaultGlyphIndex >= 0) { character = DefaultCharacter.Value; glyphIndex = defaultGlyphIndex; } else { throw new ArgumentException(string.Format("Character '{0}' is not available in the SpriteFont character map", character), "text"); } } key |= character; var glyph = (SpriteFontData.Glyph*) pGlyph + glyphIndex; x += glyph->Offset.X; if (x < 0) x = 0; // Offset the kerning float kerningOffset; if (kerningMap != null && kerningMap.TryGetValue(key, out kerningOffset)) x += kerningOffset; if (!char.IsWhiteSpace(character)) { action(ref *glyph, x, y); } x += glyph->XAdvance; break; } // Shift the kerning key key = (key << 16); } } }