/// <summary> /// Handles the HTTP request /// </summary> /// <param name="context"></param> public void ProcessRequest(HttpContext context) { HttpResponse response = context.Response; // set cache behaviour response.Cache.SetCacheability(HttpCacheability.Public); response.Cache.VaryByParams["*"] = false; response.Cache.SetLastModified(BgfCache.LastModified); // set response type response.ContentType = "application/json"; response.ContentEncoding = new UTF8Encoding(false); response.AddHeader("Content-Disposition", "inline; filename=list.json"); // get enumerator on all bgf in cache IEnumerator <KeyValuePair <string, BgfCache.Entry> > enumerator = BgfCache.GetEnumerator(); // start output bool isComma = false; response.Write('['); while (enumerator.MoveNext()) { BgfCache.Entry entry = enumerator.Current.Value; // write comma for previous entry if (isComma) { response.Write(','); } else { isComma = true; } // unix timestamp long stamp = (entry.LastModified.Ticks - 621355968000000000) / 10000000; // write json object response.Write("{\"file\":\""); response.Write(entry.Bgf.Filename); response.Write("\",\"size\":"); response.Write(entry.Size.ToString()); response.Write(",\"modified\":"); response.Write(stamp.ToString()); response.Write(",\"shrink\":"); response.Write(entry.Bgf.ShrinkFactor.ToString()); response.Write(",\"frames\":"); response.Write(entry.Bgf.Frames.Count.ToString()); response.Write(",\"groups\":"); response.Write(entry.Bgf.FrameSets.Count.ToString()); response.Write('}'); } response.Write(']'); }
public void ProcessRequest(HttpContext context) { // -------------------------------------------------------------------------------------------- // 1) PARSE URL PARAMETERS // -------------------------------------------------------------------------------------------- // See Global.asax: // object/{scale}/{file}/{group}/{palette}/{angle} RouteValueDictionary parms = context.Request.RequestContext.RouteData.Values; string parmScale = parms.ContainsKey("scale") ? (string)parms["scale"] : null; string parmFile = parms.ContainsKey("file") ? (string)parms["file"] : null; string parmGroup = parms.ContainsKey("group") ? (string)parms["group"] : null; string parmPalette = parms.ContainsKey("palette") ? (string)parms["palette"] : null; string parmAngle = parms.ContainsKey("angle") ? (string)parms["angle"] : null; BgfCache.Entry entry; ushort scale; byte paletteidx = 0; ushort angle = 0; // verify that minimum parameters are valid/in range and bgf exists // angle ranges from [0-7] and are multiples of 512 if (!UInt16.TryParse(parmScale, out scale) || scale < MINSCALE || scale > MAXSCALE || !Byte.TryParse(parmPalette, out paletteidx) || !UInt16.TryParse(parmAngle, out angle) || angle > 7 || String.IsNullOrEmpty(parmFile) || !BgfCache.GetBGF(parmFile, out entry)) { context.Response.StatusCode = 404; return; } // multiply by 512 and remove full periods from angle angle = (ushort)((angle << 9) % GeometryConstants.MAXANGLE); // parse animation Animation anim = Animation.ExtractAnimation(parmGroup, '-'); if (anim == null || !anim.IsValid(entry.Bgf.FrameSets.Count)) { context.Response.StatusCode = 404; return; } // stores the latest lastmodified of main and all subov DateTime lastModified = entry.LastModified; // read suboverlay array params from query parameters: // object/..../?subov={file};{group};{palette};{hotspot}&subov=... string[] parmSubOverlays = context.Request.Params.GetValues("s"); if (parmSubOverlays != null) { foreach (string s in parmSubOverlays) { string[] subOvParms = s.Split(';'); BgfCache.Entry bgfSubOv; byte subOvPalette; byte subOvHotspot; if (subOvParms == null || subOvParms.Length < 4 || String.IsNullOrEmpty(subOvParms[0]) || String.IsNullOrEmpty(subOvParms[1]) || !byte.TryParse(subOvParms[2], out subOvPalette) || !byte.TryParse(subOvParms[3], out subOvHotspot) || subOvHotspot > 127 || !BgfCache.GetBGF(subOvParms[0], out bgfSubOv)) { context.Response.StatusCode = 404; return; } // get suboverlay animation Animation subOvAnim = Animation.ExtractAnimation(subOvParms[1], '-'); if (subOvAnim == null || !subOvAnim.IsValid(bgfSubOv.Bgf.FrameSets.Count)) { context.Response.StatusCode = 404; return; } // create suboverlay SubOverlay subOv = new SubOverlay(0, subOvAnim, subOvHotspot, subOvPalette, 0); // set bgf resource subOv.Resource = bgfSubOv.Bgf; // update lastModified if subov is newer if (bgfSubOv.LastModified > lastModified) { lastModified = bgfSubOv.LastModified; } // add to gameobject's suboverlays gameObject.SubOverlays.Add(subOv); } } // -------------------------------------------------------------------------------------------- // 2) SET CACHE HEADERS AND COMPARE FOR 304 RETURN // -------------------------------------------------------------------------------------------- // set cache behaviour context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.VaryByParams["*"] = false; context.Response.Cache.SetLastModified(lastModified); // set return type context.Response.ContentType = "image/png"; context.Response.AddHeader("Content-Disposition", "inline; filename=object.png"); // check if client has valid cached version (returns 304) /*DateTime dateIfModifiedSince; * string modSince = context.Request.Headers["If-Modified-Since"]; * * // try to parse received client header * bool parseOk = DateTime.TryParse( * modSince, CultureInfo.CurrentCulture, * DateTimeStyles.AdjustToUniversal, out dateIfModifiedSince); * * // send 304 and stop if last file write equals client's cache timestamp * if (parseOk && dateIfModifiedSince == lastModified) * { * context.Response.SuppressContent = true; * context.Response.StatusCode = 304; * return; * }*/ // -------------------------------------------------------------------------------------------- // 3) PREPARE RESPONSE // -------------------------------------------------------------------------------------------- // set parsed/created values on game object gameObject.Resource = entry.Bgf; gameObject.ColorTranslation = paletteidx; gameObject.Animation = anim; gameObject.ViewerAngle = angle; // set imagecomposer scale/shrink imageComposer.CustomShrink = (float)scale * 0.1f; // tick object (triggers image recreation) gameObject.Tick(0, 1); if (imageComposer.Image == null) { context.Response.StatusCode = 404; return; } // -------------------------------------------------------------------------------------------- // 4) CREATE RESPONSE AND FINISH // -------------------------------------------------------------------------------------------- // write image as png imageComposer.Image.Save(context.Response.OutputStream, ImageFormat.Png); // clear imageComposer.Image.Dispose(); gameObject.SubOverlays.Clear(); }
/// <summary> /// Handles the HTTP request /// </summary> /// <param name="context"></param> public void ProcessRequest(HttpContext context) { HttpResponse response = context.Response; // -------------------------------------------------------------------------------------------- // 1) PARSE URL PARAMETERS // -------------------------------------------------------------------------------------------- // read parameters from url-path (see Global.asax): RouteValueDictionary parms = context.Request.RequestContext.RouteData.Values; string parmFile = parms.ContainsKey("file") ? (string)parms["file"] : null; string parmReq = parms.ContainsKey("req") ? (string)parms["req"] : null; string parm1 = parms.ContainsKey("parm1") ? (string)parms["parm1"] : null; string parm2 = parms.ContainsKey("parm2") ? (string)parms["parm2"] : null; string parm3 = parms.ContainsKey("parm3") ? (string)parms["parm3"] : null; BgfCache.Entry entry; // no filename or request type if (String.IsNullOrEmpty(parmFile) || !BgfCache.GetBGF(parmFile, out entry) || String.IsNullOrEmpty(parmReq)) { context.Response.StatusCode = 404; return; } // set cache behaviour context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.VaryByParams["*"] = false; context.Response.Cache.SetLastModified(entry.LastModified); // -------------------------------------------------------------------------------------------- // FRAME IMAGE // -------------------------------------------------------------------------------------------- if (parmReq == "frame") { ushort index; byte palette = 0; Byte.TryParse(parm3, out palette); // try to parse index and palette and validate range if (!UInt16.TryParse(parm2, out index) || index >= entry.Bgf.Frames.Count) { context.Response.StatusCode = 404; return; } // create BMP (256 col) or PNG (32-bit) or return raw pixels (8bit indices) if (parm1 == "bmp") { response.ContentType = "image/bmp"; response.AddHeader( "Content-Disposition", "inline; filename=" + entry.Bgf.Filename + "-" + index.ToString() + ".bmp"); Bitmap bmp = entry.Bgf.Frames[index].GetBitmap(palette); bmp.Save(context.Response.OutputStream, ImageFormat.Bmp); bmp.Dispose(); } else if (parm1 == "png") { response.ContentType = "image/png"; response.AddHeader( "Content-Disposition", "inline; filename=" + entry.Bgf.Filename + "-" + index.ToString() + ".png"); Bitmap bmp = entry.Bgf.Frames[index].GetBitmapA8R8G8B8(palette); bmp.Save(context.Response.OutputStream, ImageFormat.Png); bmp.Dispose(); } else if (parm1 == "bin") { response.ContentType = "application/octet-stream"; response.AddHeader( "Content-Disposition", "attachment; filename=" + entry.Bgf.Filename + "-" + index.ToString() + ".bin"); byte[] pixels = entry.Bgf.Frames[index].PixelData; context.Response.OutputStream.Write(pixels, 0, pixels.Length); } else { context.Response.StatusCode = 404; } } // -------------------------------------------------------------------------------------------- // JSON META DATA // -------------------------------------------------------------------------------------------- else if (parmReq == "meta") { // set response type response.ContentType = "application/json"; response.ContentEncoding = new System.Text.UTF8Encoding(false); response.AddHeader("Content-Disposition", "inline; filename=" + entry.Bgf.Filename + ".json"); // unix timestamp long stamp = (entry.LastModified.Ticks - 621355968000000000) / 10000000; ///////////////////////////////////////////////////////////// response.Write("{\"file\":\""); response.Write(entry.Bgf.Filename); response.Write("\",\"size\":"); response.Write(entry.Size.ToString()); response.Write(",\"modified\":"); response.Write(stamp.ToString()); response.Write(",\"shrink\":"); response.Write(entry.Bgf.ShrinkFactor.ToString()); response.Write(",\"frames\":["); for (int i = 0; i < entry.Bgf.Frames.Count; i++) { BgfBitmap frame = entry.Bgf.Frames[i]; if (i > 0) { response.Write(','); } response.Write("{\"w\":"); response.Write(frame.Width.ToString()); response.Write(",\"h\":"); response.Write(frame.Height.ToString()); response.Write(",\"x\":"); response.Write(frame.XOffset.ToString()); response.Write(",\"y\":"); response.Write(frame.YOffset.ToString()); response.Write(",\"hs\":["); for (int j = 0; j < frame.HotSpots.Count; j++) { BgfBitmapHotspot hs = frame.HotSpots[j]; if (j > 0) { response.Write(','); } response.Write("{\"i\":"); response.Write(hs.Index.ToString()); response.Write(",\"x\":"); response.Write(hs.X.ToString()); response.Write(",\"y\":"); response.Write(hs.Y.ToString()); response.Write('}'); } response.Write("]}"); } response.Write("],\"groups\":["); for (int i = 0; i < entry.Bgf.FrameSets.Count; i++) { BgfFrameSet group = entry.Bgf.FrameSets[i]; if (i > 0) { response.Write(','); } response.Write('['); for (int j = 0; j < group.FrameIndices.Count; j++) { if (j > 0) { response.Write(','); } response.Write(group.FrameIndices[j].ToString()); } response.Write(']'); } response.Write("]}"); } // -------------------------------------------------------------------------------------------- // INVALID // -------------------------------------------------------------------------------------------- else { context.Response.StatusCode = 404; return; } }
public void ProcessRequest(HttpContext context) { // -------------------------------------------------------------------------------------------- // 1) PARSE URL PARAMETERS // -------------------------------------------------------------------------------------------- // See Global.asax: // render/{width}/{height}/{scale}/{file}/{anim}/{palette}/{angle} RouteValueDictionary parms = context.Request.RequestContext.RouteData.Values; string parmWidth = parms.ContainsKey("width") ? (string)parms["width"] : null; string parmHeight = parms.ContainsKey("height") ? (string)parms["height"] : null; string parmScale = parms.ContainsKey("scale") ? (string)parms["scale"] : null; string parmFile = parms.ContainsKey("file") ? (string)parms["file"] : null; string parmAnim = parms.ContainsKey("anim") ? (string)parms["anim"] : null; string parmPalette = parms.ContainsKey("palette") ? (string)parms["palette"] : null; string parmAngle = parms.ContainsKey("angle") ? (string)parms["angle"] : null; BgfCache.Entry entry; byte paletteidx; ushort angle; ushort width; ushort height; ushort scale; // verify that minimum parameters are valid/in range and bgf exists // angle ranges from [0-7] and are multiples of 512 if (!UInt16.TryParse(parmWidth, out width) || width < MINWIDTH || width > MAXWIDTH || !UInt16.TryParse(parmHeight, out height) || height < MINHEIGHT || height > MAXHEIGHT || !UInt16.TryParse(parmScale, out scale) || scale < MINSCALE || scale > MAXSCALE || !Byte.TryParse(parmPalette, out paletteidx) || !UInt16.TryParse(parmAngle, out angle) || angle > 7 || String.IsNullOrEmpty(parmFile) || !BgfCache.GetBGF(parmFile, out entry)) { Finish(context, 404); return; } // multiply by 512 and remove full periods from angle angle = (ushort)((angle << 9) % GeometryConstants.MAXANGLE); // parse animation Animation anim = Animation.ExtractAnimation(parmAnim, '-'); if (anim == null || !anim.IsValid(entry.Bgf.FrameSets.Count)) { Finish(context, 404); return; } // set groupmax anim.GroupMax = entry.Bgf.FrameSets.Count; // stores the latest lastmodified of main and all subov DateTime lastModified = entry.LastModified; // set parsed/created values on game-object ObjectBase gameObject = new ObjectBase(); gameObject.OverlayFileRID = entry.Num; gameObject.Resource = entry.Bgf; gameObject.ColorTranslation = paletteidx; gameObject.Animation = anim; gameObject.ViewerAngle = angle; // read suboverlay array params from query parameters: // object/..../?subov={file};{anim};{palette};{hotspot}&subov=... string[] parmSubOverlays = context.Request.Params.GetValues("s"); if (parmSubOverlays != null) { foreach (string s in parmSubOverlays) { string[] subOvParms = s.Split(';'); BgfCache.Entry bgfSubOv; byte subOvPalette; byte subOvHotspot; if (subOvParms == null || subOvParms.Length < 4 || String.IsNullOrEmpty(subOvParms[0]) || String.IsNullOrEmpty(subOvParms[1]) || !byte.TryParse(subOvParms[2], out subOvPalette) || !byte.TryParse(subOvParms[3], out subOvHotspot) || subOvHotspot > 127 || !BgfCache.GetBGF(subOvParms[0], out bgfSubOv)) { Finish(context, 404); return; } // get suboverlay animation Animation subOvAnim = Animation.ExtractAnimation(subOvParms[1], '-'); if (subOvAnim == null || !subOvAnim.IsValid(bgfSubOv.Bgf.FrameSets.Count)) { Finish(context, 404); return; } // set group max subOvAnim.GroupMax = bgfSubOv.Bgf.FrameSets.Count; // create suboverlay SubOverlay subOv = new SubOverlay(0, subOvAnim, subOvHotspot, subOvPalette, 0); // set bgf resource subOv.ResourceID = entry.Num; subOv.Resource = bgfSubOv.Bgf; // update lastModified if subov is newer if (bgfSubOv.LastModified > lastModified) { lastModified = bgfSubOv.LastModified; } // add to gameobject's suboverlays gameObject.SubOverlays.Add(subOv); } } // -------------------------------------------------------------------------------------------- // 2) SET CACHE HEADERS AND COMPARE FOR 304 RETURN // -------------------------------------------------------------------------------------------- // set cache behaviour context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.VaryByParams["*"] = false; context.Response.Cache.SetLastModified(lastModified); // set return type context.Response.ContentType = "image/gif"; context.Response.AddHeader("Content-Disposition", "inline; filename=object.gif"); // check if client has valid cached version (returns 304) /*DateTime dateIfModifiedSince; * string modSince = context.Request.Headers["If-Modified-Since"]; * * // try to parse received client header * bool parseOk = DateTime.TryParse( * modSince, CultureInfo.CurrentCulture, * DateTimeStyles.AdjustToUniversal, out dateIfModifiedSince); * * // send 304 and stop if last file write equals client's cache timestamp * if (parseOk && dateIfModifiedSince == lastModified) * { * context.Response.SuppressContent = true; * Finish(context, 304); * return; * }*/ // -------------------------------------------------------------------------------------------- // 3) PREPARE RESPONSE // -------------------------------------------------------------------------------------------- // set gif instance size gif.CanvasWidth = width; gif.CanvasHeight = height; // set imagecomposer size and shrink imageComposer.Width = width; imageComposer.Height = height; imageComposer.CustomShrink = (float)scale * 0.1f; // set gameobject (causes initial frame) imageComposer.DataSource = gameObject; // run animationlength in 1 ms steps (causes new image events) for (int i = 0; i < gameObject.AnimationLength + 10; i++) { tick += 1.0; gameObject.Tick(tick, 1.0); } // handle single frame case if (gif.Frames.Count == 0 && frame != null) { gif.Frames.Add(frame); } // -------------------------------------------------------------------------------------------- // 4) CREATE RESPONSE AND FINISH // -------------------------------------------------------------------------------------------- // write the gif to output stream gif.Write(context.Response.OutputStream); // cleanup Finish(context); }